From 7f4169e5fe0bfe5ab06d2a592b9039598e93ae5f Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Fri, 24 Jul 2026 20:34:04 +0200 Subject: [PATCH 1/3] [IMP] edi_queue_oca: update exc record state on failed jobs Rely on new on fail hooks from the queue job module. --- edi_queue_oca/data/job_function.xml | 4 ++++ edi_queue_oca/models/edi_exchange_record.py | 22 +++++++++++++++++++++ edi_queue_oca/tests/test_backend_jobs.py | 21 ++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/edi_queue_oca/data/job_function.xml b/edi_queue_oca/data/job_function.xml index aedc76dec..6183d9c3b 100644 --- a/edi_queue_oca/data/job_function.xml +++ b/edi_queue_oca/data/job_function.xml @@ -2,21 +2,25 @@ action_exchange_generate + _job_on_fail_generate action_exchange_send + _job_on_fail_send action_exchange_receive + _job_on_fail_receive action_exchange_process + _job_on_fail_process diff --git a/edi_queue_oca/models/edi_exchange_record.py b/edi_queue_oca/models/edi_exchange_record.py index 58915fa5a..21c606e15 100644 --- a/edi_queue_oca/models/edi_exchange_record.py +++ b/edi_queue_oca/models/edi_exchange_record.py @@ -91,3 +91,25 @@ def action_exchange_generate_send_chained(self): # Raise prio to max to send the record out as fast as possible. job1.on_done(self.delayable(priority=0).action_exchange_send()) job1.delay() + + def _job_on_fail_generate(self, **kw): + return self._job_on_fail_update("validate_error", **kw) + + def _job_on_fail_send(self, **kw): + return self._job_on_fail_update("output_error_on_send", **kw) + + def _job_on_fail_receive(self, **kw): + return self._job_on_fail_update("input_receive_error", **kw) + + def _job_on_fail_process(self, **kw): + return self._job_on_fail_update("input_processed_error", **kw) + + def _job_on_fail_update(self, failed_state, **kw): + self.ensure_one() + self.write( + { + "edi_exchange_state": failed_state, + "exchange_error": ": ".join([kw["exc_name"], kw["exc_message"]]), + "exchange_error_traceback": kw["exc_info"], + } + ) diff --git a/edi_queue_oca/tests/test_backend_jobs.py b/edi_queue_oca/tests/test_backend_jobs.py index 44c90eac7..c2ddb2060 100644 --- a/edi_queue_oca/tests/test_backend_jobs.py +++ b/edi_queue_oca/tests/test_backend_jobs.py @@ -159,3 +159,24 @@ def test_input_processed_error(self): # Check related jobs record.invalidate_recordset() self.assertEqual(created, self._get_related_jobs(record)) + + def test_on_fail_job(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + self.assertEqual(record.edi_exchange_state, "new") + job = record.action_exchange_generate() + exc_vals = { + "exc_info": "Dummy traceback", + "exc_name": "Dummy exception", + "exc_message": "Dummy message", + } + job.on_fail(exc_vals) + self.assertEqual(record.edi_exchange_state, "validate_error") + self.assertEqual( + record.exchange_error, + ": ".join([exc_vals["exc_name"], exc_vals["exc_message"]]), + ) + self.assertEqual(record.exchange_error_traceback, exc_vals["exc_info"]) From a00b19513ee8ef472054163f7ab081469c6020b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Thu, 3 Sep 2026 13:28:41 -0300 Subject: [PATCH 2/3] [FIX] edi_queue_oca: set on_fail_method on existing job functions The on fail hooks introduced in b3290c735d29da133c44a6124b1ae05eb98d64bf are declared in a noupdate data file, so databases created before that commit never got `on_fail_method` on the exchange record job functions, and failed jobs do not update the exchange record state. Add a post-migration script to fill the missing values. --- edi_queue_oca/README.rst | 16 ++++------ edi_queue_oca/__manifest__.py | 2 +- .../migrations/18.0.1.0.3/post-migration.py | 30 +++++++++++++++++++ edi_queue_oca/static/description/index.html | 24 ++++++--------- 4 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 edi_queue_oca/migrations/18.0.1.0.3/post-migration.py diff --git a/edi_queue_oca/README.rst b/edi_queue_oca/README.rst index c9b6f4945..dd84a7d8d 100644 --- a/edi_queue_oca/README.rst +++ b/edi_queue_oca/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ============= Edi Queue Oca ============= @@ -17,7 +13,7 @@ Edi Queue Oca .. |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/license-LGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi--framework-lightgray.png?logo=github @@ -65,11 +61,11 @@ Authors Contributors ------------ -- Simone Orsi -- Enric Tobella -- Manuel Regidor -- Thien Vo -- Jordi Masvidal +- Simone Orsi +- Enric Tobella +- Manuel Regidor +- Thien Vo +- Jordi Masvidal Maintainers ----------- diff --git a/edi_queue_oca/__manifest__.py b/edi_queue_oca/__manifest__.py index dec27cca5..e34d4ddf2 100644 --- a/edi_queue_oca/__manifest__.py +++ b/edi_queue_oca/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Edi Queue Oca", "summary": """Set Queue Jobs on EDI""", - "version": "18.0.1.0.2", + "version": "18.0.1.0.3", "license": "LGPL-3", "author": "Dixmit,Camptocamp,Odoo Community Association (OCA)", "website": "https://github.com/OCA/edi-framework", diff --git a/edi_queue_oca/migrations/18.0.1.0.3/post-migration.py b/edi_queue_oca/migrations/18.0.1.0.3/post-migration.py new file mode 100644 index 000000000..c0d734735 --- /dev/null +++ b/edi_queue_oca/migrations/18.0.1.0.3/post-migration.py @@ -0,0 +1,30 @@ +# Copyright 2026 Camptocamp SA (http://www.camptocamp.com) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + +# The job functions are declared in a ``noupdate`` data file, so databases +# created before the on fail hooks were added never got ``on_fail_method``. +ON_FAIL_METHODS = { + "edi_queue_oca.job_fun_exchange_record_generate": "_job_on_fail_generate", + "edi_queue_oca.job_fun_exchange_record_send": "_job_on_fail_send", + "edi_queue_oca.job_fun_exchange_record_receive": "_job_on_fail_receive", + "edi_queue_oca.job_fun_exchange_record_process": "_job_on_fail_process", +} + + +def migrate(cr, version): + env = api.Environment(cr, SUPERUSER_ID, {}) + for xmlid, method in ON_FAIL_METHODS.items(): + job_function = env.ref(xmlid, raise_if_not_found=False) + if not job_function: + _logger.warning("Job function %s not found, skipping", xmlid) + continue + if job_function.on_fail_method: + continue + _logger.info("Setting on_fail_method=%s on %s", method, xmlid) + job_function.on_fail_method = method diff --git a/edi_queue_oca/static/description/index.html b/edi_queue_oca/static/description/index.html index c65aff333..de5c87ae6 100644 --- a/edi_queue_oca/static/description/index.html +++ b/edi_queue_oca/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Edi Queue Oca -
+
+

