Browse Source

separar impresora del server principal

latapp 9 months ago
parent
commit
1eca20bb2c

+ 2 - 1
.gitignore

@@ -13,4 +13,5 @@ data/data.db
 mrda.txt
 .env
 public/images/
-/local/
+/local/
+deploy.sh

+ 1 - 2
app.py

@@ -1,8 +1,7 @@
-from time import struct_time
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from starlette.middleware.sessions import SessionMiddleware
-from config.settings import SECRET_KEY, validate_config
+from config.settings import SECRET_KEY
 from routes import sales
 
 

+ 9 - 0
config/settings.py

@@ -53,3 +53,12 @@ def validate_config():
         logger.critical("CRITICAL ERROR: OPENAI_API_KEY environment variable not set. The application will not work correctly.")
         return False
     return True
+
+#postgresql connection settings
+POSTGRESQL_DB_CONFIG = {
+    'dbname': os.getenv('POSTGRES_DB', 'pedidos_express'),
+    'user': os.getenv('POSTGRES_USER', 'superti'),
+    'password': os.getenv('POSTGRES_PASSWORD', 'BTD2DALN55N'),
+    'host': os.getenv('POSTGRES_HOST', 'localhost'),
+    'port': os.getenv('POSTGRES_PORT', '5432')
+}

+ 0 - 0
impresora/__init__.py


+ 0 - 25
impresora/order.py

@@ -1,25 +0,0 @@
-import datetime
-import tabulate
-
-class Item:
-    def __init__(self, name, price, quantity):
-        self.name = name
-        self.price = price
-        self.quantity = quantity
-
-class Order:
-    def __init__(self,user, items):
-        self.user: str = user
-        self.items:list[Item] = items
-        self.total = sum(item.price * item.quantity for item in items)
-        self.date = datetime.datetime.now()
-
-
-    def tabulate(self):
-        headers = ["Nombre", "Cantidad", "Total"]
-        data = [[item.name, item.quantity, item.price*item.quantity] for item in self.items]
-        return tabulate.tabulate(data, headers=headers, tablefmt="fancy_grid")
-    
-    def __str__(self):
-        return f"Orden de {self.date.strftime('%d/%m/%Y')}\n{self.items}\nTotal: {self.total}"
-

+ 0 - 103
impresora/printer.py

@@ -1,103 +0,0 @@
-import logging
-from venv import logger
-from escpos.printer.win32raw import Win32Raw
-from escpos.printer.usb import Usb
-from escpos.printer.network import Network
-from escpos.printer import Dummy
-from escpos.escpos import Escpos
-from impresora.order import Order
-import usb.core
-
-logger = logging.getLogger(__name__)
-
-
-class BasePrinter:
-
-    def __init__(self):
-        self.bolded = False
-        self.font = "a"
-        self.doubled_size = False
-        self.work = Dummy()
-        self.printer:Escpos
-    
-    def is_connected(self):
-        return self.printer.is_online()
-
-    def change_font(self):
-        self.font = "b" if self.font == "a" else "a"
-        self.work.set(font=self.font)
-    def text(self, text):
-        # Print the text
-        self.work.text(text+"\n")
-    def bold(self):
-        # Set bold for the next print
-        self.bolded = not self.bolded
-        self.work.set(bold=self.bolded)
-    def double_size(self):
-        # Set double size for the next print
-        self.doubled_size = not self.doubled_size
-        self.work.set(double_height=self.doubled_size, double_width=self.doubled_size)
-        
-    def default(self):
-        # Set default size for the next print
-        self.work.set_with_default()
-        
-    def print_order(self, order:Order, mesa:int = 0):
-    # Print the order details
-        self.default()
-        self.double_size()
-        self.bold()
-
-        self.text(f"Orden Biergarten Klein\n")
-        self.default()
-        self.text(f"Fecha: {order.date.strftime('%d/%m/%Y: %H:%M:%S')}")
-        self.bold()
-        self.text(f"Mesa: {mesa}")
-        self.text(f"Cliente: {order.user}\n")
-        self.default()
-        self.text(order.tabulate())
-        self.bold()
-        self.text(f"Total: {order.total}")
-        
-        self.work.cut()
-
-        # Print the order details
-        self.printer._raw(self.work.output)
-        self.work.clear()
-        # Intentar cerrar la impresora si el método existe
-        if hasattr(self.printer, 'close'):
-            self.printer.close()
-
-
-class PrinterWindows(BasePrinter):
-    def __init__(self, printer_name="Impresora Termica"):
-        super().__init__()
-        self.printer = Win32Raw(printer_name)
-
-class PrinterUSB(BasePrinter):
-    def __init__(self, vendor_id, product_id):
-        super().__init__()
-        self.printer = Usb(vendor_id, product_id,in_ep=0x81,out_ep=0x03)
-        self.vendor_id = vendor_id
-        self.product_id = product_id
-    @staticmethod
-    def check_usb_port(vendor_id, product_id):
-        """Check if the USB printer is connected."""
-        try:
-            element = usb.core.find(idVendor=vendor_id, idProduct=product_id)
-            if element is None:
-                logger.error(f"Printer is not connected on USB port {vendor_id}:{product_id}.")
-                return False
-            return True
-        except Exception as e:
-            logger.error(f"Error checking USB printer: {e}")
-            return False
-    
-    def is_connected(self):
-        """Check if the USB printer is connected."""
-        return self.check_usb_port(self.vendor_id, self.product_id)
-
-class PrinterNetwork(BasePrinter):
-    def __init__(self, host, port):
-        super().__init__()
-        self.printer = Network(host, port)

