orders.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import time
  2. from logging import getLogger
  3. from threading import Thread
  4. from uuid import uuid4
  5. from enums.locations import Locations
  6. from fastapi import HTTPException, APIRouter, Depends
  7. from fastapi.responses import JSONResponse
  8. from fudo import fudo
  9. from models.sales import ItemWeb, OrderWeb
  10. from models.items import Item, Order
  11. from models.user import User
  12. from services.fudo_service import add_product_to_fudo
  13. from services.email_service import get_email_sender
  14. import services.print_service as ps
  15. from services.data_service import DataServiceFactory
  16. from config.mails import PRINTER_DISCONNECTED_MAIL
  17. from config.messages import ErrorResponse, SuccessResponse, UserResponse
  18. from config.settings import DEVELOPMENT
  19. from auth.security import get_current_user
  20. from data.product_category import CAT_ITEMS
  21. logger = getLogger(__name__)
  22. # Data services initialization
  23. user_data_service = DataServiceFactory.get_user_service()
  24. product_data_service = DataServiceFactory.get_product_service()
  25. sale_data_service = DataServiceFactory.get_sales_service()
  26. # Global variables
  27. printer_orders = []
  28. order_router = APIRouter()
  29. name_promo = "Shop"
  30. @order_router.post("/send")
  31. async def printer_order(order: OrderWeb, current_user: User = Depends(get_current_user)):
  32. """Process printer order"""
  33. logger.info(f"Printer order received from user {current_user.email} for table {order.table}")
  34. # Printer status validation
  35. if not DEVELOPMENT:
  36. try:
  37. printer_status = filter(lambda x: x == False, [ps.get_status(Locations.BAR), ps.get_status(Locations.KITCHEN), ps.get_status(Locations.COCTELERY)])
  38. if not printer_status:
  39. logger.error(f"Printer is not connected. Order from user {current_user.email} cannot be processed.")
  40. # Send notification email to admins
  41. email_thread = Thread(
  42. target=get_email_sender().send_email,
  43. args=(
  44. PRINTER_DISCONNECTED_MAIL["subject"],
  45. PRINTER_DISCONNECTED_MAIL["body"].format(location=", ".join([loc.value for loc in printer_status])),
  46. ["erwinjacimino2003@gmail.com", "mompyn@gmail.com"]
  47. ),
  48. daemon=True
  49. )
  50. email_thread.start()
  51. return JSONResponse(status_code=424, content={"message": ErrorResponse.PRINTER_DISCONNECTED})
  52. except Exception as e:
  53. logger.error(f"Error checking printer status: {e}")
  54. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  55. # Extract order data
  56. items = order.items
  57. table = order.table
  58. # Input validation
  59. if not items or not table:
  60. logger.warning(f"Invalid order data from user {current_user.email}: missing items or table")
  61. return JSONResponse(status_code=400, content={"message": ErrorResponse.MISSING_FIELDS})
  62. if not isinstance(table, int):
  63. logger.warning(f"Invalid table type from user {current_user.email}: {type(table)}")
  64. return JSONResponse(status_code=400, content={"message": ErrorResponse.INVALID_TABLE_TYPE})
  65. logger.info(f"Processing order for table {table} with {len(items)} items")
  66. # Add products to Fudo
  67. product_errors = []
  68. # Get products data
  69. try:
  70. products = product_data_service.get_products([item.id for item in items])
  71. logger.info(f"Retrieved {len(products)} products from database")
  72. except Exception as e:
  73. error_msg = f"Error retrieving products: {e}"
  74. logger.error(error_msg)
  75. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  76. beers_for_promo = 0
  77. try:
  78. fudo.get_token()
  79. logger.info("Fudo token obtained successfully")
  80. for item, product in zip(items, products):
  81. try:
  82. # Si es dia de promo
  83. if time.localtime().tm_wday + 1 == product.promo_day and product.promo_id:
  84. logger.info(f"Applying promotion for product {product.id} on table {table}")
  85. fudo_product = add_product_to_fudo(product.promo_id, item.quantity, table)
  86. #en caso contrario
  87. else:
  88. if product.type == name_promo:
  89. beers_for_promo += item.quantity
  90. logger.debug(f"Added {item.quantity} beers for promotion calculation")
  91. fudo_product = add_product_to_fudo(item.id, item.quantity, table)
  92. logger.info(f"Added product {item.id} to table {table} with quantity {item.quantity} in fudo")
  93. if not fudo_product:
  94. error_msg = f"Error adding product {item.id} to table {table}."
  95. product_errors.append(error_msg)
  96. logger.error(error_msg)
  97. except Exception as e:
  98. error_msg = f"Error processing product {item.id}: {e}"
  99. logger.error(error_msg)
  100. product_errors.append(error_msg)
  101. except Exception as e:
  102. error_msg = f"Error with Fudo integration: {e}"
  103. logger.error(error_msg)
  104. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  105. if product_errors:
  106. logger.error(f"Product errors occurred: {product_errors}")
  107. return JSONResponse(
  108. status_code=424,
  109. content={"message": ErrorResponse.PRODUCT_ADD_ERROR, "errors": product_errors}
  110. )
  111. # User validation
  112. try:
  113. user = user_data_service.get_by_id(order.customerId)
  114. if not user:
  115. logger.warning(f"User not found: {order.customerId}")
  116. return JSONResponse(status_code=404, content={"message": UserResponse.USER_NOT_FOUND.format(user_id=order.customerId)})
  117. logger.info(f"Order customer validated: {user.email}")
  118. except Exception as e:
  119. error_msg = f"Error validating user {order.customerId}: {e}"
  120. logger.error(error_msg)
  121. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  122. # Get active sale
  123. try:
  124. active_sale_id = fudo.get_active_sale(fudo.get_table(table))
  125. if not active_sale_id:
  126. error_msg = f"No active sale found for table {table}"
  127. logger.error(error_msg)
  128. raise HTTPException(status_code=404, detail=error_msg)
  129. active_sale_id = active_sale_id['id']
  130. logger.info(f"Active sale found for table {table}: {active_sale_id}")
  131. except HTTPException:
  132. raise
  133. except Exception as e:
  134. error_msg = f"Error retrieving active sale for table {table}: {e}"
  135. logger.error(error_msg)
  136. raise HTTPException(status_code=500, detail="Error interno del servidor")
  137. # Update user reward progress
  138. try:
  139. new_progress = user.reward_progress + beers_for_promo * 10
  140. user_data_service.set_reward_progress(user.id, new_progress)
  141. logger.info(f"Updated reward progress for user {user.email}: {user.reward_progress} -> {new_progress}")
  142. except Exception as e:
  143. error_msg = f"Error updating reward progress for user {user.id}: {e}"
  144. logger.error(error_msg)
  145. # Don't fail the order for this, just log it
  146. new_progress = user.reward_progress
  147. # Create sale record
  148. try:
  149. sale = sale_data_service.create(
  150. order.customerId,
  151. active_sale_id or uuid4().hex,
  152. order.totalAmount,
  153. order.table,
  154. [item.id for item in items],
  155. [item.quantity for item in items]
  156. )
  157. if sale > 0:
  158. logger.info(f"Sale created successfully: ID {sale}")
  159. else:
  160. error_msg = "Failed to create sale record"
  161. logger.error(error_msg)
  162. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  163. except Exception as e:
  164. error_msg = f"Error creating sale record: {e}"
  165. logger.error(error_msg)
  166. return JSONResponse(status_code=500, content={"message": "Error interno del servidor"})
  167. # Print order
  168. try:
  169. kitchen_items = [ Item(name=item.name, price=product.price, quantity=item.quantity) for product, item in zip(items, products) if CAT_ITEMS.get(item.type, Locations.BAR) == Locations.KITCHEN]
  170. bar_items = [ Item(name=item.name, price=product.price, quantity=item.quantity) for product, item in zip(items, products) if CAT_ITEMS.get(item.type, Locations.BAR) == Locations.BAR]
  171. coctelery_items = [ Item(name=item.name, price=product.price, quantity=item.quantity) for product, item in zip(items, products) if CAT_ITEMS.get(item.type, Locations.COCTELERY) == Locations.COCTELERY]
  172. if kitchen_items:
  173. ps.print_order(Order(table=table, items=kitchen_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.KITCHEN)
  174. logger.info(f"Order kitchen printed successfully for table {table}")
  175. if bar_items:
  176. ps.print_order(Order(table=table, items=bar_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.BAR)
  177. logger.info(f"Order bar printed successfully for table {table}")
  178. if coctelery_items:
  179. ps.print_order(Order(table=table, items=coctelery_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.COCTELERY)
  180. logger.info(f"Order coctelery printed successfully for table {table}")
  181. # ps.print_order(order_for_print, location=CAT_ITEMS.get())
  182. logger.info(f"Order printed successfully for table {table}")
  183. except Exception as e:
  184. error_msg = f"Error printing order for table {table}: {e}"
  185. logger.error(error_msg)
  186. # Don't fail the order for print issues, just log it
  187. # Log order
  188. try:
  189. logger.info(f"Logging order for table {table} with sale ID {sale}, products= {[(product.name, item.quantity) for product, item in zip(products, items)]}")
  190. except Exception as e:
  191. logger.error(f"Error in legacy order logging: {e}")
  192. # Don't fail the order for logging issues
  193. logger.info(f"Order processing completed successfully for table {table}, sale ID: {sale}")
  194. return JSONResponse({"message": SuccessResponse.ORDER_SUCCESS, "new_progress": new_progress})