Quellcode durchsuchen

listar las compras del usuario

latapp vor 9 Monaten
Ursprung
Commit
8f44f1d3d6

+ 0 - 1
auth/security.py

@@ -3,7 +3,6 @@ from typing import Union
 from venv import logger
 from fastapi import Depends, HTTPException
 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
-from typing import Annotated
 from logging import getLogger
 
 from pydantic import BaseModel

+ 1 - 0
config/settings.py

@@ -28,6 +28,7 @@ FEEDBACK_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs',
 OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
 PORT = int(os.getenv("PORT", 6001))
 PIN_KEY = os.getenv("PIN_KEY", "-1")
+DEVELOPMENT = True if os.getenv("NODE_ENV", "development").lower() == "development" else False
 if PIN_KEY == "-1":
     logging.warning("Using default PIN_KEY. Please set a strong PIN_KEY in your .env file for production.")
 # SECRET_KEY is crucial for signing session cookies.

+ 0 - 0
fudo/__init__.py


+ 1 - 2
models/__init__.py

@@ -1,7 +1,7 @@
 # Models module
 
 from .user import User, UserIDRequest, RegisterUserRequest
-from .items import Product, ProductWithQuantity
+from .items import Product
 from .sales import Sale, ItemWeb, OrderWeb
 from .blacklist import Blacklist
 from .chat import Message, ChatCompletionRequest
@@ -11,7 +11,6 @@ __all__ = [
     "UserIDRequest",
     "RegisterUserRequest",
     "Product",
-    "ProductWithQuantity",
     "Sale",
     "ItemWeb",
     "OrderWeb", 

+ 1 - 1
models/blacklist.py

@@ -6,5 +6,5 @@ class Blacklist(BaseModel):
     id: int
     user_id: int
     email: Optional[str] = None
-    nombre: Optional[str] = None
+    name: Optional[str] = None
     rut: Optional[str] = None

+ 1 - 6
models/items.py

@@ -10,9 +10,4 @@ class Product(BaseModel):
     price: float
     image: Optional[str] = None
     status: int = 1  # 0: Inactive, 1: Active
-
-class ProductWithQuantity(Product):
-    """Product model with quantity field for sales"""
-    cantidad: int = 1
-
-
+    quantity: Optional[int] = 1  # Optional quantity for the product

+ 5 - 14
models/sales.py

@@ -1,6 +1,8 @@
 from typing import List, Optional
 from pydantic import BaseModel
 
+from models.items import Product
+
 class ItemWeb(BaseModel):
     id: int
     quantity: int
@@ -12,25 +14,14 @@ class OrderWeb(BaseModel):
     orderDate: str
     table: int
 
-class Product(BaseModel):
-    """Legacy Product model - use models.items.Product instead"""
-    id: int
-    name: str
-    price: float
-    type: str
-    
-    description: str
-    image: str
-    status: int  # 0: inactive, 1: active
-    quantity: Optional[int] = 1  # Optional quantity for the product
-
 class Sale(BaseModel):
     """Sale model matching the database schema"""
     id: int
     user_id: int
     total: float
     fudo_id: str
-    fecha: str
+    date: str
     table: int
-    user_name: Optional[str] = None
+    username: Optional[str] = None
     user_email: Optional[str] = None
+    products: List[Product] = []

+ 1 - 1
models/user.py

@@ -13,7 +13,7 @@ class User(BaseModel):
     """User model matching the database schema"""
     id: int
     email: str
-    nombre: str
+    name: str
     rut: str
     pin_hash: str
     kleincoins: str

+ 0 - 1
public/main/index.html

@@ -180,7 +180,6 @@
               <td class="px-6 py-4">
                 <div class="flex flex-col">
                   <span class="list-element-name text-sm font-medium text-[#101419]">Klein Lager</span>
-                  <span class="list-element-desc text-xs text-[#58728d]">Cerveza rubia refrescante</span>
                 </div>
               </td>
               <td class="list-element-price px-6 py-4 text-sm font-semibold text-[#101419] text-right">$6.000</td>

+ 8 - 1
public/main/js/app.js

@@ -3,7 +3,7 @@ import { getProducts, sendOrder } from './service/product.js';
 import { login } from './service/auth.js'
 import { createGlobalLoader, showGlobalLoader, hideGlobalLoader } from './utils/loader.js';
 import { showError } from './utils/error.js';
-import { setupShoppingCart } from './utils/shoppingCart.js';
+import { addHistoryRow, setupShoppingCart } from './utils/shoppingCart.js';
 // --- Variables de Usuario ---
 let userId = -1;
 let userName = "Cliente";
@@ -307,6 +307,13 @@ async function processOrder() {
         };
         await sendOrder(orderData,userToken);
         alert("Pedido enviado con éxito.");
+        cart.forEach(item => {
+            addHistoryRow({
+                productName: item.name,
+                quantity: item.quantity,
+                price: item.price,
+            });
+        });
         cart = []
         updateCartDisplay();
     } catch (error) {

+ 1 - 1
public/main/js/service/product.js

@@ -6,7 +6,7 @@ async function sendOrder(order, token) {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
-        "X-App-Token": token
+        "Authorization": `Bearer ${token}`
       },
       body: JSON.stringify(order)
     });