+ 0 - 5
main.py

@@ -1,10 +1,8 @@
-import asyncio
 import os
 import uvicorn
 from app import create_app, setup_routes
 from config.settings import PORT, OPENAI_API_KEY, BG_DATA_PATH, validate_config
 from logging import getLogger
-from routes.orders import order_thread
 from threading import Thread
 
 from services.data_service import initialize_db
@@ -34,10 +32,7 @@ def main():
     else:
         logger.info(f"Datos del asistente cargados desde: {os.path.abspath(BG_DATA_PATH)}")
 
-    # Run the threads
 
-    thread = Thread(target=order_thread, daemon=True)
-    thread.start()
     # Start the server
     #rut without logs
     return app

+ 17 - 3
models/items.py

@@ -1,13 +1,27 @@
 from typing import List, Optional
 from pydantic import BaseModel
 
+class Item(BaseModel):
+    name: str
+    price: float
+    quantity: int
+
+class Order(BaseModel):
+    """Order model matching the database schema"""
+    customerName: str
+    items: List[Item]
+    totalAmount: float
+    orderDate: str
+    table: int
+
+
 class Product(BaseModel):
     """Product model matching the database schema"""
     id: int
     name: str
     type: Optional[str] = None
     description: Optional[str] = None
-    price: float
+    price: int
     image: Optional[str] = None
     status: int = 1  # 0: Inactive, 1: Active
     quantity: Optional[int] = 1  # Optional quantity for the product
@@ -17,7 +31,7 @@ class ProductEditRequest(BaseModel):
     name: Optional[str] = None
     type: Optional[str] = None
     description: Optional[str] = None
-    price: Optional[float] = None
+    price: Optional[int] = None
     image: Optional[str] = None
     status: Optional[int] = None  # 0: Inactive, 1: Active
     quantity: Optional[int] = None  # Optional quantity for the product
@@ -28,7 +42,7 @@ class ProductCreateRequest(BaseModel):
     name: str
     type: str
     description: str
-    price: float
+    price: int
     image: str
     status: Optional[int] = 1  # 0: Inactive, 1: Active
     quantity: Optional[int] = 1  # Optional quantity for the product

+ 2 - 0
models/sales.py

@@ -5,8 +5,10 @@ from models.items import Product
 
 class ItemWeb(BaseModel):
     id: int
+    price: int
     quantity: int
 
+
 class OrderWeb(BaseModel):
     customerId: int
     items: List[ItemWeb]

+ 1 - 1
public/main/index.html

@@ -164,7 +164,7 @@
       <h2 id="usernameTable" class="text-[19px] font-bold text-[#101419]">Pedido de Juan Pérez</h2>
     </div>
 
-    <div class="overflow-x-auto">
+    <div class="overflow-x-auto max-h-[33vh]">
       <table id="historyTable" class="w-full">
         <thead class="bg-gray-50 border-b border-gray-200">
           <tr>

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

