[ADD] pos_payment_method_cashdro_fix: 17.0 API leftovers in the OCA base module

The 18.0 migration of `pos_payment_method_cashdro` (OCA/pos@6a4c251d) kept
three uses of APIs removed in 18.0. The module has no tests nor tours and needs
a physical drawer to be exercised, so the OCA CI can't catch them:

* `_cashdro_url()` reads `order.selected_paymentline`, so `.payment_method` on
  the resulting `undefined` throws a TypeError on every payment, swallowed into
  a generic "An error occurred while connecting to the cashdro" dialog.
* The `add_paymentline()` patch checks `line.payment_method`, renamed to
  `payment_method_id`, so the line keeps the default due amount instead of
  waiting for the amount the customer inserts.
* `_loader_params_pos_payment_method()` is the 17.0 loading API, replaced by
  `_load_pos_data_fields()`. Dead code, so the CashDro credentials never reach
  the front end.

Patched from the outside, as OCA sources are not to be modified. Written to
stay harmless once fixed upstream: the Python override only adds the missing
fields and the JS patches are idempotent against the fixed code.

`upstream/pos_payment_method_cashdro-18.0-fixes.patch` holds the same fixes as
a ready to send `git format-patch`, verified with `git apply --check`. This
module is temporary and should be removed once that lands in OCA/pos.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
GitHub Copilot 2026-08-11 13:31:11 +02:00
parent fb5b3d1f7e
commit 4615da6dae
14 changed files with 426 additions and 0 deletions

View file

@ -0,0 +1,86 @@
======================
POS CashDro 18.0 Fixes
======================
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
|badge1| |badge2|
The 18.0 migration of ``pos_payment_method_cashdro``
(`OCA/pos@6a4c251d <https://github.com/OCA/pos/commit/6a4c251d>`_) left three
uses of the 17.0 API that stop the CashDro integration from working. The module
has no tests nor tours and needs a physical drawer to be exercised, so nothing
in the OCA CI catches them.
This module patches the three of them from the outside, without touching the
OCA sources:
#. ``_cashdro_url()`` reads ``order.selected_paymentline``, replaced in 18.0 by
``order.get_selected_paymentline()``. Reading ``.payment_method`` on the
resulting ``undefined`` raises a ``TypeError`` on every payment, which the
caller swallows into a generic *"An error occurred while connecting to the
cashdro"* message.
#. The ``add_paymentline`` patch checks ``line.payment_method``, renamed to
``line.payment_method_id`` in 18.0. The condition is never true, so the
payment line keeps the default due amount instead of waiting for the amount
the customer inserts in the drawer.
#. ``pos.session._loader_params_pos_payment_method`` is the 17.0 loading API,
replaced in 18.0 by ``_load_pos_data_fields`` on each model. The override is
dead code, so ``cashdro_host``, ``cashdro_user`` and ``cashdro_password``
never reach the front end.
**Table of contents**
.. contents::
:local:
Usage
=====
There is nothing to configure: installing the module is enough.
To check the three fixes on a PoS with a CashDro terminal:
#. Open a PoS session, add a product and go to the payment screen.
#. Select the CashDro payment method. The payment line must show **0**, not the
due amount (fix 2).
#. The drawer must start the operation and ask for the amount, instead of the
*"An error occurred while connecting to the cashdro"* dialog (fixes 1 and 3).
#. With no CashDro at hand, the browser console tells fix 1 from fix 3 apart:
without this module the failure is a ``TypeError: Cannot read properties of
undefined (reading 'payment_method')``; with it, a network error against the
configured host.
The Python side is covered by the tests. The base module lives outside this
repo, so the OCA sources have to be on the addons path (see
``docker-compose.override.yml``, which is local and untracked)::
docker-compose run --rm odoo odoo -c /etc/odoo/odoo.conf \
--addons-path=/mnt/extra-addons,/mnt/oca/pos,/usr/lib/python3/dist-packages/odoo/addons \
-d odoo -u pos_payment_method_cashdro_fix --test-enable --stop-after-init
Known issues / Roadmap
======================
This module is meant to be temporary. The fixes are proposed upstream in
``upstream/pos_payment_method_cashdro-18.0-fixes.patch``; once they are merged
in ``OCA/pos`` this module can be uninstalled and removed.
It is written to stay harmless in the meantime: the Python override only adds
the credential fields that are missing from the loader, and the JavaScript
patches are idempotent with respect to the fixed upstream code.
The JavaScript side can't be covered by automated tests, as it needs a CashDro
terminal answering on the network.
Contributors
============
* `Criptomart <https://criptomart.net>`_:
* Criptomart

View file

@ -0,0 +1,3 @@
# Copyright 2026 - Today Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

View file