+ 17 - 1
public/main/js/service/user.js

@@ -39,4 +39,20 @@ async function fetchUserSales(userId, token) {
   }
 }
 
-export { fetchUserSales };
+async function historyProducts(userId, token) {
+  const sales = await fetchUserSales(userId, token);
+  if (!sales || sales.length === 0) {
+    console.warn("No hay ventas para mostrar en el historial.");
+    return [];
+  }
+  const products = sales.map(sale => ([
+    sale.products.map(product => ({
+      quantity: product.quantity,
+      productName: product.name,
+      price: product.price.toFixed(0)
+    }))
+  ]));
+  return products.flat(2);
+}
+
+export { fetchUserSales, historyProducts };

+ 10 - 9
public/main/js/utils/shoppingCart.js

@@ -1,4 +1,4 @@
-import { fetchUserSales } from "../service/user.js";
+import { fetchUserSales, historyProducts } from "../service/user.js";
 
 const table = document.getElementById('historyTable');
 const rowTemplate = document.getElementById('historyRowTemplate');
@@ -12,18 +12,18 @@ function setHistoryTable(tableData) {
   }
     table.querySelector('tbody').innerHTML = ''; // Clear existing rows
     tableData.forEach(item => {
-      addHistoryRow(item.quantity, item.productName, item.price);
+      addHistoryRow(item);
     });
 }   
 
 
-function addHistoryRow(quantity, productName, price) {
+function addHistoryRow(sale) {
   const newRow = rowTemplate.content.cloneNode(true);
-  newRow.querySelector('.list-element-quantity').textContent = quantity;
-  newRow.querySelector('.list-element-name').textContent = productName;
-  newRow.querySelector('.list-element-price').textContent = `$${price.toFixed(2)}`;
+  newRow.querySelector('.list-element-quantity').textContent = sale.quantity;
+  newRow.querySelector('.list-element-name').textContent = sale.productName;
+  newRow.querySelector('.list-element-price').textContent = `$${sale.price}`;
 
-    addCartHistoryTotal(price);
+    addCartHistoryTotal(sale.price);
 
   table.querySelector('tbody').appendChild(newRow);
 }
@@ -35,7 +35,7 @@ function addCartHistoryTotal(price) {
 }
 
 function updateCartHistoryTotal(total) {
-  cartHistoryTotal.textContent = `$${total.toFixed(2)}`;
+  cartHistoryTotal.textContent = `$${total.toFixed(0)}`;
 }
 
 function getHistoryTotal() {
@@ -49,7 +49,8 @@ async function setupShoppingCart(userID, userToken, userName) {
   }else{
     console.warn("No se encontró el elemento de nombre en la tabla de historial.");
   }
-  const sales = await fetchUserSales(userID, userToken);
+  const sales = await historyProducts(userID, userToken);
+  
   if (sales){
     setHistoryTable(sales);
   }

+ 40 - 23
routes/orders.py

@@ -1,6 +1,7 @@
-from itertools import product
+from threading import Thread
 from math import log
 import time
+from uuid import uuid4
 from fastapi import HTTPException, APIRouter
 from fastapi.responses import JSONResponse
 from fudo import fudo
@@ -11,14 +12,14 @@ from services.logging_service import log_order
 from impresora.printer import PrinterUSB
 from impresora.order import Order, Item
 from logging import getLogger
-from threading import Thread
-from services.data_service import ProductDataService, UserDataService
+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
 logger = getLogger(__name__)
-user_data_service = UserDataService()
-product_data_service = ProductDataService()
+user_data_service = DataServiceFactory.get_user_service()
+product_data_service = DataServiceFactory.get_product_service()
+sale_data_service = DataServiceFactory.get_sales_service()
 printer_orders = []
 order_router = APIRouter()
 
@@ -27,17 +28,17 @@ async def printer_order(order: OrderWeb):
     """Process printer order"""
     logger.info("Printer order received")
     logger.info(order)
-
-    if not PrinterUSB.check_usb_port(0xfe6, 0x811e):
-        logger.error("Printer is not connected.")
-        email_thread = Thread(
-            target=send_email,
-            args=(PRINTER_DISCONNECTED_MAIL["subject"], PRINTER_DISCONNECTED_MAIL["body"], ["erwinjacimino2003@gmail.com"]),
-            daemon=True
-        )
-        email_thread.start()
-        logger.error("Email sent to admin about printer issue.")
-        return JSONResponse(status_code=424, content={"message": ErrorResponse.PRINTER_DISCONNECTED})
+    if not DEVELOPMENT:
+        if not PrinterUSB.check_usb_port(0xfe6, 0x811e):
+            logger.error("Printer is not connected.")
+            email_thread = Thread(
+                target=send_email,
+                args=(PRINTER_DISCONNECTED_MAIL["subject"], PRINTER_DISCONNECTED_MAIL["body"], ["erwinjacimino2003@gmail.com"]),
+                daemon=True
+            )
+            email_thread.start()
+            logger.error("Email sent to admin about printer issue.")
+            return JSONResponse(status_code=424, content={"message": ErrorResponse.PRINTER_DISCONNECTED})
 
     items = order.items
     table = order.table
@@ -52,9 +53,9 @@ async def printer_order(order: OrderWeb):
     product_errors = []
     for item in items:
         fudo.get_token()
-        # product = add_product_to_fudo(item.id, item.quantity, table)
-        # if not product:
-        #     product_errors.append(f"Error adding product {item.id} to table {table}.")
+        product = add_product_to_fudo(item.id, item.quantity, table)
+        if not product:
+            product_errors.append(f"Error adding product {item.id} to table {table}.")
 
     if product_errors:
         return JSONResponse(
@@ -63,17 +64,33 @@ async def printer_order(order: OrderWeb):
         )
 
     user = user_data_service.get_by_id(order.customerId)
+    
     if not user:
         return JSONResponse(status_code=404, content={"message": UserResponse.USER_NOT_FOUND.format(user_id=order.customerId)})
+    active_sale_id = fudo.get_active_sale(fudo.get_table(table))
+    if not active_sale_id:
+        logger.error(f"Error: No active sale found for table {table}.")
+        raise HTTPException(status_code=404)
+    active_sale_id = active_sale_id['id']
     products = product_data_service.get_products([item.id for item in items])
+
+    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]
+    )   
+    logger.info(f"Sale created: {sale}")
     # Print order
     printer_orders.append(Order(
-        user=user.nombre if user else "Unknown User",
+        user=user.name if user else "Unknown User",
         items=[Item(product.name, product.price, item.quantity) for product, item in zip(products, items)]
     ))
 
     # Log order
