orders.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. from logging import getLogger
  2. from typing import List
  3. from uuid import uuid4
  4. from pydantic import BaseModel
  5. from fastapi import APIRouter, Depends
  6. from toteat import toteat as fudo
  7. from models.sales import ItemWeb, OrderWeb
  8. from models.items import Product
  9. from models.user import User
  10. from services.fudo_service import add_product_to_fudo
  11. from services.data_service import DataServiceFactory
  12. from config.messages import ErrorResponse, SuccessResponse
  13. from auth.security import get_current_user
  14. from utils.responses import error_response, success_response
  15. logger = getLogger(__name__)
  16. # Data services initialization
  17. user_data_service = DataServiceFactory.get_user_service()
  18. product_data_service = DataServiceFactory.get_product_service()
  19. sale_data_service = DataServiceFactory.get_sales_service()
  20. order_router = APIRouter()
  21. name_promo = "Cervezas"
  22. class ComparePricesResponse(BaseModel):
  23. product: Product
  24. oldPrice: int
  25. newPrice: int
  26. isAvailable: bool
  27. def compare_prices(products: List[Product], items: List[ItemWeb]) -> list:
  28. """Compare prices of products and items and return the cheapest product"""
  29. # Initialize a dictionary to store the prices
  30. prices: List[ComparePricesResponse] = []
  31. for product, item in zip(products, items):
  32. if product.status == 0:
  33. prices.append(
  34. ComparePricesResponse(
  35. product=product,
  36. oldPrice=item.price,
  37. newPrice=product.price,
  38. isAvailable=False
  39. )
  40. )
  41. continue
  42. if product.price != item.price:
  43. prices.append(
  44. ComparePricesResponse(
  45. product=product,
  46. oldPrice=item.price,
  47. newPrice=product.price,
  48. isAvailable=True
  49. )
  50. )
  51. return list(map(lambda x: x.model_dump(), prices))
  52. @order_router.post("/send")
  53. async def printer_order(order: OrderWeb, current_user: User = Depends(get_current_user)):
  54. """Process printer order"""
  55. logger.info(f"Printer order received from user {current_user.email} for table {order.table}")
  56. # Extract order data
  57. items = order.items
  58. table = order.table
  59. # Input validation
  60. if not items or not table:
  61. logger.warning(f"Invalid order data from user {current_user.email}: missing items or table")
  62. return error_response(message=ErrorResponse.MISSING_FIELDS, status_code=400)
  63. if not isinstance(table, int):
  64. logger.warning(f"Invalid table type from user {current_user.email}: {type(table)}")
  65. return error_response(message=ErrorResponse.INVALID_TABLE_TYPE, status_code=400)
  66. logger.info(f"Processing order for table {table} with {len(items)} items")
  67. # Get products data
  68. try:
  69. products = await product_data_service.get_products([item.id for item in items])
  70. # Me aseguro de que los items y los productos esten en el mismo orden
  71. products = list(sorted(products, key=lambda x: x.id))
  72. items = list(sorted(items, key=lambda x: x.id))
  73. logger.info(f"Retrieved {len(products)} products from database")
  74. except Exception as e:
  75. error_msg = f"Error getting products: {e}"
  76. logger.error(error_msg)
  77. return error_response(message=error_msg, status_code=500)
  78. # Comparo los precios de los items con los productos
  79. prices = compare_prices(products, items)
  80. if prices:
  81. return success_response(data=prices, message="El estado de los productos y items coincide con el de la venta", status_code=409)
  82. # Add products to Fudo
  83. product_errors = []
  84. beers_for_promo = 0
  85. try:
  86. fudo.get_token()
  87. logger.info("Fudo token obtained successfully")
  88. for item, product in zip(items, products):
  89. try:
  90. if product.type == name_promo and current_user.name != "Guest":
  91. beers_for_promo += item.quantity
  92. logger.debug(f"Added {item.quantity} beers for promotion calculation")
  93. fudo_product = add_product_to_fudo(item.id, item.quantity, table)
  94. logger.info(f"Added product {item.id} to table {table} with quantity {item.quantity} in fudo")
  95. if not fudo_product:
  96. error_msg = f"Error adding product {item.id} to table {table}."
  97. product_errors.append(error_msg)
  98. logger.error(error_msg)
  99. except Exception as e:
  100. error_msg = f"Error processing product {item.id}: {e}"
  101. logger.error(error_msg)
  102. product_errors.append(error_msg)
  103. except Exception as e:
  104. error_msg = f"Error with Fudo integration: {e}"
  105. logger.error(error_msg)
  106. return error_response(message=error_msg, status_code=500)
  107. if product_errors:
  108. logger.error(f"Product errors occurred: {product_errors}")
  109. return error_response(error={"errors": product_errors}, message=ErrorResponse.PRODUCT_ADD_ERROR, status_code=424)
  110. # Get active sale
  111. try:
  112. active_sale_id = fudo.get_active_sale(fudo.get_table(table))
  113. if not active_sale_id:
  114. error_msg = f"No active sale found for table {table}"
  115. logger.error(error_msg)
  116. return error_response(message=error_msg, status_code=404)
  117. active_sale_id = active_sale_id['id']
  118. logger.info(f"Active sale found for table {table}: {active_sale_id}")
  119. except Exception as e:
  120. error_msg = f"Error retrieving active sale for table {table}: {e}"
  121. logger.error(error_msg)
  122. return error_response(message=error_msg, status_code=500)
  123. # Update user reward progress
  124. try:
  125. new_progress = current_user.reward_progress + beers_for_promo * 10
  126. user_data_service.set_reward_progress(current_user.id, new_progress)
  127. logger.info(f"Updated reward progress for user {current_user.email}: {current_user.reward_progress} -> {new_progress}")
  128. except Exception as e:
  129. error_msg = f"Error updating reward progress for user {current_user.id}: {e}"
  130. logger.error(error_msg)
  131. # Don't fail the order for this, just log it
  132. new_progress = current_user.reward_progress
  133. # Create sale record
  134. try:
  135. sale = sale_data_service.create(
  136. order.customerId,
  137. active_sale_id or uuid4().hex,
  138. order.totalAmount,
  139. order.table,
  140. [item.id for item in items],
  141. [item.quantity for item in items]
  142. )
  143. if sale > 0:
  144. logger.info(f"Sale created successfully: ID {sale}")
  145. else:
  146. error_msg = "Failed to create sale record"
  147. logger.error(error_msg)
  148. return error_response(message=error_msg, status_code=500)
  149. except Exception as e:
  150. error_msg = f"Error creating sale record: {e}"
  151. logger.error(error_msg)
  152. return error_response(message=error_msg, status_code=500)
  153. logger.info(f"Order processing completed successfully for table {table}, sale ID: {sale}")
  154. return success_response(data={"new_progress": new_progress}, message=SuccessResponse.ORDER_SUCCESS)