@ -0,0 +1,23 @@
# Copyright 2026 - Today Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "POS CashDro 18.0 Fixes",
"version": "18.0.1.0.0",
"summary": "Fix the 17.0 API leftovers that break pos_payment_method_cashdro on 18.0",
"category": "Point of Sale",
"license": "AGPL-3",
"author": "Criptomart",
"maintainers": ["Criptomart"],
"website": "https://git.criptomart.net/criptomart/addons-cm",
"depends": [
"point_of_sale",
"pos_payment_method_cashdro",
],
"assets": {
"point_of_sale._assets_pos": [
"pos_payment_method_cashdro_fix/static/src/js/payment_cashdro_fix.esm.js",
"pos_payment_method_cashdro_fix/static/src/js/models_fix.esm.js",
],
},
"installable": True,
}

View file

@ -0,0 +1 @@
from . import pos_payment_method

View file

@ -0,0 +1,26 @@
# Copyright 2026 - Today Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api
from odoo import models
CASHDRO_FIELDS = ["cashdro_host", "cashdro_user", "cashdro_password"]
class PosPaymentMethod(models.Model):
_inherit = "pos.payment.method"
@api.model
def _load_pos_data_fields(self, config_id):
"""Send the CashDro credentials to the PoS front end.
`pos_payment_method_cashdro` still declares them through
`_loader_params_pos_payment_method`, the 17.0 loading API, which 18.0
replaced by this method. That override is dead code, so the front end
never receives the host and can't build the CashDro url.
Only the missing fields are added, so this module stays harmless once
the base one is fixed upstream.
"""
fields = super()._load_pos_data_fields(config_id)
fields.extend(field for field in CASHDRO_FIELDS if field not in fields)
return fields

View file

@ -0,0 +1,3 @@
* `Criptomart <https://criptomart.net>`_:
* Criptomart

View file

@ -0,0 +1,22 @@
The 18.0 migration of ``pos_payment_method_cashdro``
(`OCA/pos@6a4c251d <https://github.com/OCA/pos/commit/6a4c251d>`_) left three
uses of the 17.0 API that stop the CashDro integration from working. The module
has no tests nor tours and needs a physical drawer to be exercised, so nothing
in the OCA CI catches them.
This module patches the three of them from the outside, without touching the
OCA sources:
#. ``_cashdro_url()`` reads ``order.selected_paymentline``, replaced in 18.0 by
``order.get_selected_paymentline()``. Reading ``.payment_method`` on the
resulting ``undefined`` raises a ``TypeError`` on every payment, which the
caller swallows into a generic *"An error occurred while connecting to the
cashdro"* message.
#. The ``add_paymentline`` patch checks ``line.payment_method``, renamed to
``line.payment_method_id`` in 18.0. The condition is never true, so the
payment line keeps the default due amount instead of waiting for the amount
the customer inserts in the drawer.
#. ``pos.session._loader_params_pos_payment_method`` is the 17.0 loading API,
replaced in 18.0 by ``_load_pos_data_fields`` on each model. The override is
dead code, so ``cashdro_host``, ``cashdro_user`` and ``cashdro_password``
never reach the front end.

View file

@ -0,0 +1,10 @@
This module is meant to be temporary. The fixes are proposed upstream in
``upstream/pos_payment_method_cashdro-18.0-fixes.patch``; once they are merged
in ``OCA/pos`` this module can be uninstalled and removed.
It is written to stay harmless in the meantime: the Python override only adds
the credential fields that are missing from the loader, and the JavaScript
patches are idempotent with respect to the fixed upstream code.
The JavaScript side can't be covered by automated tests, as it needs a CashDro
terminal answering on the network. See ``USAGE`` for the manual check.

View file

@ -0,0 +1,21 @@
There is nothing to configure: installing the module is enough.
To check the three fixes on a PoS with a CashDro terminal:
#. Open a PoS session, add a product and go to the payment screen.
#. Select the CashDro payment method. The payment line must show **0**, not the
due amount (fix 2).
#. The drawer must start the operation and ask for the amount, instead of the
*"An error occurred while connecting to the cashdro"* dialog (fixes 1 and 3).
#. With no CashDro at hand, the browser console tells fix 1 from fix 3 apart:
without this module the failure is a ``TypeError: Cannot read properties of
undefined (reading 'payment_method')``; with it, a network error against the
configured host.
The Python side is covered by the tests. The base module lives outside this
repo, so the OCA sources have to be on the addons path (see
``docker-compose.override.yml``, which is local and untracked)::
docker-compose run --rm odoo odoo -c /etc/odoo/odoo.conf \
--addons-path=/mnt/extra-addons,/mnt/oca/pos,/usr/lib/python3/dist-packages/odoo/addons \
-d odoo -u pos_payment_method_cashdro_fix --test-enable --stop-after-init