-    log_order(user.nombre, order.table, order_date=order.orderDate, items=[product.name for product in products])
+    log_order(user.name, order.table, order_date=order.orderDate, items=[product.name for product in products])
 
     return JSONResponse({"message": SuccessResponse.ORDER_SUCCESS})
 
@@ -90,7 +107,7 @@ def order_thread():
                     logger.error("Printer is not connected.")
                     continue
                 
-                # printer.print_order(order)
+                printer.print_order(order)
                 logger.info(f"Order printed: {order}")
             except Exception as e:
                 logger.error(f"Error printing order: {e}")

+ 5 - 6
routes/sales.py

@@ -1,15 +1,13 @@
-from csv import Error
-from tkinter import E
+from logging import getLogger
+from venv import logger
 from fastapi.responses import JSONResponse
-from pydantic import BaseModel
 from config.messages import ErrorResponse
-from models import sales
 from services.data_service import SalesDataService
 from fastapi import APIRouter, Depends
 from models.sales import Sale
 sale_data_service = SalesDataService()
 
-
+logger = getLogger(__name__)
 sales_router = APIRouter()
 
 @sales_router.get("/user/{user_id}", response_model=list[Sale])
@@ -20,7 +18,8 @@ def get_user_sales(user_id: int):
             status_code=404,
             content={"message": ErrorResponse.SALE_NOT_FOUND}
         )
+    logger.info(f"Sales found for user {user_id}: {len(sales)} sales")
     return JSONResponse(
         status_code=200,
-        content={"sales": sales, "message": "Ventas obtenidas correctamente."}
+        content={"sales": [sale.model_dump() for sale in sales], "message": "Ventas obtenidas correctamente."}
     )

+ 2 - 2
routes/users.py

