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