diff --git a/README.md b/README.md index a884880..b7628fe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Odoo 19 custom module — automatically prints a **session closing summary** via - Fault-tolerant: print failures are caught and logged; they never block the session closing process. - **Reprint**: adds a **Reprint Closing Summary** button on the closed session form in the Odoo backend. ---- +\--- ## Receipt Layout diff --git a/__manifest__.py b/__manifest__.py index 61e02b6..01a8a05 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- { 'name': 'POS Closing Receipt Printer', - 'version': '19.0.1.3.0', + 'version': '19.0.1.3.1', 'category': 'Point of Sale', 'summary': 'Print payment summary receipt when closing a POS session, with reprint from backend', 'description': """ diff --git a/static/src/app/closing_receipt_patch.js b/static/src/app/closing_receipt_patch.js index 5b0bb71..ed64552 100644 --- a/static/src/app/closing_receipt_patch.js +++ b/static/src/app/closing_receipt_patch.js @@ -198,80 +198,163 @@ patch(ClosePosPopup.prototype, { // builds receipt data, and prints with webPrintFallback. // ───────────────────────────────────────────────────────────────────────────── patch(Navbar.prototype, { - setup() { - super.setup(...arguments); - this.printer = useService("printer"); - }, - async reprintLastClosingReceipt() { const formatCurrency = this.env.utils.formatCurrency; // ── 1. Fetch the last closed session for this config ───────────────── let sessionData; try { - const sessions = await this.pos.data.call( + const result = await this.pos.data.call( "pos.session", "get_last_closed_session_summary", [this.pos.config.id] ); - if (!sessions) { + if (!result) { this.notification.add(_t("No closed session found to reprint."), { type: "warning", }); return; } - sessionData = sessions; + sessionData = result; } catch (err) { console.error("[pos_closing_receipt] Could not fetch last session:", err); this.notification.add(_t("Failed to fetch session data."), { type: "danger" }); return; } - // ── 2. Build receipt props from server data ────────────────────────── - let cashPayment = null; - const nonCashPayments = []; + // ── 2. Build formatted payment rows ────────────────────────────────── + let cashRow = ""; + const nonCashRows = []; for (const pm of sessionData.payment_methods) { + const formatted = formatCurrency(pm.amount); + const row = ` + + ${pm.name} + ${formatted} + `; if (pm.is_cash) { - cashPayment = { - id: pm.id, - name: pm.name, - amount: pm.amount, - formattedAmount: formatCurrency(pm.amount), - }; + cashRow = row; } else { - nonCashPayments.push({ - id: pm.id, - name: pm.name, - amount: pm.amount, - formattedAmount: formatCurrency(pm.amount), - }); + nonCashRows.push(row); } } - const cashAmount = cashPayment ? cashPayment.amount : 0; - const nonCashTotal = nonCashPayments.reduce((s, pm) => s + pm.amount, 0); + const cashAmount = (sessionData.payment_methods.find((p) => p.is_cash) || {}).amount || 0; + const nonCashTotal = sessionData.payment_methods + .filter((p) => !p.is_cash) + .reduce((s, p) => s + p.amount, 0); const grandTotal = formatCurrency(cashAmount + nonCashTotal); - const receiptData = { - sessionName: sessionData.session_name, - cashierName: sessionData.cashier_name, - closingTime: sessionData.closing_time + " (REPRINT)", - cashPayment, - nonCashPayments, - grandTotal, - }; - - // ── 3. Print ───────────────────────────────────────────────────────── - try { - await this.printer.print(ClosingReceipt, receiptData, { - webPrintFallback: true, - }); - } catch (err) { - console.warn("[pos_closing_receipt] Reprint failed:", err); - this.notification.add(_t("Print failed. Check browser print settings."), { - type: "warning", - }); + // ── 3. Build standalone HTML receipt and open print window ─────────── + const html = ` + + + + Closing Summary Reprint + + + +
+
+
${sessionData.session_name}
+
SESSION CLOSING SUMMARY
+
(REPRINT)
+
+
+ + + + + + + + + +
Cashier${sessionData.cashier_name}
Date/Time${sessionData.closing_time}
+
+ + ${cashRow} + ${nonCashRows.join("")} +
+
+ + + + + +
TOTAL${grandTotal}
+
+ +
 
+
+ + +`; + + const printWindow = window.open("", "_blank", "width=400,height=600"); + if (!printWindow) { + this.notification.add( + _t("Pop-up blocked. Please allow pop-ups for this site and try again."), + { type: "warning" } + ); + return; + } + printWindow.document.open(); + printWindow.document.write(html); + printWindow.document.close(); }, });