@@ -50,7 +50,7 @@ async def register_user(request: RegisterUserRequest):
     send_email(
         REGISTER_MAIL["subject"],
         REGISTER_MAIL["body"],
-        [user.email], name=user.nombre, app_name=APPNAME, pin=pin
+        [user.email], name=user.name, app_name=APPNAME, pin=pin
     )
     return JSONResponse(status_code=201, content={"message": SuccessResponse.USER_CREATED_SUCCESS, "data": {
         **user.model_dump(exclude={"pin_hash"}),
@@ -77,7 +77,7 @@ async def login_user(request: LoginRequest):
         redis_client.delete(f"login_attempts:{request.email}")
         return JSONResponse(status_code=200, content={"message": SuccessResponse.LOGIN_SUCCESS, "data": {
             "id": user.id,
-            "name": user.nombre,
+            "name": user.name,
             "email": user.email,
             "kleincoins": user.kleincoins,
             "created_at": user.created_at,

+ 146 - 107
services/data_service.py

@@ -2,18 +2,17 @@ import json
 from math import log
 import os
 import sqlite3
-from typing import List, Dict, Optional, Any, Union
+from typing import List, Dict, Optional, Any
 from abc import ABC, abstractmethod
 from config.settings import BG_DATA_PATH, DB_PATH, PRODUCT_DATA_PATH
 from logging import getLogger
 from datetime import datetime
-import uuid
 from cryptography.fernet import Fernet
 from config.settings import PIN_KEY
 
 # Import models
 from models.user import User
-from models.items import Product, ProductWithQuantity
+from models.items import Product
 from models.sales import Sale
 from models.blacklist import Blacklist
 
@@ -28,14 +27,14 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 -----------------------------------
 - id           INTEGER PRIMARY KEY AUTOINCREMENT
 - email       TEXT UNIQUE NOT NULL
-- nombre       TEXT NOT NULL
+- name       TEXT NOT NULL
 - rut          TEXT UNIQUE NOT NULL
 - pin_hash     TEXT NOT NULL (encriptado)
 - kleincoins   TEXT NOT NULL (encriptado, valor por defecto "0")
 - created_at   TEXT NOT NULL (fecha de creación en formato ISO 8601)
 (Guarda la información del usuario con su pin hasheado y kleincoins encriptadas)
 
-2. Tabla: productos
+2. Tabla: products
 -----------------------------------
 - id           INTEGER PRIMARY KEY
 - name         TEXT NOT NULL
@@ -46,21 +45,21 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 - status       INTEGER DEFAULT 1 NOT NULL CHECK (status IN (0, 1)) -- 0: Inactivo, 1: Activo
 (Guarda los productos disponibles para venta con su estado activo/inactivo)
 
-3. Tabla: ventas
+3. Tabla: sales
 -----------------------------------
 - id           INTEGER PRIMARY KEY AUTOINCREMENT
 - user_id      INTEGER NOT NULL (relación a users.id)
 - total        REAL NOT NULL (precio total de la venta)
 - fudo_id      TEXT UNIQUE NOT NULL (ID string único por venta)
-- fecha        TEXT NOT NULL (fecha y hora en formato ISO 8601)
+- date        TEXT NOT NULL (fecha y hora en formato ISO 8601)
 - table        INTEGER NOT NULL (número de mesa)
 (Guarda cada venta, asociada a un usuario y mesa)
 
-4. Tabla: venta_productos
+4. Tabla: sale_products
 -----------------------------------
-- venta_id     INTEGER NOT NULL (relación a ventas.id)
-- producto_id  INTEGER NOT NULL (relación a productos.id)
-- cantidad     INTEGER NOT NULL DEFAULT 1 (cantidad del producto)
+- sale_id     INTEGER NOT NULL (relación a sales.id)
+- product_id  INTEGER NOT NULL (relación a products.id)
+- quantity    INTEGER NOT NULL DEFAULT 1 (cantidad del producto)
 (Relación muchos a muchos entre ventas y productos con cantidad)
 
 5. Tabla: blacklist
@@ -71,9 +70,9 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 
 RELACIONES:
 -----------------------------------
-- users puede tener muchas ventas
-- ventas puede tener muchos productos (y viceversa), por eso se usa una tabla intermedia (venta_productos)
-- productos pueden repetirse en múltiples ventas
+- users puede tener muchas sales
+- sales puede tener muchos productos (y viceversa), por eso se usa una tabla intermedia (sale_products)
+- products pueden repetirse en múltiples sales
 """
 
 # Base abstract class for data access
@@ -117,14 +116,14 @@ class BaseDataService(ABC):
 class UserDataService(BaseDataService):
     """Service for managing user data"""
     #region Create
-    def create(self, nombre: str, email: str, rut: str, pin_hash: str) -> int:
+    def create(self, name: str, email: str, rut: str, pin_hash: str) -> int:
         """Add a new user to the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "INSERT INTO users (nombre, email, rut, pin_hash, kleincoins, created_at) VALUES (?, ?, ?, ?, ?, ?)",
-                (nombre, email, rut, fernet.encrypt(pin_hash.encode()).decode(), fernet.encrypt(b"0").decode(), datetime.now().isoformat())
+                "INSERT INTO users (name, email, rut, pin_hash, kleincoins, created_at) VALUES (?, ?, ?, ?, ?, ?)",
+                (name, email, rut, fernet.encrypt(pin_hash.encode()).decode(), fernet.encrypt(b"0").decode(), datetime.now().isoformat())
             )
             conn.commit()
             user_id = cursor.lastrowid
@@ -152,7 +151,7 @@ class UserDataService(BaseDataService):
             User(
                 id=user[0],
                 email=user[1],
-                nombre=user[2],
+                name=user[2],
                 rut=user[3],
                 pin_hash=fernet.decrypt(user[4].encode()).decode(),
                 kleincoins=fernet.decrypt(user[5].encode()).decode(),
@@ -171,7 +170,7 @@ class UserDataService(BaseDataService):
             return User(
                 id=user[0],
                 email=user[1],
-                nombre=user[2],
+                name=user[2],
                 rut=user[3],
                 pin_hash=fernet.decrypt(user[4].encode()).decode(),
                 kleincoins=fernet.decrypt(user[5].encode()).decode(),
@@ -191,7 +190,7 @@ class UserDataService(BaseDataService):
             return User(
                 id=user[0],
                 email=user[1],
-                nombre=user[2],
+                name=user[2],
                 rut=user[3],
                 pin_hash=user[4],
                 kleincoins=fernet.decrypt(user[5].encode()).decode(),
@@ -221,7 +220,7 @@ class UserDataService(BaseDataService):
             return User(
                 id=user[0],
                 email=user[1],
-                nombre=user[2],
+                name=user[2],
                 rut=user[3],
                 pin_hash=fernet.decrypt(user[4].encode()).decode(),
                 kleincoins=fernet.decrypt(user[5].encode()).decode(),
@@ -230,7 +229,7 @@ class UserDataService(BaseDataService):
         return None
     #endregion
     #region Update
-    def update(self, user_id: int, email=None, nombre=None, rut=None, pin_hash=None, kleincoins=None) -> bool:
+    def update(self, user_id: int, email=None, name=None, rut=None, pin_hash=None, kleincoins=None) -> bool:
         """Update user information in the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
@@ -239,9 +238,9 @@ class UserDataService(BaseDataService):
         if email:
             updates.append("email = ?")
             params.append(email)
-        if nombre:
-            updates.append("nombre = ?")
-            params.append(nombre)
+        if name:
+            updates.append("name = ?")
+            params.append(name)
         if rut:
             updates.append("rut = ?")
             params.append(rut)
@@ -348,7 +347,7 @@ class BlacklistDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT b.id, b.user_id, u.email, u.nombre, u.rut 
+            SELECT b.id, b.user_id, u.email, u.name, u.rut 
             FROM blacklist b 
             LEFT JOIN users u ON b.user_id = u.id
         """)
@@ -359,7 +358,7 @@ class BlacklistDataService(BaseDataService):
                 id=row[0],
                 user_id=row[1],
                 email=row[2],
-                nombre=row[3],
+                name=row[3],
                 rut=row[4]
             ) for row in blacklisted
         ]
@@ -369,7 +368,7 @@ class BlacklistDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT b.id, b.user_id, u.email, u.nombre, u.rut 
+            SELECT b.id, b.user_id, u.email, u.name, u.rut 
             FROM blacklist b 
             LEFT JOIN users u ON b.user_id = u.id 
             WHERE b.id = ?
@@ -381,7 +380,7 @@ class BlacklistDataService(BaseDataService):
                 id=row[0],
                 user_id=row[1],
                 email=row[2],
-                nombre=row[3],
+                name=row[3],
                 rut=row[4]
             )
         return None
@@ -450,7 +449,7 @@ class ProductDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "INSERT INTO productos (id, name, type, description, price, image, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
+                "INSERT INTO products (id, name, type, description, price, image, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
                 (id, name, type, description, price, image, status)
             )
             conn.commit()
@@ -490,7 +489,7 @@ class ProductDataService(BaseDataService):
         """Get all products from the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos")
+        cursor.execute("SELECT * FROM products")
         products = cursor.fetchall()
         conn.close()
         return [
@@ -509,7 +508,7 @@ class ProductDataService(BaseDataService):
         """Get product by ID"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos WHERE id = ?", (product_id,))
+        cursor.execute("SELECT * FROM products WHERE id = ?", (product_id,))
         product = cursor.fetchone()
         conn.close()
         if product:
@@ -528,7 +527,7 @@ class ProductDataService(BaseDataService):
         """Get products by type"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos WHERE type = ?", (product_type,))
+        cursor.execute("SELECT * FROM products WHERE type = ?", (product_type,))
         products = cursor.fetchall()
         conn.close()
         return [
@@ -547,7 +546,7 @@ class ProductDataService(BaseDataService):
         """Search products by name"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos WHERE name LIKE ?", (f"%{name}%",))
+        cursor.execute("SELECT * FROM products WHERE name LIKE ?", (f"%{name}%",))
         products = cursor.fetchall()
         conn.close()
         return [
@@ -569,7 +568,7 @@ class ProductDataService(BaseDataService):
         placeholders = ', '.join('?' for _ in product_ids)
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute(f"SELECT * FROM productos WHERE id IN ({placeholders})", product_ids)
+        cursor.execute(f"SELECT * FROM products WHERE id IN ({placeholders})", product_ids)
         products = cursor.fetchall()
         conn.close()
         return [
@@ -613,7 +612,7 @@ class ProductDataService(BaseDataService):
             conn.close()
             return False
         try:
-            cursor.execute(f"UPDATE productos SET {', '.join(updates)} WHERE id = ?", (*params, product_id))
+            cursor.execute(f"UPDATE products SET {', '.join(updates)} WHERE id = ?", (*params, product_id))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
@@ -629,7 +628,7 @@ class ProductDataService(BaseDataService):
         """Get only active products (status = 1)"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos WHERE status = 1")
+        cursor.execute("SELECT * FROM products WHERE status = 1")
         products = cursor.fetchall()
         conn.close()
         return [
@@ -648,7 +647,7 @@ class ProductDataService(BaseDataService):
         """Get only inactive products (status = 0)"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM productos WHERE status = 0")
+        cursor.execute("SELECT * FROM products WHERE status = 0")
         products = cursor.fetchall()
         conn.close()
         return [
@@ -683,7 +682,7 @@ class ProductDataService(BaseDataService):
         """Delete a product from the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("DELETE FROM productos WHERE id = ?", (product_id,))
+        cursor.execute("DELETE FROM products WHERE id = ?", (product_id,))
         conn.commit()
         success = cursor.rowcount > 0
         conn.close()
@@ -698,7 +697,7 @@ class ProductDataService(BaseDataService):
 class SalesDataService(BaseDataService):
     """Service for managing sales"""
     #region C
-    def create(self, user_id: int,fudo_id:str, total: float, table: int, product_ids: List[int], quantities: Optional[List[int]] = None) -> int:
+    def create(self, user_id: int, fudo_id: str, total: float, table: int, product_ids: List[int], quantities: Optional[List[int]] = None) -> int:
         """Create a new sale with products and quantities"""
         conn = self._get_connection()
         cursor = conn.cursor()
@@ -707,7 +706,7 @@ class SalesDataService(BaseDataService):
             
             # Insert sale
             cursor.execute(
-                "INSERT INTO ventas (user_id, total, fudo_id, fecha, 'table') VALUES (?, ?, ?, ?, ?)",
+                "INSERT INTO sales (user_id, total, fudo_id, date, 'table') VALUES (?, ?, ?, ?, ?)",
                 (user_id, total, fudo_id, fecha, table)
             )
             sale_id = cursor.lastrowid
@@ -720,7 +719,7 @@ class SalesDataService(BaseDataService):
                 for i, product_id in enumerate(product_ids):
                     quantity = quantities[i] if i < len(quantities) else 1
                     cursor.execute(
-                        "INSERT INTO venta_productos (venta_id, producto_id, cantidad) VALUES (?, ?, ?)",
+                        "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
                         (sale_id, product_id, quantity)
                     )
             
@@ -745,7 +744,7 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "INSERT INTO venta_productos (venta_id, producto_id, cantidad) VALUES (?, ?, ?)",
+                "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
                 (sale_id, product_id, quantity)
             )
             conn.commit()
@@ -759,6 +758,31 @@ class SalesDataService(BaseDataService):
         finally:
             conn.close()
     
+    def add_products_to_sale(self, sale_id: int, product_ids: List[int], quantities: Optional[List[int]] = None) -> bool:
+        """Add multiple products to an existing sale with quantities"""
+        conn = self._get_connection()
+        cursor = conn.cursor()
+        try:
+            if quantities is None:
+                quantities = [1] * len(product_ids)  # Default quantity 1
+            
+            for i, product_id in enumerate(product_ids):
+                quantity = quantities[i] if i < len(quantities) else 1
+                cursor.execute(
+                    "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
+                    (sale_id, product_id, quantity)
+                )
+            
+            conn.commit()
+            success = cursor.rowcount > 0
+            if success:
+                logger.info(f"Products added to sale {sale_id}.")
+            return success
+        except sqlite3.IntegrityError as e:
+            logger.error(f"Failed to add products to sale: {e}")
+            return False
+        finally:
+            conn.close()
     #endregion
     #region R
     def get_all(self) -> List[Sale]:
@@ -766,10 +790,10 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT v.id, v.user_id, v.total, v.fudo_id, v.fecha, v.table, u.nombre, u.email
-            FROM ventas v
-            LEFT JOIN users u ON v.user_id = u.id
-            ORDER BY v.fecha DESC
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
+            FROM sales s
+            LEFT JOIN users u ON s.user_id = u.id
+            ORDER BY s.date DESC
         """)
         sales = cursor.fetchall()
         conn.close()
@@ -779,10 +803,11 @@ class SalesDataService(BaseDataService):
                 user_id=sale[1],
                 total=sale[2],
                 fudo_id=sale[3],
-                fecha=sale[4],
+                date=sale[4],
                 table=sale[5],
-                user_name=sale[6],
-                user_email=sale[7]
+                username=sale[6],
+                user_email=sale[7],
+                products=self.get_sale_products(sale[0])
             ) for sale in sales
         ]
     
@@ -791,10 +816,10 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT v.id, v.user_id, v.total, v.fudo_id, v.fecha, v.table, u.nombre, u.email
-            FROM ventas v
-            LEFT JOIN users u ON v.user_id = u.id
-            WHERE v.id = ?
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
+            FROM sales s
+            LEFT JOIN users u ON s.user_id = u.id
+            WHERE s.id = ?
         """, (sale_id,))
         sale = cursor.fetchone()
         conn.close()
@@ -804,9 +829,9 @@ class SalesDataService(BaseDataService):
                 user_id=sale[1],
                 total=sale[2],
                 fudo_id=sale[3],
-                fecha=sale[4],
+                date=sale[4],
                 table=sale[5],
-                user_name=sale[6],
+                username=sale[6],
                 user_email=sale[7]
             )
         return None
@@ -816,10 +841,10 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT v.id, v.user_id, v.total, v.fudo_id, v.fecha, v.table, u.nombre, u.email
-            FROM ventas v
-            LEFT JOIN users u ON v.user_id = u.id
-            WHERE v.fudo_id = ?
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table, u.name, u.email
+            FROM sales s
+            LEFT JOIN users u ON s.user_id = u.id
+            WHERE s.fudo_id = ?
         """, (fudo_id,))
         sale = cursor.fetchone()
         conn.close()