@@ -301,7 +301,7 @@ async function processOrder() {
         const orderData = {
             customerId: userId,
             table: userTable,
-            items: cart.map(item => ({ id: item.id, quantity: item.quantity})),
+            items: cart.map(item => ({ id: item.id, price: item.price, quantity: item.quantity})),
             totalAmount: cart.reduce((sum, item) => sum + item.price * item.quantity, 0),
             orderDate: new Date().toLocaleString('sv-SE').replace(' ', 'T')
         };

+ 4 - 2
public/main/js/service/user.js

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

+ 14 - 27
routes/orders.py

@@ -5,17 +5,17 @@ from uuid import uuid4
 from fastapi import HTTPException, APIRouter
 from fastapi.responses import JSONResponse
 from fudo import fudo
-from models.sales import OrderWeb
+from models.sales import ItemWeb, OrderWeb
 from services.fudo_service import add_product_to_fudo
 from services.email_service import send_email
 from services.logging_service import log_order
-from impresora.printer import PrinterUSB
-from impresora.order import Order, Item
+import services.print_service  as ps
 from logging import getLogger
 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 models.items import Item, Order
 logger = getLogger(__name__)
 user_data_service = DataServiceFactory.get_user_service()
 product_data_service = DataServiceFactory.get_product_service()
@@ -29,7 +29,7 @@ async def printer_order(order: OrderWeb):
     logger.info("Printer order received")
     logger.info(order)
     if not DEVELOPMENT:
-        if not PrinterUSB.check_usb_port(0xfe6, 0x811e):
+        if not ps.get_status():
             logger.error("Printer is not connected.")
             email_thread = Thread(
                 target=send_email,
@@ -83,32 +83,19 @@ async def printer_order(order: OrderWeb):
         [item.quantity for item in items]
     )   
     logger.info(f"Sale created: {sale}")
-    # Print order
-    printer_orders.append(Order(
-        user=user.name if user else "Unknown User",
-        items=[Item(product.name, product.price, item.quantity) for product, item in zip(products, items)]
-    ))
+
+    ps.print_order(
+        Order(
+            table=table,
+            items=[Item(name=product.name, price=product.price or 0, quantity=item.quantity) for product, item in zip(products, items)],
+            customerName=user.name,
+            totalAmount=order.totalAmount,
+            orderDate=order.orderDate
+        )
+    )
 
     # Log order
     log_order(user.name, order.table, order_date=order.orderDate, items=[product.name for product in products])
 
     return JSONResponse({"message": SuccessResponse.ORDER_SUCCESS})
 
-def order_thread():
-    """Thread to process orders"""
-    logger.info("Starting order thread")
-    while True:
-        if printer_orders:
-            order = printer_orders.pop(0)
-            logger.info(f"Processing order: {order}")
-            try:
-                printer = PrinterUSB(0xfe6, 0x811e)
-                if not printer.is_connected():
-                    logger.error("Printer is not connected.")
-                    continue
-                
-                printer.print_order(order)
-                logger.info(f"Order printed: {order}")
-            except Exception as e:
-                logger.error(f"Error printing order: {e}")
-        time.sleep(1)  # Sleep to avoid busy waiting

+ 156 - 110
services/data_service.py

@@ -2,7 +2,8 @@ import json
 from math import log
 import os
 import re
-import sqlite3
+import psycopg2
+from config.settings import POSTGRESQL_DB_CONFIG
 from typing import List, Dict, Optional, Any
 from abc import ABC, abstractmethod
 from config.settings import BG_DATA_PATH, DB_PATH, IMAGE_PATH, PRODUCT_DATA_PATH, CURRENT_URL
@@ -26,10 +27,10 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 
 1. Tabla: users
 -----------------------------------
-- id           INTEGER PRIMARY KEY AUTOINCREMENT
-- email       TEXT UNIQUE NOT NULL
-- name       TEXT NOT NULL
-- rut          TEXT UNIQUE NOT NULL
+- id           SERIAL PRIMARY KEY
+- email       VARCHAR(255) UNIQUE NOT NULL
+- name       VARCHAR(255) NOT NULL
+- rut          VARCHAR(20) 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)
@@ -38,19 +39,19 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 2. Tabla: products
 -----------------------------------
 - id           INTEGER PRIMARY KEY
-- name         TEXT NOT NULL
-- type         TEXT
-- description  TEXT
-- price        REAL NOT NULL
+- name         VARCHAR(255) NOT NULL
+- type         VARCHAR(100) NOT NULL
+- description  TEXT NOT NULL
+- price        INTEGER NOT NULL
 - image        TEXT (URL de la imagen)
 - 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: 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)
+- id           SERIAL PRIMARY KEY
+- user_id      INTEGER NOT NULL REFERENCES users.id
+- total        INTEGER NOT NULL (precio total de la venta)
 - fudo_id      TEXT UNIQUE NOT NULL (ID string único por venta)
 - date        TEXT NOT NULL (fecha y hora en formato ISO 8601)
 - table        INTEGER NOT NULL (número de mesa)
@@ -65,7 +66,7 @@ ESQUEMA DE BASE DE DATOS SQLITE (data.db)
 
 5. Tabla: blacklist
 -----------------------------------
-- id           INTEGER PRIMARY KEY AUTOINCREMENT
+- id           SERIAL PRIMARY KEY
 - user_id      INTEGER NOT NULL (relación a users.id)
 (Usuarios bloqueados o no autorizados para ciertas acciones)
 
@@ -79,14 +80,20 @@ RELACIONES:
 # Base abstract class for data access
 class BaseDataService(ABC):
     """Abstract base class for data services"""
-    
-    def __init__(self, db_path: str = DB_PATH):
-        self.db_path = db_path
-    
-    def _get_connection(self) -> sqlite3.Connection:
+
+    def __init__(self):
+        self.db_config = POSTGRESQL_DB_CONFIG
+
+    def _get_connection(self):
         """Get database connection"""
-        return sqlite3.connect(self.db_path)
-    
+        return psycopg2.connect(
+            dbname=self.db_config['dbname'],
+            user=self.db_config['user'],
+            password=self.db_config['password'],
+            host=self.db_config['host'],
+            port=self.db_config['port']
+        )
+
     @abstractmethod
     def get_all(self) -> List[Any]:
         """Get all records"""
