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
+
+
+
+
+
+
+
+
+ | 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();
},
});