@@ -829,9 +854,9 @@ class SalesDataService(BaseDataService):
                 user_id=sale[1],
                 total=sale[2],
                 fudo_id=sale[3],
-                fecha=sale[4],
+                date=sale[4],
                 table=sale[5],
-                user_name=sale[6],
+                username=sale[6],
                 user_email=sale[7]
             )
         return None
@@ -842,43 +867,57 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         cursor.execute(
             """
-            SELECT v.id, v.user_id, v.total, v.fudo_id, v.fecha, v."table", u.nombre, u.email
-            FROM ventas v
-            LEFT JOIN users u ON v.user_id = u.id
-            WHERE v.user_id = ?
-            ORDER BY v.fecha DESC
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s."table", u.name, u.email
+            FROM sales s
+            LEFT JOIN users u ON s.user_id = u.id
+            WHERE s.user_id = ?
+            ORDER BY s.date DESC
             """,
             (user_id,)
         )
+
         sales = cursor.fetchall()
         conn.close()
+
         return [
             Sale(
                 id=sale[0],
                 user_id=sale[1],
                 total=sale[2],
                 fudo_id=sale[3],
-                fecha=sale[4],
+                date=sale[4],
                 table=sale[5],
-                user_name=sale[6],
-                user_email=sale[7]
+                username=sale[6],
+                user_email=sale[7],
+                products=self.get_sale_products(sale[0])
             ) for sale in sales
         ]
     
