From 216abc8ad23ce4ab1d7f01c62db76e17bf8825b5 Mon Sep 17 00:00:00 2001 From: SekiDesu01 Date: Mon, 9 Mar 2026 13:02:00 -0300 Subject: [PATCH] Fixed receipt design --- app.py | 55 ++++++++++++++++++++++++++---------- templates/checkout.html | 11 ++++++-- templates/sales.html | 62 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 17 deletions(-) diff --git a/app.py b/app.py index cf1244c..72b83f5 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,7 @@ from werkzeug.security import generate_password_hash, check_password_hash import mimetypes import time import uuid +from datetime import datetime # from dotenv import load_dotenv # load_dotenv() @@ -347,31 +348,31 @@ def update_scale_weight(): @app.route('/api/checkout', methods=['POST']) @login_required def process_checkout(): - data = request.get_json() - cart = data.get('cart', []) - payment_method = data.get('payment_method', 'efectivo') - - if not cart: - return jsonify({"error": "Cart is empty"}), 400 - - # Recalculate total on the server because trusting the frontend is a security risk - total = sum(item.get('subtotal', 0) for item in cart) - try: + data = request.get_json() + cart = data.get('cart', []) + payment_method = data.get('payment_method', 'efectivo') + + if not cart: + return jsonify({"error": "Cart is empty"}), 400 + + # Recalculate total on the server because the frontend is a liar + total = sum(item.get('subtotal', 0) for item in cart) + with sqlite3.connect(DB_FILE) as conn: cur = conn.cursor() - # 1. Create the main sale record - cur.execute('INSERT INTO sales (total, payment_method) VALUES (?, ?)', (total, payment_method)) + # Let SQLite handle the exact UTC timestamp internally + cur.execute('INSERT INTO sales (date, total, payment_method) VALUES (CURRENT_TIMESTAMP, ?, ?)', (total, payment_method)) sale_id = cur.lastrowid - # 2. Record each item and deduct stock + # Record each item and deduct stock for item in cart: cur.execute('''INSERT INTO sale_items (sale_id, barcode, name, price, quantity, subtotal) VALUES (?, ?, ?, ?, ?, ?)''', (sale_id, item['barcode'], item['name'], item['price'], item['qty'], item['subtotal'])) - # Deduct from inventory + # Deduct from inventory (Manual products will safely be ignored here) cur.execute('UPDATE products SET stock = stock - ? WHERE barcode = ?', (item['qty'], item['barcode'])) conn.commit() @@ -417,6 +418,32 @@ def get_sale_details(sale_id): item_list = [{"barcode": i[0], "name": i[1], "price": i[2], "qty": i[3], "subtotal": i[4]} for i in items] return jsonify(item_list), 200 +@app.route('/api/sale/', methods=['DELETE']) +@login_required +def reverse_sale(sale_id): + try: + with sqlite3.connect(DB_FILE) as conn: + cur = conn.cursor() + + # 1. Get the items and quantities from the receipt + items = cur.execute('SELECT barcode, quantity FROM sale_items WHERE sale_id = ?', (sale_id,)).fetchall() + + # 2. Add the stock back to the inventory + for barcode, qty in items: + # This safely ignores manual products since their fake barcodes won't match any row + cur.execute('UPDATE products SET stock = stock + ? WHERE barcode = ?', (qty, barcode)) + + # 3. Destroy the evidence + cur.execute('DELETE FROM sale_items WHERE sale_id = ?', (sale_id,)) + cur.execute('DELETE FROM sales WHERE id = ?', (sale_id,)) + + conn.commit() + + return jsonify({"status": "success"}), 200 + + except Exception as e: + print(f"Reverse Sale Error: {e}") + return jsonify({"error": str(e)}), 500 # @app.route('/process_payment', methods=['POST']) # @login_required # def process_payment(): diff --git a/templates/checkout.html b/templates/checkout.html index d18cb26..48664e2 100644 --- a/templates/checkout.html +++ b/templates/checkout.html @@ -298,6 +298,7 @@

SekiPOS

Comprobante de Venta
+
Ticket Nº
@@ -448,6 +449,9 @@ Manual + @@ -608,9 +612,9 @@ // Hide the payment modal bootstrap.Modal.getInstance(document.getElementById('paymentModal')).hide(); - // Calculate total and print BEFORE wiping the cart + // Pass the new sale_id from the result to the printer const total = cart.reduce((sum, item) => sum + item.subtotal, 0); - printReceipt(total); + printReceipt(total, result.sale_id); // Show the success checkmark const successModal = bootstrap.Modal.getOrCreateInstance(document.getElementById('successModal')); @@ -861,7 +865,7 @@ modal.show(); } - function printReceipt(total) { + function printReceipt(total, saleId) { const tbody = document.getElementById('receipt-items-print'); tbody.innerHTML = ''; @@ -876,6 +880,7 @@ `; }); + document.getElementById('receipt-ticket-id').innerText = saleId || "N/A"; document.getElementById('receipt-total-print').innerText = clp.format(total); document.getElementById('receipt-date').innerText = new Date().toLocaleString('es-CL'); diff --git a/templates/sales.html b/templates/sales.html index 57ee265..40ec2a8 100644 --- a/templates/sales.html +++ b/templates/sales.html @@ -184,6 +184,34 @@
+ + + + + + + @@ -191,6 +219,7 @@