|
@@ -1,208 +1,86 @@
|
|
|
from logging import getLogger
|
|
from logging import getLogger
|
|
|
-from typing import List
|
|
|
|
|
-from uuid import uuid4
|
|
|
|
|
-
|
|
|
|
|
-from pydantic import BaseModel
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from fastapi import APIRouter, Depends
|
|
|
|
|
|
|
|
-from toteat import toteat as fudo
|
|
|
|
|
-from models.sales import ItemWeb, OrderWeb
|
|
|
|
|
-from models.items import Product
|
|
|
|
|
|
|
+from models.sales import OrderWeb
|
|
|
from models.user import User
|
|
from models.user import User
|
|
|
-from services.fudo_service import add_product_to_fudo
|
|
|
|
|
-from services.data_service import DataServiceFactory
|
|
|
|
|
-from config.messages import ErrorResponse, SuccessResponse
|
|
|
|
|
from auth.security import get_current_user
|
|
from auth.security import get_current_user
|
|
|
|
|
+from config.messages import ErrorResponse, SuccessResponse
|
|
|
from utils.responses import error_response, success_response
|
|
from utils.responses import error_response, success_response
|
|
|
|
|
+from services.order_service import (
|
|
|
|
|
+ compare_prices,
|
|
|
|
|
+ fetch_sorted_products,
|
|
|
|
|
+ push_items_to_toteat,
|
|
|
|
|
+ get_active_sale_id,
|
|
|
|
|
+ update_reward_progress,
|
|
|
|
|
+ create_sale_record,
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
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()
|
|
|
|
|
-
|
|
|
|
|
order_router = APIRouter()
|
|
order_router = APIRouter()
|
|
|
-name_promo = "Cervezas"
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class ComparePricesResponse(BaseModel):
|
|
|
|
|
- product: Product
|
|
|
|
|
- oldPrice: int
|
|
|
|
|
- newPrice: int
|
|
|
|
|
- isAvailable: bool
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def compare_prices(products: List[Product], items: List[ItemWeb]) -> list:
|
|
|
|
|
- """Compare prices of products and items and return the cheapest product"""
|
|
|
|
|
- # Initialize a dictionary to store the prices
|
|
|
|
|
- prices: List[ComparePricesResponse] = []
|
|
|
|
|
- for product, item in zip(products, items):
|
|
|
|
|
- if product.status == 0:
|
|
|
|
|
- prices.append(
|
|
|
|
|
- ComparePricesResponse(
|
|
|
|
|
- product=product,
|
|
|
|
|
- oldPrice=item.price,
|
|
|
|
|
- newPrice=product.price,
|
|
|
|
|
- isAvailable=False
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
- continue
|
|
|
|
|
- if product.price != item.price:
|
|
|
|
|
- prices.append(
|
|
|
|
|
- ComparePricesResponse(
|
|
|
|
|
- product=product,
|
|
|
|
|
- oldPrice=item.price,
|
|
|
|
|
- newPrice=product.price,
|
|
|
|
|
- isAvailable=True
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- return list(map(lambda x: x.model_dump(), prices))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@order_router.post("/send")
|
|
@order_router.post("/send")
|
|
|
async def printer_order(order: OrderWeb, current_user: User = Depends(get_current_user)):
|
|
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
|
|
|
|
|
|
|
+ """Procesa un pedido: valida, registra en Toteat POS y persiste en BD."""
|
|
|
|
|
+ items, table = order.items, order.table
|
|
|
|
|
+ logger.info(f"Order received from {current_user.email} for table {table}")
|
|
|
|
|
|
|
|
- # Input validation
|
|
|
|
|
|
|
+ # 1. Validación de entrada
|
|
|
if not items or not table:
|
|
if not items or not table:
|
|
|
- logger.warning(f"Invalid order data from user {current_user.email}: missing items or table")
|
|
|
|
|
|
|
+ logger.warning(f"Missing fields from {current_user.email}")
|
|
|
return error_response(message=ErrorResponse.MISSING_FIELDS, status_code=400)
|
|
return error_response(message=ErrorResponse.MISSING_FIELDS, status_code=400)
|
|
|
-
|
|
|
|
|
if not isinstance(table, int):
|
|
if not isinstance(table, int):
|
|
|
- logger.warning(f"Invalid table type from user {current_user.email}: {type(table)}")
|
|
|
|
|
-
|
|
|
|
|
|
|
+ logger.warning(f"Invalid table type from {current_user.email}: {type(table)}")
|
|
|
return error_response(message=ErrorResponse.INVALID_TABLE_TYPE, status_code=400)
|
|
return error_response(message=ErrorResponse.INVALID_TABLE_TYPE, status_code=400)
|
|
|
|
|
|
|
|
- logger.info(f"Processing order for table {table} with {len(items)} items")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- # Get products data
|
|
|
|
|
|
|
+ # 2. Verificar precios actuales en BD
|
|
|
try:
|
|
try:
|
|
|
- products = await product_data_service.get_products([item.id for item in items])
|
|
|
|
|
- # Me aseguro de que los items y los productos esten en el mismo orden
|
|
|
|
|
- products = list(sorted(products, key=lambda x: x.id))
|
|
|
|
|
- items = list(sorted(items, key=lambda x: x.id))
|
|
|
|
|
- logger.info(f"Retrieved {len(products)} products from database")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ products = await fetch_sorted_products(items)
|
|
|
|
|
+ items = sorted(items, key=lambda x: x.id)
|
|
|
|
|
+ logger.info(f"Retrieved {len(products)} products for table {table}")
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- error_msg = f"Error getting products: {e}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
- return error_response(message=error_msg, status_code=500)
|
|
|
|
|
- # Comparo los precios de los items con los productos
|
|
|
|
|
- prices = compare_prices(products, items)
|
|
|
|
|
- if prices:
|
|
|
|
|
- return success_response(data=prices, message="El estado de los productos y items coincide con el de la venta", status_code=409)
|
|
|
|
|
|
|
+ return error_response(message=f"Error getting products: {e}", status_code=500)
|
|
|
|
|
|
|
|
- # Add products to Fudo
|
|
|
|
|
- product_errors = []
|
|
|
|
|
|
|
+ price_changes = compare_prices(products, items)
|
|
|
|
|
+ if price_changes:
|
|
|
|
|
+ return success_response(
|
|
|
|
|
+ data=price_changes,
|
|
|
|
|
+ message="El estado de los productos y items coincide con el de la venta",
|
|
|
|
|
+ status_code=409,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- beers_for_promo = 0
|
|
|
|
|
-
|
|
|
|
|
|
|
+ # 3. Registrar en Toteat POS
|
|
|
try:
|
|
try:
|
|
|
- fudo.get_token()
|
|
|
|
|
- logger.info("Fudo token obtained successfully")
|
|
|
|
|
-
|
|
|
|
|
- for item, product in zip(items, products):
|
|
|
|
|
- try:
|
|
|
|
|
-
|
|
|
|
|
- if product.type == name_promo and current_user.name != "Guest":
|
|
|
|
|
- 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)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ product_errors, beers = push_items_to_toteat(items, products, table, current_user.name)
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- error_msg = f"Error with Fudo integration: {e}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
-
|
|
|
|
|
- return error_response(message=error_msg, status_code=500)
|
|
|
|
|
|
|
+ return error_response(message=f"Error with Toteat integration: {e}", status_code=500)
|
|
|
|
|
|
|
|
if product_errors:
|
|
if product_errors:
|
|
|
- logger.error(f"Product errors occurred: {product_errors}")
|
|
|
|
|
-
|
|
|
|
|
- return error_response(error={"errors": product_errors}, message=ErrorResponse.PRODUCT_ADD_ERROR, status_code=424)
|
|
|
|
|
|
|
+ logger.error(f"Product errors for table {table}: {product_errors}")
|
|
|
|
|
+ return error_response(error={"errors": product_errors},
|
|
|
|
|
+ message=ErrorResponse.PRODUCT_ADD_ERROR, status_code=424)
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- # Get active sale
|
|
|
|
|
|
|
+ # 4. Venta activa de la mesa
|
|
|
try:
|
|
try:
|
|
|
- active_sale_id = fudo.get_active_sale(fudo.get_table(table))
|
|
|
|
|
|
|
+ active_sale_id = get_active_sale_id(table)
|
|
|
if not active_sale_id:
|
|
if not active_sale_id:
|
|
|
- error_msg = f"No active sale found for table {table}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
-
|
|
|
|
|
- return error_response(message=error_msg, status_code=404)
|
|
|
|
|
-
|
|
|
|
|
- active_sale_id = active_sale_id['id']
|
|
|
|
|
- logger.info(f"Active sale found for table {table}: {active_sale_id}")
|
|
|
|
|
|
|
+ return error_response(message=f"No active sale found for table {table}", status_code=404)
|
|
|
|
|
+ logger.info(f"Active sale for table {table}: {active_sale_id}")
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- error_msg = f"Error retrieving active sale for table {table}: {e}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
-
|
|
|
|
|
- return error_response(message=error_msg, status_code=500)
|
|
|
|
|
|
|
+ return error_response(message=f"Error retrieving active sale: {e}", status_code=500)
|
|
|
|
|
|
|
|
- # Update user reward progress
|
|
|
|
|
- try:
|
|
|
|
|
- new_progress = current_user.reward_progress + beers_for_promo * 10
|
|
|
|
|
- user_data_service.set_reward_progress(current_user.id, new_progress)
|
|
|
|
|
-
|
|
|
|
|
- logger.info(f"Updated reward progress for user {current_user.email}: {current_user.reward_progress} -> {new_progress}")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
- error_msg = f"Error updating reward progress for user {current_user.id}: {e}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
- # Don't fail the order for this, just log it
|
|
|
|
|
- new_progress = current_user.reward_progress
|
|
|
|
|
|
|
+ # 5. Puntos de fidelización (no bloquea el pedido si falla)
|
|
|
|
|
+ new_progress = update_reward_progress(current_user, beers)
|
|
|
|
|
|
|
|
- # Create sale record
|
|
|
|
|
|
|
+ # 6. Persistir en BD
|
|
|
try:
|
|
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 error_response(message=error_msg, status_code=500)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ sale_id = create_sale_record(order, active_sale_id, items)
|
|
|
|
|
+ if sale_id <= 0:
|
|
|
|
|
+ return error_response(message="Failed to create sale record", status_code=500)
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- error_msg = f"Error creating sale record: {e}"
|
|
|
|
|
- logger.error(error_msg)
|
|
|
|
|
-
|
|
|
|
|
- return error_response(message=error_msg, status_code=500)
|
|
|
|
|
|
|
+ return error_response(message=f"Error creating sale record: {e}", status_code=500)
|
|
|
|
|
|
|
|
- logger.info(f"Order processing completed successfully for table {table}, sale ID: {sale}")
|
|
|
|
|
|
|
+ logger.info(f"Order completed for table {table}, sale ID: {sale_id}")
|
|
|
return success_response(data={"new_progress": new_progress}, message=SuccessResponse.ORDER_SUCCESS)
|
|
return success_response(data={"new_progress": new_progress}, message=SuccessResponse.ORDER_SUCCESS)
|
|
|
-
|
|
|