-    def get_sale_products(self, sale_id: int) -> List[ProductWithQuantity]:
+    def get_sale_products_ids(self, sale_id: int) -> List[int]:
+        """Get product IDs for a specific sale"""
+        conn = self._get_connection()
+        cursor = conn.cursor()
+        cursor.execute("""
+            SELECT product_id FROM sale_products WHERE sale_id = ?
+        """, (sale_id,))
+        products = cursor.fetchall()
+        conn.close()
+        return [product[0] for product in products]
+
+    def get_sale_products(self, sale_id: int) -> List[Product]:
         """Get products for a specific sale with quantities"""
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT p.id, p.name, p.type, p.description, p.price, p.image, p.status, vp.cantidad
-            FROM venta_productos vp
-            JOIN productos p ON vp.producto_id = p.id
-            WHERE vp.venta_id = ?
+            SELECT p.id, p.name, p.type, p.description, p.price, p.image, p.status, sp.quantity
+            FROM sale_products sp
+            JOIN products p ON sp.product_id = p.id
+            WHERE sp.sale_id = ?
         """, (sale_id,))
         products = cursor.fetchall()
         conn.close()
         return [
-            ProductWithQuantity(
+            Product(
                 id=product[0],
                 name=product[1],
                 type=product[2],
@@ -886,7 +925,7 @@ class SalesDataService(BaseDataService):
                 price=product[4],
                 image=product[5],
                 status=product[6],
-                cantidad=product[7]
+                quantity=product[7]
             ) for product in products
         ]
     
@@ -896,11 +935,11 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         cursor.execute(
             """
