order_service.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. from typing import List, Optional, Tuple
  2. from uuid import uuid4
  3. from logging import getLogger
  4. from pydantic import BaseModel
  5. import toteat.toteat as toteat
  6. from models.sales import ItemWeb, OrderWeb
  7. from models.items import Product
  8. from models.user import User
  9. from services.toteat_service import add_order_item
  10. from services.data_service import DataServiceFactory
  11. logger = getLogger(__name__)
  12. _user_svc = DataServiceFactory.get_user_service()
  13. _product_svc = DataServiceFactory.get_product_service()
  14. _sale_svc = DataServiceFactory.get_sales_service()
  15. PROMO_PRODUCT_TYPE = "Cervezas"
  16. REWARD_POINTS_PER_BEER = 10
  17. class ComparePricesResponse(BaseModel):
  18. product: Product
  19. oldPrice: int
  20. newPrice: int
  21. isAvailable: bool
  22. def compare_prices(products: List[Product], items: List[ItemWeb]) -> list:
  23. """Detecta diferencias de precio o productos inactivos entre el carrito y la BD."""
  24. result = []
  25. for product, item in zip(products, items):
  26. if product.status == 0:
  27. result.append(ComparePricesResponse(
  28. product=product, oldPrice=item.price,
  29. newPrice=product.price, isAvailable=False
  30. ))
  31. elif product.price != item.price:
  32. result.append(ComparePricesResponse(
  33. product=product, oldPrice=item.price,
  34. newPrice=product.price, isAvailable=True
  35. ))
  36. return [r.model_dump() for r in result]
  37. async def fetch_sorted_products(items: List[ItemWeb]) -> List[Product]:
  38. """Obtiene productos de la BD ordenados igual que los items del carrito."""
  39. products = await _product_svc.get_products([item.id for item in items])
  40. return sorted(products, key=lambda x: x.id)
  41. def push_items_to_toteat(
  42. items: List[ItemWeb],
  43. products: List[Product],
  44. table: int,
  45. user_name: str,
  46. ) -> Tuple[List[str], int]:
  47. """
  48. Registra cada item en Toteat POS.
  49. Retorna (errores, cervezas_para_promo).
  50. Lanza excepción si toteat.get_token() falla — el handler la captura como error fatal.
  51. """
  52. toteat.get_token()
  53. errors: List[str] = []
  54. beers = 0
  55. for item, product in zip(items, products):
  56. try:
  57. if product.type == PROMO_PRODUCT_TYPE and user_name != "Guest":
  58. beers += item.quantity
  59. result = add_order_item(item.id, item.quantity, table)
  60. if not result:
  61. errors.append(f"Error adding product {item.id} to table {table}.")
  62. except Exception as e:
  63. errors.append(f"Error processing product {item.id}: {e}")
  64. return errors, beers
  65. def get_active_sale_id(table: int) -> Optional[str]:
  66. """Devuelve el ID de la venta activa de la mesa en Toteat, o None si no hay."""
  67. sale = toteat.get_active_sale(toteat.get_table(table))
  68. return sale['id'] if sale else None
  69. def update_reward_progress(user: User, beers: int) -> int:
  70. """
  71. Actualiza puntos de fidelización del usuario.
  72. No lanza excepción — un fallo aquí no bloquea el pedido.
  73. """
  74. new_progress = user.reward_progress + beers * REWARD_POINTS_PER_BEER
  75. try:
  76. _user_svc.set_reward_progress(user.id, new_progress)
  77. logger.info(f"Reward progress updated for user {user.email}: {user.reward_progress} -> {new_progress}")
  78. except Exception as e:
  79. logger.error(f"Error updating reward progress for user {user.id}: {e}")
  80. new_progress = user.reward_progress
  81. return new_progress
  82. def create_sale_record(order: OrderWeb, active_sale_id: str, items: List[ItemWeb]) -> int:
  83. """Persiste la venta en PostgreSQL. Retorna el ID o -1 si falló."""
  84. return _sale_svc.create(
  85. order.customerId,
  86. active_sale_id or uuid4().hex,
  87. order.totalAmount,
  88. order.table,
  89. [item.id for item in items],
  90. [item.quantity for item in items],
  91. )