@@ -123,7 +130,7 @@ class UserDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "INSERT INTO users (name, email, rut, pin_hash, kleincoins, created_at) VALUES (?, ?, ?, ?, ?, ?)",
+                "INSERT INTO users (name, email, rut, pin_hash, kleincoins, created_at) VALUES (%s, %s, %s, %s, %s, %s)",
                 (name, email, rut, fernet.encrypt(pin_hash.encode()).decode(), fernet.encrypt(b"0").decode(), datetime.now().isoformat())
             )
             conn.commit()
@@ -134,7 +141,7 @@ class UserDataService(BaseDataService):
             else:
                 logger.error("Failed to add user.")
                 return -1
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to add user: {e}")
             return -1
         finally:
@@ -164,7 +171,7 @@ class UserDataService(BaseDataService):
         """Get user data from the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
+        cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
         user = cursor.fetchone()
         conn.close()
         if user:
@@ -183,7 +190,7 @@ class UserDataService(BaseDataService):
         """Get user by email"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
+        cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
         user = cursor.fetchone()
         conn.close()
         logger.debug(f"get_by_email: {email}, user: {user}")
@@ -213,8 +220,12 @@ class UserDataService(BaseDataService):
         """Get user permissions"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT permissions FROM users WHERE id = ?", (user_id,))
+        cursor.execute("SELECT permissions FROM users WHERE id = %s", (user_id,))
         result = cursor.fetchone()
+        if not result:
+            logger.error(f"User with ID {user_id} not found.")
+            return 0
+        result = result[0]
         conn.close()
         if result:
             return result[0]
@@ -224,8 +235,13 @@ class UserDataService(BaseDataService):
         """Get user by RUT"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM users WHERE rut = ?", (rut,))
+        cursor.execute("SELECT * FROM users WHERE rut = %s", (rut,))
         user = cursor.fetchone()
+        if not user:
+            logger.error(f"User with RUT {rut} not found.")
+            conn.close()
+            return None
+        user = user[0]
         conn.close()
         if user:
             return User(
@@ -247,31 +263,31 @@ class UserDataService(BaseDataService):
         updates = []
         params = []
         if email:
-            updates.append("email = ?")
+            updates.append("email = %s")
             params.append(email)
         if name:
-            updates.append("name = ?")
+            updates.append("name = %s")
             params.append(name)
         if rut:
-            updates.append("rut = ?")
+            updates.append("rut = %s")
             params.append(rut)
         if pin_hash:
-            updates.append("pin_hash = ?")
+            updates.append("pin_hash = %s")
             params.append(fernet.encrypt(pin_hash.encode()).decode())
         if kleincoins is not None:
-            updates.append("kleincoins = ?")
+            updates.append("kleincoins = %s")
             params.append(fernet.encrypt(str(kleincoins).encode()).decode())
         if not updates:
             conn.close()
             return False
         try:
-            cursor.execute(f"UPDATE users SET {', '.join(updates)} WHERE id = ?", (*params, user_id))
+            cursor.execute(f"UPDATE users SET {', '.join(updates)} WHERE id = %s", (*params, user_id))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
                 logger.info(f"User with ID {user_id} updated.")
             return success
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to update user: {e}")
             return False
         finally:
@@ -282,7 +298,7 @@ class UserDataService(BaseDataService):
         """Delete a user from the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("DELETE FROM users WHERE id = ?", (user_id,))
+        cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
         conn.commit()
         conn.close()
         if cursor.rowcount > 0:
@@ -298,7 +314,7 @@ class UserDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "UPDATE users SET kleincoins = ? WHERE id = ?", 
+                "UPDATE users SET kleincoins = %s WHERE id = %s", 
                 (fernet.encrypt(str(kleincoins).encode()).decode(), user_id)
             )
             conn.commit()
@@ -306,7 +322,7 @@ class UserDataService(BaseDataService):
             if success:
                 logger.info(f"Kleincoins updated for user {user_id}: {kleincoins}")
             return success
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to update kleincoins: {e}")
             return False
         finally:
@@ -316,7 +332,7 @@ class UserDataService(BaseDataService):
         """Get user's kleincoins"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT kleincoins FROM users WHERE id = ?", (user_id,))
+        cursor.execute("SELECT kleincoins FROM users WHERE id = %s", (user_id,))
         result = cursor.fetchone()
         conn.close()
         if result:
@@ -337,7 +353,7 @@ class BlacklistDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         try:
-            cursor.execute("INSERT INTO blacklist (user_id) VALUES (?)", (user_id,))
+            cursor.execute("INSERT INTO blacklist (user_id) VALUES (%s)", (user_id,))
             conn.commit()
             blacklist_id = cursor.lastrowid
             if blacklist_id:
@@ -346,7 +362,7 @@ class BlacklistDataService(BaseDataService):
             else:
                 logger.error(f"Failed to add user with ID {user_id} to blacklist.")
                 return -1
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to add user to blacklist: {e}")
             return -1
         finally:
@@ -382,7 +398,7 @@ class BlacklistDataService(BaseDataService):
             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 = ?
+            WHERE b.id = %s
         """, (id,))
         row = cursor.fetchone()
         conn.close()
@@ -425,7 +441,7 @@ class BlacklistDataService(BaseDataService):
         """Remove a blacklist entry by ID"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("DELETE FROM blacklist WHERE id = ?", (id,))
+        cursor.execute("DELETE FROM blacklist WHERE id = %s", (id,))
         conn.commit()
         success = cursor.rowcount > 0
         conn.close()
@@ -439,7 +455,7 @@ class BlacklistDataService(BaseDataService):
         """Remove a user from the blacklist"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("DELETE FROM blacklist WHERE user_id = ?", (user_id,))
+        cursor.execute("DELETE FROM blacklist WHERE user_id = %s", (user_id,))
         conn.commit()
         success = cursor.rowcount > 0
         conn.close()
@@ -459,14 +475,14 @@ class ProductDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         if image and "base64" in image:
-            extension = re.search(r'data:image/(.*?);base64,', image)
+            extension = re.search(r'data:image/(.*%s);base64,', image)
             image_name = f"{id}_img"
             if extension:
                 image_name += f".{extension.group(1)}"
             image = self._image_process(image_name, image)
         try:
             cursor.execute(
-                "INSERT INTO products (id, name, type, description, price, image, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
+                "INSERT INTO products (id, name, type, description, price, image, status) VALUES (%s, %s, %s, %s, %s, %s, %s)",
                 (id, name, type, description, price, image, status)
             )
             conn.commit()
@@ -477,7 +493,7 @@ class ProductDataService(BaseDataService):
             else:
                 logger.error("Failed to add product.")
                 return -1
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to add product: {e}")
             return -1
         finally:
@@ -525,7 +541,7 @@ class ProductDataService(BaseDataService):
         """Get product by ID"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM products WHERE id = ?", (product_id,))