-            SELECT v.id, v.user_id, v.total, v.fudo_id, v.fecha, v."table", u.nombre, u.email
-            FROM ventas v
-            LEFT JOIN users u ON v.user_id = u.id
-            WHERE v."table" = ?
-            ORDER BY v.fecha DESC
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s."table", u.name, u.email
+            FROM sales s
+            LEFT JOIN users u ON s.user_id = u.id
+            WHERE s."table" = ?
+            ORDER BY s.date DESC
             """,
             (table,)
         )
@@ -912,9 +951,9 @@ class SalesDataService(BaseDataService):
                 user_id=sale[1],
                 total=sale[2],
                 fudo_id=sale[3],
-                fecha=sale[4],
+                date=sale[4],
                 table=sale[5],
-                user_name=sale[6],
+                username=sale[6],
                 user_email=sale[7]
             ) for sale in sales
         ]
@@ -925,7 +964,7 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "UPDATE venta_productos SET cantidad = ? WHERE venta_id = ? AND producto_id = ?",
+                "UPDATE sale_products SET quantity = ? WHERE sale_id = ? AND product_id = ?",
                 (new_quantity, sale_id, product_id)
             )
             conn.commit()
@@ -944,7 +983,7 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute(
-            "SELECT cantidad FROM venta_productos WHERE venta_id = ? AND producto_id = ?",
+            "SELECT quantity FROM sale_products WHERE sale_id = ? AND product_id = ?",
             (sale_id, product_id)
         )
         result = cursor.fetchone()
@@ -971,7 +1010,7 @@ class SalesDataService(BaseDataService):
             conn.close()
             return False
         try:
-            cursor.execute(f"UPDATE ventas SET {', '.join(updates)} WHERE id = ?", (*params, sale_id))
+            cursor.execute(f"UPDATE sales SET {', '.join(updates)} WHERE id = ?", (*params, sale_id))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
@@ -990,9 +1029,9 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             # Delete sale-product relationships first
-            cursor.execute("DELETE FROM venta_productos WHERE venta_id = ?", (sale_id,))
+            cursor.execute("DELETE FROM sale_products WHERE sale_id = ?", (sale_id,))
             # Delete the sale
-            cursor.execute("DELETE FROM ventas WHERE id = ?", (sale_id,))
+            cursor.execute("DELETE FROM sales WHERE id = ?", (sale_id,))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
@@ -1011,7 +1050,7 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute(
-            "DELETE FROM venta_productos WHERE venta_id = ? AND producto_id = ?",
+            "DELETE FROM sale_products WHERE sale_id = ? AND product_id = ?",
             (sale_id, product_id)
         )
         conn.commit()
@@ -1030,7 +1069,7 @@ class SalesDataService(BaseDataService):
         try:
             # Get current quantity
             cursor.execute(
-                "SELECT cantidad FROM venta_productos WHERE venta_id = ? AND producto_id = ?",
+                "SELECT quantity FROM sale_products WHERE sale_id = ? AND product_id = ?",
                 (sale_id, product_id)
             )
             result = cursor.fetchone()
@@ -1043,14 +1082,14 @@ class SalesDataService(BaseDataService):
             if new_quantity <= 0:
                 # Remove the product completely
                 cursor.execute(
-                    "DELETE FROM venta_productos WHERE venta_id = ? AND producto_id = ?",
+                    "DELETE FROM sale_products WHERE sale_id = ? AND product_id = ?",
                     (sale_id, product_id)
                 )
                 logger.info(f"Product {product_id} removed from sale {sale_id} (quantity reached 0).")
             else:
                 # Update with new quantity
                 cursor.execute(
-                    "UPDATE venta_productos SET cantidad = ? WHERE venta_id = ? AND producto_id = ?",
+                    "UPDATE sale_products SET quantity = ? WHERE sale_id = ? AND product_id = ?",
                     (new_quantity, sale_id, product_id)
                 )
                 logger.info(f"Product {product_id} quantity decreased to {new_quantity} in sale {sale_id}.")
@@ -1118,7 +1157,7 @@ def initialize_db():
     CREATE TABLE IF NOT EXISTS users (
         id INTEGER PRIMARY KEY AUTOINCREMENT,
         email TEXT UNIQUE NOT NULL,
-        nombre TEXT NOT NULL,
+        name TEXT NOT NULL,
         rut TEXT UNIQUE NOT NULL,
         pin_hash TEXT NOT NULL,
         kleincoins TEXT NOT NULL,
@@ -1127,9 +1166,9 @@ def initialize_db():
     """)
 
     # Crear tabla de productos
