From 70457207a894a6a8bf69db269cd4f694d058aedf Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Mon, 15 Jun 2026 23:11:41 +0700 Subject: [PATCH] feat: enable logging of cancelled POS order lines to chatter for non-paid cancellations --- models/pos_order.py | 17 +++++++++++++++-- static/src/app/services/pos_store.js | 19 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/models/pos_order.py b/models/pos_order.py index 479150e..ed45063 100644 --- a/models/pos_order.py +++ b/models/pos_order.py @@ -60,11 +60,24 @@ class PosOrder(models.Model): else: employee_name = self.env.user.name + # Flush any pending x_cancelled_lines that were accumulated on the + # frontend but never sent via _process_order (i.e. the order was + # cancelled without payment). The JS passes these as a dict keyed by + # order ID: { "": [...cancelled_lines...] } + cancelled_lines_map = self.env.context.get('cancelled_lines_map', {}) + res = super().action_pos_order_cancel() - + for order in self: order.message_post(body=f"Order Cancelled by {employee_name}") - + + # Log any frontend-tracked line deletions/reductions that happened + # before this cancellation. Use str(order.id) as the JS context key + # since JSON object keys are always strings. + pending_lines = cancelled_lines_map.get(str(order.id)) or cancelled_lines_map.get(order.id) + if pending_lines: + order._log_cancelled_lines_to_chatter(pending_lines) + # Prevent Odoo backend warning regarding custom action properties if isinstance(res, dict) and 'type' not in res: res['type'] = '' diff --git a/static/src/app/services/pos_store.js b/static/src/app/services/pos_store.js index 633bcea..7502c76 100644 --- a/static/src/app/services/pos_store.js +++ b/static/src/app/services/pos_store.js @@ -37,13 +37,28 @@ patch(PosStore.prototype, { async deleteOrders(orders, serverIds = [], ignoreChange = false) { const cashier = this.getCashier(); const oldCall = this.data.call; - if (cashier) { + + // Build a map of { order.id → x_cancelled_lines } for all synced orders + // that are being deleted. This lets the server flush the cancellation log + // to the order chatter even when the order is cancelled without payment + // (i.e. _process_order is never called for cancelled orders). + const cancelledLinesMap = {}; + for (const order of orders) { + if (order && typeof order.id === "number" && order.x_cancelled_lines?.length) { + cancelledLinesMap[order.id] = order.x_cancelled_lines; + } + } + + if (cashier || Object.keys(cancelledLinesMap).length > 0) { this.data.call = async function(model, method, args, kwargs) { if (model === "pos.order" && method === "action_pos_order_cancel") { kwargs = kwargs || {}; kwargs.context = { ...(kwargs.context || {}), - cancelled_by_employee_id: cashier.id + ...(cashier ? { cancelled_by_employee_id: cashier.id } : {}), + ...(Object.keys(cancelledLinesMap).length > 0 + ? { cancelled_lines_map: cancelledLinesMap } + : {}), }; } return oldCall.apply(this, arguments);