View file

@ -0,0 +1,31 @@
/* Copyright 2026 - Today Criptomart
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
The `add_paymentline` patch of `pos_payment_method_cashdro` checks
`line.payment_method`, renamed to `payment_method_id` in 18.0, so the
condition is never met and the line keeps the default due amount instead of
waiting for the amount the customer actually inserts in the drawer.
*/
import { PosOrder } from "@point_of_sale/app/models/pos_order";
import { patch } from "@web/core/utils/patch";
patch(PosOrder.prototype, {
/**
* @override
* Set the amount to 0 as it's going to be filled by the Cashdro response.
*/
add_paymentline() {
const line = super.add_paymentline(...arguments);
if (!line) {
return line;
}
if (
line.payment_method_id?.use_payment_terminal === "cashdro" &&
line.amount > 0 // For refund transactions we need to keep the amount
) {
line.set_amount(0);
}
return line;
},
});

View file

@ -0,0 +1,33 @@
/* Copyright 2026 - Today Criptomart
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
`_cashdro_url` was left with the 17.0 API in the 18.0 migration of
`pos_payment_method_cashdro`: `order.selected_paymentline` no longer exists,
so reading `.payment_method` on it throws a TypeError that the caller
swallows as a generic connection error. Rewritten with the 18.0 accessors.
*/
import { PaymentCashdro } from "@pos_payment_method_cashdro/js/payment_cashdro.esm";
import { patch } from "@web/core/utils/patch";
patch(PaymentCashdro.prototype, {
/**
* @override
* The whole body has to be replaced: the very first statement is the one
* that breaks, so there is nothing left to delegate to `super`.
*/
_cashdro_url() {
// Cashdro machines don't support safe POST calls, so we're sending
// all the data quite unsafely constantly...
const order = this.pos.get_order();
const method = order.get_selected_paymentline()?.payment_method_id;
const host = method && method.cashdro_host;
if (!host) {
return false;
}
let url = `${host}/Cashdro3WS/index.php`;
url += `?name=${method.cashdro_user}`;
url += `&password=${method.cashdro_password}`;
return url;
},
});

View file

@ -0,0 +1 @@
from . import test_load_pos_data_fields

View file