-    logger.info("Creando tabla de productos...")
+    logger.info("Creando tabla de products...")
     cursor.execute("""
-    CREATE TABLE IF NOT EXISTS productos (
+    CREATE TABLE IF NOT EXISTS products (
         id INTEGER PRIMARY KEY,
         name TEXT NOT NULL,
         type TEXT,
@@ -1141,28 +1180,28 @@ def initialize_db():
     """)
 
     # Crear tabla de ventas
-    logger.info("Creando tabla de ventas...")
+    logger.info("Creando tabla de sales...")
     cursor.execute("""
-    CREATE TABLE IF NOT EXISTS ventas (
+    CREATE TABLE IF NOT EXISTS sales (
         id INTEGER PRIMARY KEY AUTOINCREMENT,
         user_id INTEGER NOT NULL,
         total REAL NOT NULL,
-        fudo_id TEXT UNIQUE NOT NULL,
-        fecha TEXT NOT NULL,
+        fudo_id TEXT NOT NULL,
+        date TEXT NOT NULL,
         "table" INTEGER NOT NULL,
         FOREIGN KEY (user_id) REFERENCES users(id)
     );
     """)
 
     # Crear tabla intermedia para ventas y productos
-    logger.info("Creando tabla intermedia de venta_productos...")
+    logger.info("Creando tabla intermedia de sale_products...")
     cursor.execute("""
-    CREATE TABLE IF NOT EXISTS venta_productos (
-        venta_id INTEGER NOT NULL,
-        producto_id INTEGER NOT NULL,
-        cantidad INTEGER NOT NULL DEFAULT 1,
-        FOREIGN KEY (venta_id) REFERENCES ventas(id),
-        FOREIGN KEY (producto_id) REFERENCES productos(id)
+    CREATE TABLE IF NOT EXISTS sale_products (
+        sale_id INTEGER NOT NULL,
+        product_id INTEGER NOT NULL,
+        quantity INTEGER NOT NULL DEFAULT 1,
+        FOREIGN KEY (sale_id) REFERENCES sales(id),
+        FOREIGN KEY (product_id) REFERENCES products(id)
     );
     """)