+        cursor.execute("SELECT * FROM products WHERE id = %s", (product_id,))
         product = cursor.fetchone()
         conn.close()
         if product:
@@ -544,7 +560,7 @@ class ProductDataService(BaseDataService):
         """Get products by type"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM products WHERE type = ?", (product_type,))
+        cursor.execute("SELECT * FROM products WHERE type = %s", (product_type,))
         products = cursor.fetchall()
         conn.close()
         return [
@@ -563,7 +579,7 @@ class ProductDataService(BaseDataService):
         """Search products by name"""
         conn = self._get_connection()
         cursor = conn.cursor()
-        cursor.execute("SELECT * FROM products WHERE name LIKE ?", (f"%{name}%",))
+        cursor.execute("SELECT * FROM products WHERE name LIKE %s", (f"%{name}%",))
         products = cursor.fetchall()
         conn.close()
         return [
@@ -582,7 +598,7 @@ class ProductDataService(BaseDataService):
         """Get multiple products by their IDs"""
         if not product_ids:
             return []
-        placeholders = ', '.join('?' for _ in product_ids)
+        placeholders = ', '.join('%s' for _ in product_ids)
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute(f"SELECT * FROM products WHERE id IN ({placeholders})", product_ids)
@@ -623,23 +639,23 @@ class ProductDataService(BaseDataService):
         updates = []
         params = []
         if name is not None:
-            updates.append("name = ?")
+            updates.append("name = %s")
             params.append(name)
         if type is not None:
-            updates.append("type = ?")
+            updates.append("type = %s")
             params.append(type)
         if description is not None:
-            updates.append("description = ?")
+            updates.append("description = %s")
             params.append(description)
         if price is not None:
-            updates.append("price = ?")
+            updates.append("price = %s")
             params.append(price)
         if image is not None:
             if product.image and product.image != image:
                 if os.path.exists(os.path.join(IMAGE_PATH, product.image)):
                     os.remove(os.path.join(IMAGE_PATH, product.image))
                 try:
-                    extension = re.search(r'data:image/(.*?);base64,', image)
+                    extension = re.search(r'data:image/(.*%s);base64,', image)
                     if not extension:
                         raise ValueError("Invalid image format")
                     extension = extension.group(1)
@@ -647,22 +663,22 @@ class ProductDataService(BaseDataService):
                 except ValueError as e:
                     logger.error(f"Failed to process image: {e}")
                     return False
-            updates.append("image = ?")
+            updates.append("image = %s")
             params.append(image)
         if status is not None:
-            updates.append("status = ?")
+            updates.append("status = %s")
             params.append(status)
         if not updates:
             conn.close()
             return False
         try:
-            cursor.execute(f"UPDATE products SET {', '.join(updates)} WHERE id = ?", (*params, product_id))
+            cursor.execute(f"UPDATE products SET {', '.join(updates)} WHERE id = %s", (*params, product_id))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
                 logger.info(f"Product with ID {product_id} updated.")
             return success
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to update product: {e}")
             return False
         finally:
@@ -729,7 +745,7 @@ class ProductDataService(BaseDataService):
         product = self.get_by_id(product_id)
         if product and product.image:
             os.remove(os.path.join(IMAGE_PATH, product.image.removeprefix(CURRENT_URL + "/images/")))
-        cursor.execute("DELETE FROM products WHERE id = ?", (product_id,))
+        cursor.execute("DELETE FROM products WHERE id = %s", (product_id,))
         conn.commit()
         success = cursor.rowcount > 0
         conn.close()
@@ -753,10 +769,11 @@ class SalesDataService(BaseDataService):
             
             # Insert sale
             cursor.execute(
-                "INSERT INTO sales (user_id, total, fudo_id, date, 'table') VALUES (?, ?, ?, ?, ?)",
+                "INSERT INTO sales (user_id, total, fudo_id, date, table_number) VALUES (%s, %s, %s, %s, %s) RETURNING id",
                 (user_id, total, fudo_id, fecha, table)
             )
-            sale_id = cursor.lastrowid
+            last_sale = cursor.fetchone()
+            sale_id = last_sale[0] if last_sale else None
             
             if sale_id and product_ids:
                 # Insert sale-product relationships with quantities
@@ -766,7 +783,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 sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
+                        "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
                         (sale_id, product_id, quantity)
                     )
             
@@ -777,7 +794,7 @@ class SalesDataService(BaseDataService):
             else:
                 logger.error("Failed to create sale.")
                 return -1
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to create sale: {e}")
             conn.rollback()
             return -1
@@ -791,7 +808,7 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
+                "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
                 (sale_id, product_id, quantity)
             )
             conn.commit()
@@ -799,7 +816,7 @@ class SalesDataService(BaseDataService):
             if success:
                 logger.info(f"Product {product_id} (qty: {quantity}) added to sale {sale_id}.")
             return success
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to add product to sale: {e}")
             return False
         finally:
@@ -816,7 +833,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 sale_products (sale_id, product_id, quantity) VALUES (?, ?, ?)",
+                    "INSERT INTO sale_products (sale_id, product_id, quantity) VALUES (%s, %s, %s)",
                     (sale_id, product_id, quantity)
                 )
             
@@ -825,7 +842,7 @@ class SalesDataService(BaseDataService):
             if success:
                 logger.info(f"Products added to sale {sale_id}.")
             return success
-        except sqlite3.IntegrityError as e:
+        except psycopg2.IntegrityError as e:
             logger.error(f"Failed to add products to sale: {e}")
             return False
         finally:
@@ -866,7 +883,7 @@ class SalesDataService(BaseDataService):
             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 = ?
+            WHERE s.id = %s
         """, (sale_id,))
         sale = cursor.fetchone()
         conn.close()
