From cd9ed0a22ea2a6ebadceae0541896b0c713af431 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Thu, 28 May 2026 09:17:52 +0700 Subject: [PATCH] feat: implement automatic incremental Table Checker printing upon order submission --- README.md | 3 + .../order_display/order_display_patch.js | 13 +++ static/src/app/services/pos_store.js | 79 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 static/src/app/components/order_display/order_display_patch.js diff --git a/README.md b/README.md index 932edd4..0d0c014 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This Odoo 19 module enhances the security and auditability of the Point of Sale - Hides the save order for later (upload icon) button from the POS UI. - Hides the "Set Table" button from Register/Direct Sale orders. - Renames the "Basic Receipt" button to "Table Checker" (on both the action pad and print success screen) for clearer pre-payment table verification. +- **Automatic Table Checker on Send**: Automatically prints a "Table Checker" (Basic Receipt) whenever the employee clicks the kitchen "Send" button to submit changes. +- **Incremental Printing on Send**: When additional items are added to an already sent order, the automatic print on "Send" is filtered to print *only the newly added (additional) items*, preventing duplicate printing of already served items. + ## Dependencies diff --git a/static/src/app/components/order_display/order_display_patch.js b/static/src/app/components/order_display/order_display_patch.js new file mode 100644 index 0000000..e2d1cb8 --- /dev/null +++ b/static/src/app/components/order_display/order_display_patch.js @@ -0,0 +1,13 @@ +/** @odoo-module **/ + +import { OrderDisplay } from "@point_of_sale/app/components/order_display/order_display"; +import { patch } from "@web/core/utils/patch"; + +patch(OrderDisplay.prototype, { + get comboSortedLines() { + if (this.order?.print_changes_only && this.order?.changed_lines_to_print) { + return this.order.changed_lines_to_print; + } + return super.comboSortedLines; + } +}); diff --git a/static/src/app/services/pos_store.js b/static/src/app/services/pos_store.js index 1c2964d..14b4010 100644 --- a/static/src/app/services/pos_store.js +++ b/static/src/app/services/pos_store.js @@ -98,6 +98,85 @@ patch(PosStore.prototype, { return res; }, + async submitOrder() { + const order = this.getOrder(); + let orderProxy = null; + if (order && this.config.basic_receipt) { + const oldLines = order.last_order_preparation_change?.lines || {}; + const changedLines = []; + + for (const orderline of order.getOrderlines()) { + const key = Object.keys(oldLines).find(k => k.startsWith(orderline.uuid)); + const oldQty = key ? oldLines[key].quantity : 0; + const currentQty = orderline.getQuantity(); + const diffQty = currentQty - oldQty; + + if (diffQty > 0) { + const lineProxy = new Proxy(orderline, { + get(target, prop, receiver) { + if (prop === 'qty') { + return diffQty; + } + if (prop === 'getQuantity') { + return function() { + return diffQty; + }; + } + if (prop === 'getQuantityStr') { + return function() { + return lineProxy.quantityStr; + }; + } + if (prop === 'quantityStr') { + const qty = diffQty; + const unitPart = qty % 1 === 0 ? qty.toFixed(0) : qty.toFixed(3).replace(/\.?0+$/, ''); + return { + qtyStr: unitPart, + unitPart: unitPart, + decimalPoint: '.', + decimalPart: '', + }; + } + let value = Reflect.get(target, prop, receiver); + if (typeof value === 'function') { + return value.bind(receiver); + } + return value; + } + }); + changedLines.push(lineProxy); + } + } + + if (changedLines.length > 0) { + orderProxy = new Proxy(order, { + get(target, prop, receiver) { + if (prop === 'print_changes_only') { + return true; + } + if (prop === 'changed_lines_to_print') { + return changedLines; + } + let value = Reflect.get(target, prop, receiver); + if (typeof value === 'function') { + return value.bind(receiver); + } + return value; + } + }); + } + } + + const res = await super.submitOrder(...arguments); + + if (order && this.config.basic_receipt) { + if (orderProxy) { + this.printReceipt({ order: orderProxy, basic: true, printBillActionTriggered: true }); + } + } + return res; + }, + async _selectCashierByPin() {