@ -0,0 +1,33 @@
# Copyright 2026 - Today Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
from ..models.pos_payment_method import CASHDRO_FIELDS
@tagged("post_install", "-at_install")
class TestLoadPosDataFields(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.payment_method = cls.env["pos.payment.method"]
# Nothing dereferences it, but we pass a real config when there's one
cls.config_id = cls.env["pos.config"].search([], limit=1).id
def test_cashdro_credentials_are_loaded(self):
"""The front end can't build the CashDro url without them."""
fields = self.payment_method._load_pos_data_fields(self.config_id)
for field in CASHDRO_FIELDS:
self.assertIn(field, fields)
def test_no_duplicated_fields(self):
"""Once the base module is fixed upstream we must not add them twice."""
fields = self.payment_method._load_pos_data_fields(self.config_id)
self.assertEqual(sorted(fields), sorted(set(fields)))
def test_fields_exist_on_the_model(self):
"""Guard against a rename of the credentials in the base module."""
for field in CASHDRO_FIELDS:
self.assertIn(field, self.payment_method._fields)

View file

@ -0,0 +1,133 @@
From 11c65e8000282b9a761346f1fbff16e09581fc17 Mon Sep 17 00:00:00 2001
From: Criptomart <ofi@criptomart.net>
Date: Tue, 11 Aug 2026 13:21:28 +0200
Subject: [PATCH] [FIX] pos_payment_method_cashdro: leftovers of the 17.0 API
The 18.0 migration kept three uses of APIs that no longer exist, and the
module has neither tests nor tours (it needs a physical drawer), so the CI
can't catch them.
* `_cashdro_url()` reads `order.selected_paymentline`, replaced by
`order.get_selected_paymentline()` in 18.0. Reading `.payment_method` on
the resulting `undefined` throws a TypeError on every payment, which
`cashdro_send_payment_request()` swallows into a generic "An error occurred
while connecting to the cashdro" dialog.
* The `add_paymentline()` patch checks `line.payment_method`, renamed to
`line.payment_method_id` in 18.0, so the payment line is never zeroed and
keeps the default due amount instead of the amount the customer inserts.
* `_loader_params_pos_payment_method()` is the 17.0 loading API, replaced in
18.0 by `_load_pos_data_fields()` on each model. The override is dead code
and the CashDro credentials never reach the front end, so `_cashdro_url()`
has no host to build the url with.
---
pos_payment_method_cashdro/__manifest__.py | 2 +-
pos_payment_method_cashdro/models/__init__.py | 1 -
.../models/pos_payment_method.py | 11 ++++++++++-
pos_payment_method_cashdro/models/pos_session.py | 14 --------------
.../static/src/js/models.esm.js | 4 ++--
.../static/src/js/payment_cashdro.esm.js | 3 ++-
6 files changed, 15 insertions(+), 20 deletions(-)
delete mode 100644 pos_payment_method_cashdro/models/pos_session.py
diff --git a/pos_payment_method_cashdro/__manifest__.py b/pos_payment_method_cashdro/__manifest__.py
index 708aac5..c19a642 100644
--- a/pos_payment_method_cashdro/__manifest__.py
+++ b/pos_payment_method_cashdro/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "PoS Payment Method CashDro",
"summary": "Allows to pay with CashDro Terminals on the Point of Sale",
- "version": "18.0.1.0.2",
+ "version": "18.0.1.0.3",
"category": "Point Of Sale",
"website": "https://github.com/OCA/pos",
"author": "Tecnativa, Odoo Community Association (OCA)",
diff --git a/pos_payment_method_cashdro/models/__init__.py b/pos_payment_method_cashdro/models/__init__.py
index 604d90c..58690ef 100644
--- a/pos_payment_method_cashdro/models/__init__.py
+++ b/pos_payment_method_cashdro/models/__init__.py
@@ -1,2 +1 @@
from . import pos_payment_method
-from . import pos_session
diff --git a/pos_payment_method_cashdro/models/pos_payment_method.py b/pos_payment_method_cashdro/models/pos_payment_method.py
index 285984a..08a64a1 100644
--- a/pos_payment_method_cashdro/models/pos_payment_method.py
+++ b/pos_payment_method_cashdro/models/pos_payment_method.py
@@ -1,6 +1,6 @@
# Copyright 2021 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo import fields, models
+from odoo import api, fields, models
class PosPaymentMethod(models.Model):
@@ -16,6 +16,15 @@ class PosPaymentMethod(models.Model):
cashdro_user = fields.Char()
cashdro_password = fields.Char()
+ @api.model
+ def _load_pos_data_fields(self, config_id):
+ """The front end needs the credentials to build the CashDro url"""
+ return super()._load_pos_data_fields(config_id) + [
+ "cashdro_host",
+ "cashdro_user",
+ "cashdro_password",
+ ]
+
def _onchange_journal_id(self):
"""Cash payment method force the `use_payment_terminal` to `False` as
it's assumed that a cash journal can't have a payment terminal. Let's keep
diff --git a/pos_payment_method_cashdro/models/pos_session.py b/pos_payment_method_cashdro/models/pos_session.py
deleted file mode 100644
index 6123090..0000000
--- a/pos_payment_method_cashdro/models/pos_session.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2024 Tecnativa - David Vidal
-# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo import models
-
-
-class PosSession(models.Model):
- _inherit = "pos.session"
-
- def _loader_params_pos_payment_method(self):
- result = super()._loader_params_pos_payment_method()
- result["search_params"]["fields"].extend(
- ["cashdro_host", "cashdro_user", "cashdro_password"]
- )
- return result
diff --git a/pos_payment_method_cashdro/static/src/js/models.esm.js b/pos_payment_method_cashdro/static/src/js/models.esm.js
index d228f08..d1ee2f7 100644
--- a/pos_payment_method_cashdro/static/src/js/models.esm.js
+++ b/pos_payment_method_cashdro/static/src/js/models.esm.js
@@ -22,8 +22,8 @@ patch(PosOrder.prototype, {
return line;
}
if (
- line.payment_method &&
- line.payment_method.use_payment_terminal === "cashdro" &&
+ line.payment_method_id &&
+ line.payment_method_id.use_payment_terminal === "cashdro" &&
line.amount > 0 // For refund transactions we need to keep the amount
) {
line.set_amount(0);
diff --git a/pos_payment_method_cashdro/static/src/js/payment_cashdro.esm.js b/pos_payment_method_cashdro/static/src/js/payment_cashdro.esm.js
index 0c0bdf2..2c618bf 100644
--- a/pos_payment_method_cashdro/static/src/js/payment_cashdro.esm.js
+++ b/pos_payment_method_cashdro/static/src/js/payment_cashdro.esm.js
@@ -107,7 +107,8 @@ export class PaymentCashdro extends PaymentInterface {
_cashdro_url() {
// Cashdro machines don't support safe POST calls, so we're sending
// all the data quite unsafely constantly...
- const method = this.pos.get_order().selected_paymentline.payment_method;
+ const order = this.pos.get_order();
+ const method = order.get_selected_paymentline()?.payment_method_id;
const host = method && method.cashdro_host;
if (!host) {
return false;
--
2.43.0