Edi Queue Oca

- - -Odoo Community Association - -
-

Edi Queue Oca

-

Beta License: LGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

This module integrates EDI with Queue Job and now the edi exchange records are generated using queue.

No need of doing a configuration on it, however, we can specify priority @@ -392,7 +387,7 @@

Edi Queue Oca

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -400,16 +395,16 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Dixmit
  • Camptocamp
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -432,6 +427,5 @@

Maintainers

-
From ce45adec6ef9df871a86fe73d23f45708dd7198b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Thu, 3 Sep 2026 14:11:55 -0300 Subject: [PATCH 3/3] [FIX] edi_queue_oca: on fail hook with non string exception message The hook joins `exc_name` and `exc_message` as strings, but the message stored by queue_job is the first argument of the exception, which is the errno for an OSError and may also be missing. The join then raises a TypeError and the exchange record is not updated. --- edi_queue_oca/models/edi_exchange_record.py | 9 ++++-- edi_queue_oca/tests/test_backend_jobs.py | 35 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/edi_queue_oca/models/edi_exchange_record.py b/edi_queue_oca/models/edi_exchange_record.py index 21c606e15..6f420947f 100644 --- a/edi_queue_oca/models/edi_exchange_record.py +++ b/edi_queue_oca/models/edi_exchange_record.py @@ -106,10 +106,15 @@ def _job_on_fail_process(self, **kw): def _job_on_fail_update(self, failed_state, **kw): self.ensure_one() + # ``exc_message`` is the first argument of the exception: it is not + # always a string (e.g. the errno of an ``OSError``) and may be missing. + error = ": ".join( + str(kw[k]) for k in ("exc_name", "exc_message") if kw.get(k) is not None + ) self.write( { "edi_exchange_state": failed_state, - "exchange_error": ": ".join([kw["exc_name"], kw["exc_message"]]), - "exchange_error_traceback": kw["exc_info"], + "exchange_error": error, + "exchange_error_traceback": kw.get("exc_info"), } ) diff --git a/edi_queue_oca/tests/test_backend_jobs.py b/edi_queue_oca/tests/test_backend_jobs.py index c2ddb2060..71521d12a 100644 --- a/edi_queue_oca/tests/test_backend_jobs.py +++ b/edi_queue_oca/tests/test_backend_jobs.py @@ -180,3 +180,38 @@ def test_on_fail_job(self): ": ".join([exc_vals["exc_name"], exc_vals["exc_message"]]), ) self.assertEqual(record.exchange_error_traceback, exc_vals["exc_info"]) + + def test_on_fail_job_non_string_message(self): + # The message is the first argument of the exception, which is the + # errno for an OSError, or may be missing altogether. + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + job = record.action_exchange_generate() + job.on_fail( + { + "exc_info": "Dummy traceback", + "exc_name": "PermissionError", + "exc_message": 13, + } + ) + self.assertEqual(record.edi_exchange_state, "validate_error") + self.assertEqual(record.exchange_error, "PermissionError: 13") + job.on_fail( + { + "exc_info": "Dummy traceback", + "exc_name": "JobFoundDead", + "exc_message": None, + } + ) + self.assertEqual(record.exchange_error, "JobFoundDead") + job.on_fail( + { + "exc_info": "Dummy traceback", + "exc_name": None, + "exc_message": "Something went wrong", + } + ) + self.assertEqual(record.exchange_error, "Something went wrong")