import time from logging import getLogger from threading import Thread from uuid import uuid4 from enums.locations import Locations from fastapi import HTTPException, APIRouter, Depends from fastapi.responses import JSONResponse from fudo import fudo from models.sales import ItemWeb, OrderWeb from models.items import Item, Order from models.user import User from services.fudo_service import add_product_to_fudo from services.email_service import get_email_sender import services.print_service as ps from services.data_service import DataServiceFactory from config.mails import PRINTER_DISCONNECTED_MAIL from config.messages import ErrorResponse, SuccessResponse, UserResponse from config.settings import DEVELOPMENT from auth.security import get_current_user from data.product_category import CAT_ITEMS logger = getLogger(__name__) # Data services initialization user_data_service = DataServiceFactory.get_user_service() product_data_service = DataServiceFactory.get_product_service() sale_data_service = DataServiceFactory.get_sales_service() # Global variables printer_orders = [] order_router = APIRouter() name_promo = "Shop" @order_router.post("/send") async def printer_order(order: OrderWeb, current_user: User = Depends(get_current_user)): """Process printer order""" logger.info(f"Printer order received from user {current_user.email} for table {order.table}") # Extract order data items = order.items table = order.table # Input validation if not items or not table: logger.warning(f"Invalid order data from user {current_user.email}: missing items or table") return JSONResponse(status_code=400, content={"message": ErrorResponse.MISSING_FIELDS}) if not isinstance(table, int): logger.warning(f"Invalid table type from user {current_user.email}: {type(table)}") return JSONResponse(status_code=400, content={"message": ErrorResponse.INVALID_TABLE_TYPE}) logger.info(f"Processing order for table {table} with {len(items)} items") # Get products data try: products = product_data_service.get_products([item.id for item in items]) logger.info(f"Retrieved {len(products)} products from database") except Exception as e: error_msg = f"Error retrieving products: {e}" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) types = set([CAT_ITEMS[product.type or ""] for product in products]) # Printer status validation if not DEVELOPMENT: try: printer_status = filter(lambda x: x == False, [ps.get_status(loc) for loc in types]) if not printer_status: logger.error(f"Printer is not connected. Order from user {current_user.email} cannot be processed.") # Send notification email to admins email_thread = Thread( target=get_email_sender().send_email, args=( PRINTER_DISCONNECTED_MAIL["subject"], PRINTER_DISCONNECTED_MAIL["body"].format(location=", ".join([loc.value for loc in printer_status])), #type: ignore ["erwinjacimino2003@gmail.com", "mompyn@gmail.com"] ), daemon=True ) email_thread.start() return JSONResponse(status_code=424, content={"message": ErrorResponse.PRINTER_DISCONNECTED}) except Exception as e: logger.error(f"Error checking printer status: {e}") return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) # Input validation # Add products to Fudo product_errors = [] beers_for_promo = 0 try: fudo.get_token() logger.info("Fudo token obtained successfully") for item, product in zip(items, products): try: # Si es dia de promo y es menos de las 8 pm if time.localtime().tm_wday + 1 == product.promo_day and product.promo_id and time.localtime().tm_hour < 20: logger.info(f"Applying promotion for product {product.id} on table {table}") fudo_product = add_product_to_fudo(product.promo_id, item.quantity, table) #en caso contrario else: if product.type == name_promo: beers_for_promo += item.quantity logger.debug(f"Added {item.quantity} beers for promotion calculation") fudo_product = add_product_to_fudo(item.id, item.quantity, table) logger.info(f"Added product {item.id} to table {table} with quantity {item.quantity} in fudo") if not fudo_product: error_msg = f"Error adding product {item.id} to table {table}." product_errors.append(error_msg) logger.error(error_msg) except Exception as e: error_msg = f"Error processing product {item.id}: {e}" logger.error(error_msg) product_errors.append(error_msg) except Exception as e: error_msg = f"Error with Fudo integration: {e}" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) if product_errors: logger.error(f"Product errors occurred: {product_errors}") return JSONResponse( status_code=424, content={"message": ErrorResponse.PRODUCT_ADD_ERROR, "errors": product_errors} ) # User validation try: user = user_data_service.get_by_id(order.customerId) if not user: logger.warning(f"User not found: {order.customerId}") return JSONResponse(status_code=404, content={"message": UserResponse.USER_NOT_FOUND.format(user_id=order.customerId)}) logger.info(f"Order customer validated: {user.email}") except Exception as e: error_msg = f"Error validating user {order.customerId}: {e}" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) # Get active sale try: active_sale_id = fudo.get_active_sale(fudo.get_table(table)) if not active_sale_id: error_msg = f"No active sale found for table {table}" logger.error(error_msg) raise HTTPException(status_code=404, detail=error_msg) active_sale_id = active_sale_id['id'] logger.info(f"Active sale found for table {table}: {active_sale_id}") except HTTPException: raise except Exception as e: error_msg = f"Error retrieving active sale for table {table}: {e}" logger.error(error_msg) raise HTTPException(status_code=500, detail="Error interno del servidor") # Update user reward progress try: new_progress = user.reward_progress + beers_for_promo * 10 user_data_service.set_reward_progress(user.id, new_progress) logger.info(f"Updated reward progress for user {user.email}: {user.reward_progress} -> {new_progress}") except Exception as e: error_msg = f"Error updating reward progress for user {user.id}: {e}" logger.error(error_msg) # Don't fail the order for this, just log it new_progress = user.reward_progress # Create sale record try: sale = sale_data_service.create( order.customerId, active_sale_id or uuid4().hex, order.totalAmount, order.table, [item.id for item in items], [item.quantity for item in items] ) if sale > 0: logger.info(f"Sale created successfully: ID {sale}") else: error_msg = "Failed to create sale record" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) except Exception as e: error_msg = f"Error creating sale record: {e}" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) # Print order try: pizza_items = [ Item(name=product.name, price=product.price, quantity=item.quantity, comment=item.comment) for item, product in zip(items, products) if CAT_ITEMS.get(product.type) == Locations.PIZZAS]#type: ignore burger_items = [ Item(name=product.name, price=product.price, quantity=item.quantity, comment=item.comment) for item, product in zip(items, products) if CAT_ITEMS.get(product.type) == Locations.BURGUER]#type: ignore bar_items = [ Item(name=product.name, price=product.price, quantity=item.quantity, comment=item.comment) for item, product in zip(items, products) if CAT_ITEMS.get(product.type) == Locations.BAR]#type: ignore coctelery_items = [ Item(name=product.name, price=product.price, quantity=item.quantity, comment=item.comment) for item, product in zip(items, products) if CAT_ITEMS.get(product.type) == Locations.COCTELERY]#type: ignore if pizza_items: ps.print_order(Order(table=table, items=pizza_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.PIZZAS) logger.info(f"Order pizza printed successfully for table {table}") if burger_items: ps.print_order(Order(table=table, items=burger_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.BURGUER) logger.info(f"Order burger printed successfully for table {table}") if bar_items: ps.print_order(Order(table=table, items=bar_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.BAR) logger.info(f"Order bar printed successfully for table {table}") if coctelery_items: ps.print_order(Order(table=table, items=coctelery_items, customerName=user.name, totalAmount=order.totalAmount, orderDate=order.orderDate), location=Locations.COCTELERY) logger.info(f"Order coctelery printed successfully for table {table}") logger.info(f"Order printed successfully for table {table}") except Exception as e: error_msg = f"Error printing order for table {table}: {e}" logger.error(error_msg) return JSONResponse(status_code=500, content={"message": "Error interno del servidor"}) # Don't fail the order for print issues, just log it logger.info(f"Logging order for table {table} with sale ID {sale}, products= {[(product.name, item.quantity) for product, item in zip(products, items)]}") logger.info(f"Order processing completed successfully for table {table}, sale ID: {sale}") return JSONResponse({"message": SuccessResponse.ORDER_SUCCESS, "new_progress": new_progress})