addons-cm/pos_payment_method_cashdro_flow_fix/readme/DESCRIPTION.rst
GitHub Copilot 7ae10abda8
[ADD] pos_payment_method_cashdro_flow_fix: fix the CashDro payment flow on 18.0
`_cashdro_payment_url()` builds the startOperation url with
`this.pos.pos_session.name`. 18.0 replaced that accessor by the `session`
getter of PosStore, so reading `.name` on the resulting `undefined` raises a
TypeError on every single payment, before any request leaves the browser.
`cashdro_send_payment_request` catches it with a bare `catch` that neither
logs nor rethrows and shows the generic "error connecting to the cashdro",
which is why the failure looked like a problem with the machine.

`pos_payment_method_cashdro_fix` only covers the data loading, so this goes
in a module of its own: it has a longer life than that temporary patch, and
it supersedes its `_cashdro_url()` override.

Along with the blocking one, the flow gets:

* the real error logged and shown in the dialog, so a wrong host, a busy
  drawer and a dead network stop being the same sentence,
* a missing host raised instead of the boolean `false` travelling into
  `fetch`, where it resolves against the Odoo server,
* `finishOperation` always sent on failure, or the drawer keeps the operation
  open and refuses every later payment of the session,
* polling on a 700ms interval with a 5 minute deadline, stopped by a cancel
  or by leaving the payment screen, instead of a tight unbounded loop,
* `send_payment_cancel()` returning whether the drawer took it, so the line
  no longer sticks in `waitingCard`,
* reversals sending the amount of the line negated: `get_due()` only
  discounts the payments already done, so the base module asked the drawer to
  collect the money a second time instead of dispensing it,
* the `uuid` of the line honoured instead of the selected one,
* the CashDro answers validated (HTTP status, JSON body, operation id).

`_cashdro_amount()` is split out as the single hook for the amount, which
`pos_payment_method_cashdro_rounding` uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 17:43:25 +02:00

54 lines
3.4 KiB
ReStructuredText

``pos_payment_method_cashdro_fix`` patches the three 17.0 leftovers that the
18.0 migration of ``pos_payment_method_cashdro`` left in the *data loading*.
This module takes care of the *payment flow* itself, which stays broken after
them.
**The blocking one.** ``_cashdro_payment_url()`` builds the ``startOperation``
url with ``this.pos.pos_session.name``. 18.0 replaced that accessor by the
``session`` getter of ``PosStore``, so ``this.pos.pos_session`` is ``undefined``
and reading ``.name`` on it raises a ``TypeError`` **on every single payment,
before any request leaves the browser**.
``cashdro_send_payment_request`` catches it with a bare ``catch`` that neither
logs nor rethrows, and shows *"An error occurred while connecting to the
cashdro"* — the same message a network failure or an unplugged drawer produce.
That is why the failure looks like a communication problem with the machine,
and why it happens with any other module of ours enabled or disabled.
**The rest.** Once the payment goes through, the flow still has these problems,
all of them fixed here:
#. **Failures are invisible.** The ``catch`` has no error binding, so nothing
reaches the browser console. Every cause — a wrong host, a busy drawer, a
bad password, an API mistake — reads as the same sentence. The error is now
logged and its message shown in the dialog.
#. **A missing host is not reported.** ``_cashdro_url()`` returns the boolean
``false``, which the callers concatenate into ``"false&operation=..."``;
``fetch`` resolves that against the Odoo server, gets a 404 page back and the
operator sees a connection error. It now raises with the method name.
#. **A started operation is never closed on failure.** If the acknowledge or the
polling fails after ``startOperation`` succeeded, the CashDro keeps the
operation open and refuses the next one: every later payment of the session
fails until the drawer is power cycled. ``finishOperation`` is now always
sent.
#. **The polling has no interval, no bound and no way to stop.** The base
implementation re-asks the drawer as fast as the network allows for as long
as the customer is inserting coins, and keeps going after a cancel. It is now
a 700 ms interval with a 5 minute deadline, stopped by a cancel or by leaving
the payment screen.
#. **A cancel is reported as failed.** ``send_payment_cancel()`` resolves to
``undefined``, which ``payment_screen`` reads as "the cancel failed" and
leaves the line stuck in ``waitingCard``, re-entering the cancel on every
click. It now returns whether the drawer took it.
#. **A reversal asks the drawer to collect the money again.**
``order.get_due()`` only discounts the payments that are already done, and
the line being reversed is not one of them, so the amount comes out positive
and the operation type is 4 (payment) instead of 3 (refund). The reversal
now uses the amount of the line, negated.
#. **The uuid of the line is ignored.** The three ``PaymentInterface`` entry
points receive the uuid of the line to act on and the base module works on
the selected one instead. The per-line *Send* and *Reverse* buttons do not
select the line first, so with two payment lines on the order the CashDro
works on the wrong one.
#. **The answers are not validated.** An HTTP error page or an envelope without
``data`` are taken as valid: the module acknowledges an empty operation id
and polls it forever. Both are now checked.