@@ -891,7 +908,7 @@ class SalesDataService(BaseDataService):
             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 = ?
+            WHERE s.fudo_id = %s
         """, (fudo_id,))
         sale = cursor.fetchone()
         conn.close()
@@ -914,10 +931,10 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         cursor.execute(
             """
-            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s."table", u.name, u.email
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table_number, u.name, u.email
             FROM sales s
             LEFT JOIN users u ON s.user_id = u.id
-            WHERE s.user_id = ?
+            WHERE s.user_id = %s
             ORDER BY s.date DESC
             """,
             (user_id,)
@@ -945,7 +962,7 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute("""
-            SELECT product_id FROM sale_products WHERE sale_id = ?
+            SELECT product_id FROM sale_products WHERE sale_id = %s
         """, (sale_id,))
         products = cursor.fetchall()
         conn.close()
@@ -959,7 +976,7 @@ class SalesDataService(BaseDataService):
             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 = ?
+            WHERE sp.sale_id = %s
         """, (sale_id,))
         products = cursor.fetchall()
         conn.close()
@@ -982,10 +999,10 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         cursor.execute(
             """
-            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s."table", u.name, u.email
+            SELECT s.id, s.user_id, s.total, s.fudo_id, s.date, s.table_number, u.name, u.email
             FROM sales s
             LEFT JOIN users u ON s.user_id = u.id
-            WHERE s."table" = ?
+            WHERE s.table_number = %s
             ORDER BY s.date DESC
             """,
             (table,)
@@ -1011,7 +1028,7 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             cursor.execute(
-                "UPDATE sale_products SET quantity = ? WHERE sale_id = ? AND product_id = ?",
+                "UPDATE sale_products SET quantity = %s WHERE sale_id = %s AND product_id = %s",
                 (new_quantity, sale_id, product_id)
             )
             conn.commit()
@@ -1019,7 +1036,7 @@ class SalesDataService(BaseDataService):
             if success:
                 logger.info(f"Updated quantity to {new_quantity} for product {product_id} in sale {sale_id}.")
             return success
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to update product quantity: {e}")
             return False
         finally:
@@ -1030,7 +1047,7 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute(
-            "SELECT quantity FROM sale_products WHERE sale_id = ? AND product_id = ?",
+            "SELECT quantity FROM sale_products WHERE sale_id = %s AND product_id = %s",
             (sale_id, product_id)
         )
         result = cursor.fetchone()
@@ -1045,25 +1062,25 @@ class SalesDataService(BaseDataService):
         updates = []
         params = []
         if user_id is not None:
-            updates.append("user_id = ?")
+            updates.append("user_id = %s")
             params.append(user_id)
         if total is not None:
-            updates.append("total = ?")
+            updates.append("total = %s")
             params.append(total)
         if table is not None:
-            updates.append("table = ?")
+            updates.append("table = %s")
             params.append(table)
         if not updates:
             conn.close()
             return False
         try:
-            cursor.execute(f"UPDATE sales SET {', '.join(updates)} WHERE id = ?", (*params, sale_id))
+            cursor.execute(f"UPDATE sales SET {', '.join(updates)} WHERE id = %s", (*params, sale_id))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
                 logger.info(f"Sale with ID {sale_id} updated.")
             return success
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to update sale: {e}")
             return False
         finally:
@@ -1076,9 +1093,9 @@ class SalesDataService(BaseDataService):
         cursor = conn.cursor()
         try:
             # Delete sale-product relationships first
-            cursor.execute("DELETE FROM sale_products WHERE sale_id = ?", (sale_id,))
+            cursor.execute("DELETE FROM sale_products WHERE sale_id = %s", (sale_id,))
             # Delete the sale
-            cursor.execute("DELETE FROM sales WHERE id = ?", (sale_id,))
+            cursor.execute("DELETE FROM sales WHERE id = %s", (sale_id,))
             conn.commit()
             success = cursor.rowcount > 0
             if success:
@@ -1086,7 +1103,7 @@ class SalesDataService(BaseDataService):
             else:
                 logger.error(f"Failed to delete sale with ID {sale_id}.")
             return success
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to delete sale: {e}")
             return False
         finally:
@@ -1097,7 +1114,7 @@ class SalesDataService(BaseDataService):
         conn = self._get_connection()
         cursor = conn.cursor()
         cursor.execute(
-            "DELETE FROM sale_products WHERE sale_id = ? AND product_id = ?",
+            "DELETE FROM sale_products WHERE sale_id = %s AND product_id = %s",
             (sale_id, product_id)
         )
         conn.commit()
@@ -1116,7 +1133,7 @@ class SalesDataService(BaseDataService):
         try:
             # Get current quantity
             cursor.execute(
-                "SELECT quantity FROM sale_products WHERE sale_id = ? AND product_id = ?",
+                "SELECT quantity FROM sale_products WHERE sale_id = %s AND product_id = %s",
                 (sale_id, product_id)
             )
             result = cursor.fetchone()
@@ -1129,21 +1146,21 @@ class SalesDataService(BaseDataService):
             if new_quantity <= 0:
                 # Remove the product completely
                 cursor.execute(
-                    "DELETE FROM sale_products WHERE sale_id = ? AND product_id = ?",
+                    "DELETE FROM sale_products WHERE sale_id = %s AND product_id = %s",
                     (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 sale_products SET quantity = ? WHERE sale_id = ? AND product_id = ?",
+                    "UPDATE sale_products SET quantity = %s WHERE sale_id = %s AND product_id = %s",
                     (new_quantity, sale_id, product_id)
                 )
                 logger.info(f"Product {product_id} quantity decreased to {new_quantity} in sale {sale_id}.")
             
             conn.commit()
             return True
-        except sqlite3.Error as e:
+        except psycopg2.Error as e:
             logger.error(f"Failed to decrease product quantity: {e}")
             return False
         finally:
@@ -1188,28 +1205,57 @@ def load_bg_data() -> List[Dict[str, str]]:
     except json.JSONDecodeError:
         logger.error(f"Could not decode JSON from {BG_DATA_PATH}. Serving with empty data.")
         return []
-
+    
 def initialize_db():
 
-    if os.path.exists(DB_PATH):
-        logger.info("Base de datos ya existe, no se necesita inicializar.")
-        return
+    # En PostgreSQL, no se crea el archivo de base de datos, así que verificamos si las tablas existen.
+    try:
+        conn = psycopg2.connect(
+            dbname=POSTGRESQL_DB_CONFIG['dbname'],
+            user=POSTGRESQL_DB_CONFIG['user'],
+            password=POSTGRESQL_DB_CONFIG['password'],
+            host=POSTGRESQL_DB_CONFIG['host'],
+            port=POSTGRESQL_DB_CONFIG['port']
+        )
+        cursor = conn.cursor()
+        # Verificar si la tabla 'users' ya existe
+        cursor.execute("""
+            SELECT EXISTS (
+                SELECT FROM information_schema.tables 
+                WHERE table_name = 'users'
+            );
+        """)
+        exists = cursor.fetchone()
+        if exists:
+            logger.info("La base de datos ya está inicializada, no se necesita inicializar.")
+            conn.close()
+            return
+        conn.close()
+    except Exception as e:
+        logger.error(f"Error comprobando la existencia de tablas: {e}")
+        # Si hay error, continuamos con la inicialización
 
-    conn = sqlite3.connect(DB_PATH)
+    conn = psycopg2.connect(
+        dbname=POSTGRESQL_DB_CONFIG['dbname'],
+        user=POSTGRESQL_DB_CONFIG['user'],
+        password=POSTGRESQL_DB_CONFIG['password'],
+        host=POSTGRESQL_DB_CONFIG['host'],
+        port=POSTGRESQL_DB_CONFIG['port']
+    )
     cursor = conn.cursor()
     # Crear tabla de usuarios
     logger.info("Inicializando base de datos...")
     logger.info("Creando tabla de usuarios...")
     cursor.execute("""
     CREATE TABLE IF NOT EXISTS users (
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
-        email TEXT UNIQUE NOT NULL,
-        name TEXT NOT NULL,
-        rut TEXT UNIQUE NOT NULL,
+        id SERIAL PRIMARY KEY,
+        email VARCHAR(255) UNIQUE NOT NULL,
+        name VARCHAR(255) NOT NULL,
+        rut VARCHAR(20) UNIQUE NOT NULL,
         pin_hash TEXT NOT NULL,
         kleincoins TEXT NOT NULL,
         created_at TEXT NOT NULL,
-        permissions INTEGER DEFAULT 0 NOT NULL CHECK (permissions IN (0, 1, 2)), -- 0: Usuario normal, 1: Administrador, 2: Superusuario
+        permissions INTEGER DEFAULT 0 NOT NULL CHECK (permissions IN (0, 1, 2)) -- 0: Usuario normal, 1: Administrador, 2: Superusuario
     );
     """)
 
@@ -1218,10 +1264,10 @@ def initialize_db():
     cursor.execute("""
     CREATE TABLE IF NOT EXISTS products (
         id INTEGER PRIMARY KEY,
-        name TEXT NOT NULL,
-        type TEXT,
-        description TEXT,
-        price REAL NOT NULL,
+        name VARCHAR(255) NOT NULL,
+        type VARCHAR(100) NOT NULL,
+        description TEXT NOT NULL,
+        price INTEGER NOT NULL,
         image TEXT,
         status INTEGER DEFAULT 1 NOT NULL CHECK (status IN (0, 1)) -- 0: Inactivo, 1: Activo
     );
@@ -1231,12 +1277,12 @@ def initialize_db():
     logger.info("Creando tabla de sales...")
     cursor.execute("""
     CREATE TABLE IF NOT EXISTS sales (
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        id SERIAL PRIMARY KEY,
         user_id INTEGER NOT NULL,
-        total REAL NOT NULL,
-        fudo_id TEXT NOT NULL,
+        total INTEGER NOT NULL,
+        fudo_id TEXT UNIQUE NOT NULL,
         date TEXT NOT NULL,
-        "table" INTEGER NOT NULL,
+        table_number INTEGER NOT NULL,
         FOREIGN KEY (user_id) REFERENCES users(id)
     );
     """)
@@ -1257,7 +1303,7 @@ def initialize_db():
     logger.info("Creando tabla de blacklist...")
     cursor.execute("""
     CREATE TABLE IF NOT EXISTS blacklist (
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        id SERIAL PRIMARY KEY,
         user_id INTEGER NOT NULL,
         FOREIGN KEY (user_id) REFERENCES users(id)
     );

+ 41 - 0
services/print_service.py

@@ -0,0 +1,41 @@
+import requests
+from models.sales import OrderWeb, ItemWeb
+from services.data_service import DataServiceFactory
+from models.items import Order
+user_data_service = DataServiceFactory.get_user_service()
+
+
+def print_order(order:Order):
+    """Send order to printer"""
+    if not order.items or not order.table:
+        raise ValueError("Order must have items and a table number.")
+    # Prepare the order data for printing
+    order_data = {
+        "table": order.table,
+        "items": [{"name": item.name,"price": item.price, "quantity": item.quantity} for item in order.items],
+        "customerName": order.customerName,
+        "totalAmount": order.totalAmount,
+        "orderDate": order.orderDate
+    }
+    print("Order data prepared for printing:", order_data)
+
+    # Send the order data to the printer service
+    response = requests.post("http://localhost:5004/print", json=order_data)
+    
+    if response.status_code != 200:
+        raise Exception(f"Failed to print order: {response.text}")
+
+    return response.json()  # Return the response from the printer service
+
+def get_status():
+    """Get the status of the printer service"""
+    try:
+        response = requests.get("http://localhost:5004/status")
+        data = response.json()
+        print("Printer service status:", data)
+        if data.get("status"):
+            return True
+        else:
+            return False
+    except requests.RequestException as e:
+        raise Exception(f"Error connecting to printer service: {e}")