From 57bf68816917890ab25ff4720899ead680d51feb Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 31 Aug 2026 17:43:38 +0200 Subject: [PATCH] [REF] pos_payment_method_cashdro_rounding: hook the amount, not the url builder `_cashdro_payment_url()` is shared by the payment and the reversal paths, so patching it applied the rounding to the reversals too: giving back a paid order sent the drawer the *order* remaining, rounded, instead of the amount of the line, and reversing an order already covered by other payments sent 0. It also collided with the modules that fix that same method, leaving the outcome to the asset load order. Both go away by hooking `_cashdro_amount()`, the extension point `pos_payment_method_cashdro_flow_fix` provides, which is only reached on a payment. `shouldRound`/`getDefaultAmountDueToPayIn` came with the 18.0 tax totals rework, so fall back instead of breaking the payment with a TypeError the operator would read as one more connection error. Adds the README_DEV.md the repo conventions ask for. Co-Authored-By: Claude Opus 5 --- .../README_DEV.md | 52 +++++++++++++++++++ .../__manifest__.py | 7 +-- .../static/src/js/cashdro_rounding.esm.js | 30 +++++++---- 3 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 pos_payment_method_cashdro_rounding/README_DEV.md diff --git a/pos_payment_method_cashdro_rounding/README_DEV.md b/pos_payment_method_cashdro_rounding/README_DEV.md new file mode 100644 index 0000000..0c2e5f5 --- /dev/null +++ b/pos_payment_method_cashdro_rounding/README_DEV.md @@ -0,0 +1,52 @@ +# pos_payment_method_cashdro_rounding + +Sends the CashDro the amount the customer is really expected to pay — the due +amount with the PoS **cash rounding** applied — instead of the plain due amount +the OCA module asks for. + +## Why + +A CashDro can only give change with the coins it holds. When the PoS is +configured with a cash rounding (`pos.config.cash_rounding`, typically to 0.05 +so that 1 and 2 cent coins are not needed), asking the drawer for the unrounded +due leaves it trying to return change it does not have. + +Core already computes that amount: `PosOrder.getDefaultAmountDueToPayIn()` is +what a payment line defaults to when it is created. We simply reuse it, so the +drawer is asked for exactly what the payment screen shows. + +`shouldRound()` decides whether the rounding applies at all — it takes +`only_round_cash_method` into account, and the CashDro method is a cash one, so +in practice it is always true when a cash rounding is configured. + +## How + +One single patch on `PaymentCashdro.prototype._cashdro_amount()`, the hook +`pos_payment_method_cashdro_flow_fix` extracted for this. Everything else of the +OCA payment flow (request, acknowledge, polling, error handling) is untouched. + +Two things this deliberately does **not** do: + +- It does not touch the **reversals**. There the amount to send is the one of + the line being given back, not the remaining of the order, and it is already + the rounded figure that was collected. Rounding it a second time against the + order total would ask the drawer to collect the total all over again. +- It does not touch `_cashdro_payment_url()`, which is what it patched until + `18.0.1.1.0`. That method is shared by the payment and the reversal paths, + which is exactly how the reversal bug above got in, and it collides with the + fix modules that also patch it — the outcome then depending on the asset load + order. + +## Dependencies + +Needs `pos_payment_method_cashdro_flow_fix` for the `_cashdro_amount()` hook, +and through it `pos_payment_method_cashdro_fix`: without them the OCA module +does not work on 18.0 at all, so there is nothing to round. + +## Testing + +There are no automated tests — the flow needs a CashDro answering on the +network. To check it by hand, configure a cash rounding of 0.05 on the PoS, take +an order to a total that is not a multiple of 0.05 (e.g. 17.43) and confirm the +drawer asks for 17.45. See the `USAGE` of +`pos_payment_method_cashdro_flow_fix` for how to read a failure. diff --git a/pos_payment_method_cashdro_rounding/__manifest__.py b/pos_payment_method_cashdro_rounding/__manifest__.py index a16b1e3..1ac11c0 100644 --- a/pos_payment_method_cashdro_rounding/__manifest__.py +++ b/pos_payment_method_cashdro_rounding/__manifest__.py @@ -1,18 +1,19 @@ +# Copyright Criptomart +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "POS CashDro Rounding", - "version": "18.0.1.0.0", + "version": "18.0.1.1.0", "summary": "Apply cash/currency rounding to CashDro payment request amount.", "category": "Point of Sale", "license": "AGPL-3", "author": "Criptomart", "depends": [ - "point_of_sale", "pos_payment_method_cashdro", + "pos_payment_method_cashdro_flow_fix", ], "assets": { "point_of_sale._assets_pos": [ "pos_payment_method_cashdro_rounding/static/src/js/cashdro_rounding.esm.js", ], }, - "installable": True, } diff --git a/pos_payment_method_cashdro_rounding/static/src/js/cashdro_rounding.esm.js b/pos_payment_method_cashdro_rounding/static/src/js/cashdro_rounding.esm.js index eb01842..d369ebc 100644 --- a/pos_payment_method_cashdro_rounding/static/src/js/cashdro_rounding.esm.js +++ b/pos_payment_method_cashdro_rounding/static/src/js/cashdro_rounding.esm.js @@ -17,21 +17,29 @@ patch(PaymentCashdro.prototype, { * really expected to pay: the due amount with the cash rounding applied, * which is the very same amount core defaults a payment line to. * - * Only the url builder is patched so the rest of the payment flow of the - * base module (request, acknowledge, polling and error handling) is kept. + * The hook belongs to `pos_payment_method_cashdro_flow_fix`, where the + * amount is computed. This module used to patch `_cashdro_payment_url` + * instead, which is shared with the reversals: there the amount to send is + * the one of the line being given back, not the remaining of the order, so + * reversing a paid order asked the drawer to collect the rounded total all + * over again. */ - _cashdro_payment_url(parameters) { - const order = this.pos.get_order(); - const line = order.get_selected_paymentline(); - if (!line || !order.shouldRound(line.payment_method_id)) { - return super._cashdro_payment_url(...arguments); + _cashdro_amount(line, reversal) { + const order = line.pos_order_id; + // `shouldRound` and `getDefaultAmountDueToPayIn` came with the 18.0 tax + // totals rework. On a build that predates them, fall back instead of + // breaking the payment with a TypeError the operator would read as one + // more connection error. + const rounds = + !reversal && + typeof order.getDefaultAmountDueToPayIn === "function" && + order.shouldRound?.(line.payment_method_id); + if (!rounds) { + return super._cashdro_amount(...arguments); } // CashDro treats decimals as positions in an integer, so we round the // product to avoid the trailing decimals of the floating point // computation or the drawer would reject our request. - const amount = Math.round( - order.getDefaultAmountDueToPayIn(line.payment_method_id) * 100 - ); - return super._cashdro_payment_url({ ...parameters, amount }); + return Math.round(order.getDefaultAmountDueToPayIn(line.payment_method_id) * 100); }, });