From f3181053b7590b5f04dc3db112e9dbf846873750 Mon Sep 17 00:00:00 2001 From: Pierre Verkest Date: Thu, 31 Aug 2023 15:46:57 +0200 Subject: [PATCH 01/25] edi_oca: avoid duplicate field name Avoid warning because of duplicated Model field name while installing this module --- edi_oca/models/edi_exchange_type_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_oca/models/edi_exchange_type_rule.py b/edi_oca/models/edi_exchange_type_rule.py index 809849372..4dfe9710b 100644 --- a/edi_oca/models/edi_exchange_type_rule.py +++ b/edi_oca/models/edi_exchange_type_rule.py @@ -32,7 +32,7 @@ class EDIExchangeTypeRule(models.Model): help="Apply to this model", ondelete="cascade", ) - model = fields.Char(related="model_id.model") # Tech field + model = fields.Char("Model code", related="model_id.model") # Tech field enable_domain = fields.Char( string="Enable on domain", help="Filter domain to be checked on Models" ) From fbe9be8a787d7c852fe2d92d761bb335c03634e2 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Fri, 15 Sep 2023 19:52:18 +0200 Subject: [PATCH 02/25] [FIX] edi_oca: Adapt tests to changes on error messages --- edi_oca/tests/test_security.py | 6 +++--- edi_oca/views/edi_backend_views.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/edi_oca/tests/test_security.py b/edi_oca/tests/test_security.py index b1ee7cd48..c8305d1dc 100644 --- a/edi_oca/tests/test_security.py +++ b/edi_oca/tests/test_security.py @@ -99,7 +99,7 @@ def test_rule_no_create(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_create(self): - with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): + with self.assertRaises(AccessError): self.create_record(self.user) @mute_logger("odoo.addons.base.models.ir_model") @@ -124,7 +124,7 @@ def test_rule_no_read(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_unlink(self): exchange_record = self.create_record() - with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): + with self.assertRaises(AccessError): exchange_record.with_user(self.user).unlink() @mute_logger("odoo.models.unlink") @@ -211,7 +211,7 @@ def test_search_no_record_admin(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_write(self): exchange_record = self.create_record() - with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): + with self.assertRaises(AccessError): exchange_record.with_user(self.user).write({"external_identifier": "1234"}) def test_group_write(self): diff --git a/edi_oca/views/edi_backend_views.xml b/edi_oca/views/edi_backend_views.xml index c7d1ce4d7..5c78b88f5 100644 --- a/edi_oca/views/edi_backend_views.xml +++ b/edi_oca/views/edi_backend_views.xml @@ -12,7 +12,7 @@ edi.backend -
+ Date: Mon, 2 Oct 2023 15:37:52 +0200 Subject: [PATCH 03/25] Revert "[FIX] edi_oca: Adapt tests to changes on error messages" This reverts commit a5da7ac9a3ca1bdc82f8f8ed2e8afc23592fb286. --- edi_oca/tests/test_security.py | 6 +++--- edi_oca/views/edi_backend_views.xml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/edi_oca/tests/test_security.py b/edi_oca/tests/test_security.py index c8305d1dc..b1ee7cd48 100644 --- a/edi_oca/tests/test_security.py +++ b/edi_oca/tests/test_security.py @@ -99,7 +99,7 @@ def test_rule_no_create(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_create(self): - with self.assertRaises(AccessError): + with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): self.create_record(self.user) @mute_logger("odoo.addons.base.models.ir_model") @@ -124,7 +124,7 @@ def test_rule_no_read(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_unlink(self): exchange_record = self.create_record() - with self.assertRaises(AccessError): + with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): exchange_record.with_user(self.user).unlink() @mute_logger("odoo.models.unlink") @@ -211,7 +211,7 @@ def test_search_no_record_admin(self): @mute_logger("odoo.addons.base.models.ir_model") def test_no_group_no_write(self): exchange_record = self.create_record() - with self.assertRaises(AccessError): + with self.assertRaisesRegex(AccessError, "You are not allowed to modify"): exchange_record.with_user(self.user).write({"external_identifier": "1234"}) def test_group_write(self): diff --git a/edi_oca/views/edi_backend_views.xml b/edi_oca/views/edi_backend_views.xml index 5c78b88f5..7c5a751c8 100644 --- a/edi_oca/views/edi_backend_views.xml +++ b/edi_oca/views/edi_backend_views.xml @@ -3,7 +3,7 @@ edi.backend - + @@ -12,7 +12,7 @@ edi.backend - + Date: Mon, 2 Oct 2023 15:38:54 +0200 Subject: [PATCH 04/25] edi: drop useless view form/tree strings --- edi_oca/views/edi_backend_views.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edi_oca/views/edi_backend_views.xml b/edi_oca/views/edi_backend_views.xml index 7c5a751c8..5c78b88f5 100644 --- a/edi_oca/views/edi_backend_views.xml +++ b/edi_oca/views/edi_backend_views.xml @@ -3,7 +3,7 @@ edi.backend - + @@ -12,7 +12,7 @@ edi.backend - + Date: Thu, 7 Sep 2023 07:25:38 +0200 Subject: [PATCH 05/25] edi: exc type fix view archived rules Archived rules were not visible on the exc type form. --- edi_oca/views/edi_exchange_type_views.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/edi_oca/views/edi_exchange_type_views.xml b/edi_oca/views/edi_exchange_type_views.xml index 6e19cd4a3..876fdac8c 100644 --- a/edi_oca/views/edi_exchange_type_views.xml +++ b/edi_oca/views/edi_exchange_type_views.xml @@ -135,7 +135,14 @@ tree,form [] - {'search_default_filter_all': 1} + + {'search_default_filter_all': 1, 'active_test': False} Date: Thu, 7 Sep 2023 07:26:27 +0200 Subject: [PATCH 06/25] edi: exc type view allow archive rules You can now toggle archived/unarchived directly from exc type form --- edi_oca/views/edi_exchange_type_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_oca/views/edi_exchange_type_views.xml b/edi_oca/views/edi_exchange_type_views.xml index 876fdac8c..354377d4a 100644 --- a/edi_oca/views/edi_exchange_type_views.xml +++ b/edi_oca/views/edi_exchange_type_views.xml @@ -72,7 +72,7 @@ context="{'default_type_id': active_id}" > - + From 3c850409032136aa381b8b6ff4328cae38de5955 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 7 Sep 2023 11:59:48 +0200 Subject: [PATCH 07/25] edi: fix archive rules when type archived --- edi_oca/models/edi_exchange_type.py | 8 +++++++- edi_oca/tests/test_exchange_type.py | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/edi_oca/models/edi_exchange_type.py b/edi_oca/models/edi_exchange_type.py index 96e846a30..7b5c38021 100644 --- a/edi_oca/models/edi_exchange_type.py +++ b/edi_oca/models/edi_exchange_type.py @@ -30,7 +30,7 @@ class EDIExchangeType(models.Model): _name = "edi.exchange.type" _description = "EDI Exchange Type" - active = fields.Boolean(default=True) + active = fields.Boolean(default=True, inverse="_inverse_active") backend_id = fields.Many2one( string="Backend", comodel_name="edi.backend", @@ -139,6 +139,12 @@ class EDIExchangeType(models.Model): ) ] + def _inverse_active(self): + for rec in self: + # Disable rules if type gets disabled + if not rec.active: + rec.rule_ids.active = False + @api.depends("advanced_settings_edit") def _compute_advanced_settings(self): for rec in self: diff --git a/edi_oca/tests/test_exchange_type.py b/edi_oca/tests/test_exchange_type.py index cc1a10d2f..db19930f0 100644 --- a/edi_oca/tests/test_exchange_type.py +++ b/edi_oca/tests/test_exchange_type.py @@ -128,3 +128,25 @@ def test_filename_pattern_settings(self): date_pattern: '%Y-%m-%d-%H-%M' """ self._test_exchange_filename("Test-File-2022-04-28-10-37.csv") + + def test_archive_rules(self): + exc_type = self.exchange_type_out + rule1 = exc_type.rule_ids.create( + { + "type_id": exc_type.id, + "name": "Fake partner rule", + "model_id": self.env["ir.model"]._get("res.partner").id, + } + ) + rule2 = exc_type.rule_ids.create( + { + "type_id": exc_type.id, + "name": "Fake user rule", + "model_id": self.env["ir.model"]._get("res.users").id, + } + ) + exc_type.active = False + rule1.invalidate_cache() + rule2.invalidate_cache() + self.assertFalse(rule1.active) + self.assertFalse(rule2.active) From c734545539047a8d71aa12e763425aad4d46ac87 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 15 May 2023 16:09:07 +0200 Subject: [PATCH 08/25] edi_oca: fix edi_id label --- edi_oca/models/edi_id_mixin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/edi_oca/models/edi_id_mixin.py b/edi_oca/models/edi_id_mixin.py index 33d39d2c1..87e039739 100644 --- a/edi_oca/models/edi_id_mixin.py +++ b/edi_oca/models/edi_id_mixin.py @@ -11,4 +11,6 @@ class EDIIdMixin(models.AbstractModel): _name = "edi.id.mixin" _description = "EDI ID mixin" - edi_id = fields.Char(help="Internal or external identifier for records.") + edi_id = fields.Char( + string="EDI ID", help="Internal or external identifier for records." + ) From 594a9f64cc1822e0ca8fe2f63653991ff561e4dd Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 7 Aug 2023 16:06:17 +0200 Subject: [PATCH 09/25] edi_oca: tests.common ease ctx setup --- edi_oca/tests/common.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/edi_oca/tests/common.py b/edi_oca/tests/common.py index 5d65cf843..d445d3f58 100644 --- a/edi_oca/tests/common.py +++ b/edi_oca/tests/common.py @@ -15,12 +15,15 @@ class EDIBackendTestMixin(object): @classmethod - def _setup_context(cls): - return dict(cls.env.context, tracking_disable=True, queue_job__no_delay=True) + def _setup_context(cls, **kw): + return dict( + cls.env.context, tracking_disable=True, test_queue_job_no_delay=True, **kw + ) @classmethod - def _setup_env(cls): - cls.env = cls.env(context=cls._setup_context()) + def _setup_env(cls, ctx=None): + ctx = ctx or {} + cls.env = cls.env(context=cls._setup_context(**ctx)) @classmethod def _setup_records(cls): From 7179735702cdef201e6a5805512af543f517ff1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Vi=20Or?= Date: Mon, 6 Nov 2023 15:55:41 +0100 Subject: [PATCH 10/25] [IMP] edi_oca: Show traceback on exception errors --- edi_oca/models/edi_backend.py | 32 +++++++++++++++----------- edi_oca/tests/test_backend_output.py | 2 +- edi_oca/tests/test_backend_process.py | 2 +- edi_oca/tests/test_backend_validate.py | 4 ++-- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 4085c8f1e..392d1e985 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -7,6 +7,8 @@ import base64 import logging +import traceback +from io import StringIO from odoo import _, exceptions, fields, models @@ -17,10 +19,12 @@ _logger = logging.getLogger(__name__) -def _get_exception_msg(exc): - if hasattr(exc, "args") and isinstance(exc.args[0], str): - return exc.args[0] - return repr(exc) +def _get_exception_msg(): + buff = StringIO() + traceback.print_exc(file=buff) + traceback_txt = buff.getvalue() + buff.close() + return traceback_txt class EDIBackend(models.Model): @@ -213,8 +217,8 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw): if output: try: self._validate_data(exchange_record, output) - except EDIValidationError as err: - error = _get_exception_msg(err) + except EDIValidationError: + error = _get_exception_msg() state = "validate_error" message = exchange_record._exchange_status_message("validate_ko") exchange_record.update( @@ -276,10 +280,10 @@ def exchange_send(self, exchange_record): message = None try: self._exchange_send(exchange_record) - except self._swallable_exceptions() as err: + except self._swallable_exceptions(): if self.env.context.get("_edi_send_break_on_error"): raise - error = _get_exception_msg(err) + error = _get_exception_msg() state = "output_error_on_send" message = exchange_record._exchange_status_message("send_ko") res = False @@ -444,10 +448,10 @@ def exchange_process(self, exchange_record): message = None try: self._exchange_process(exchange_record) - except self._swallable_exceptions() as err: + except self._swallable_exceptions(): if self.env.context.get("_edi_process_break_on_error"): raise - error = _get_exception_msg(err) + error = _get_exception_msg() state = "input_processed_error" res = False else: @@ -497,15 +501,15 @@ def exchange_receive(self, exchange_record): if content: exchange_record._set_file_content(content) self._validate_data(exchange_record) - except EDIValidationError as err: - error = _get_exception_msg(err) + except EDIValidationError: + error = _get_exception_msg() state = "validate_error" message = exchange_record._exchange_status_message("validate_ko") res = False - except self._swallable_exceptions() as err: + except self._swallable_exceptions(): if self.env.context.get("_edi_receive_break_on_error"): raise - error = _get_exception_msg(err) + error = _get_exception_msg() state = "input_receive_error" message = exchange_record._exchange_status_message("receive_ko") res = False diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index 5c386b241..db4a966bf 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -69,10 +69,10 @@ def test_send_record_with_error(self): [ { "edi_exchange_state": "output_error_on_send", - "exchange_error": "OOPS! Something went wrong :(", } ], ) + self.assertIn("OOPS! Something went wrong :(", self.record.exchange_error) def test_send_invalid_direction(self): vals = { diff --git a/edi_oca/tests/test_backend_process.py b/edi_oca/tests/test_backend_process.py index ed7b7ce74..efd11133d 100644 --- a/edi_oca/tests/test_backend_process.py +++ b/edi_oca/tests/test_backend_process.py @@ -58,10 +58,10 @@ def test_process_record_with_error(self): [ { "edi_exchange_state": "input_processed_error", - "exchange_error": "OOPS! Something went wrong :(", } ], ) + self.assertIn("OOPS! Something went wrong :(", self.record.exchange_error) @mute_logger("odoo.models.unlink") def test_process_no_file_record(self): diff --git a/edi_oca/tests/test_backend_validate.py b/edi_oca/tests/test_backend_validate.py index 7df573f08..b878d005b 100644 --- a/edi_oca/tests/test_backend_validate.py +++ b/edi_oca/tests/test_backend_validate.py @@ -62,10 +62,10 @@ def test_receive_validate_record_error(self): [ { "edi_exchange_state": "validate_error", - "exchange_error": "Data seems wrong!", } ], ) + self.assertIn("Data seems wrong!", self.record_in.exchange_error) def test_generate_validate_record(self): self.record_out.write({"edi_exchange_state": "new"}) @@ -87,7 +87,7 @@ def test_generate_validate_record_error(self): [ { "edi_exchange_state": "validate_error", - "exchange_error": "Data seems wrong!", } ], ) + self.assertIn("Data seems wrong!", self.record_out.exchange_error) From 57b66185313b3e91aeb431cb5c62d85d6cc2d95e Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 19 Oct 2023 08:39:00 +0200 Subject: [PATCH 11/25] edi: fix job return msg --- edi_oca/models/edi_backend.py | 29 ++++++++++--------- edi_oca/models/edi_exchange_record.py | 4 +-- edi_oca/tests/test_backend_jobs.py | 41 ++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 392d1e985..88df1eb8a 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -225,8 +225,9 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw): {"edi_exchange_state": state, "exchange_error": error} ) exchange_record.notify_action_complete("generate", message=message) - return output + return message + # TODO: unify to all other checkes that return something def _check_exchange_generate(self, exchange_record, force=False): exchange_record.ensure_one() if ( @@ -274,10 +275,11 @@ def exchange_send(self, exchange_record): # In case already sent: skip sending and check the state check = self._output_check_send(exchange_record) if not check: - return False + return "Nothing to do. Likely already sent." state = exchange_record.edi_exchange_state error = False message = None + res = "" try: self._exchange_send(exchange_record) except self._swallable_exceptions(): @@ -286,7 +288,7 @@ def exchange_send(self, exchange_record): error = _get_exception_msg() state = "output_error_on_send" message = exchange_record._exchange_status_message("send_ko") - res = False + res = f"Error: {error}" else: # TODO: maybe the send handler should return desired message and state message = exchange_record._exchange_status_message("send_ok") @@ -296,7 +298,7 @@ def exchange_send(self, exchange_record): if self.output_sent_processed_auto else "output_sent" ) - res = True + res = message finally: exchange_record.write( { @@ -442,22 +444,21 @@ def exchange_process(self, exchange_record): # In case already processed: skip processing and check the state check = self._exchange_process_check(exchange_record) if not check: - return False + return "Nothing to do. Likely already processed." old_state = state = exchange_record.edi_exchange_state error = False message = None try: - self._exchange_process(exchange_record) - except self._swallable_exceptions(): + res = self._exchange_process(exchange_record) + except self._swallable_exceptions() as err: if self.env.context.get("_edi_process_break_on_error"): raise error = _get_exception_msg() state = "input_processed_error" - res = False + res = f"Error: {error}" else: error = None state = "input_processed" - res = True finally: exchange_record.write( { @@ -491,7 +492,7 @@ def exchange_receive(self, exchange_record): # In case already processed: skip processing and check the state check = self._exchange_receive_check(exchange_record) if not check: - return False + return "Nothing to do. Likely already received." state = exchange_record.edi_exchange_state error = False message = None @@ -505,19 +506,19 @@ def exchange_receive(self, exchange_record): error = _get_exception_msg() state = "validate_error" message = exchange_record._exchange_status_message("validate_ko") - res = False - except self._swallable_exceptions(): + res = f"Validation error: {error}" + except self._swallable_exceptions() as err: if self.env.context.get("_edi_receive_break_on_error"): raise error = _get_exception_msg() state = "input_receive_error" message = exchange_record._exchange_status_message("receive_ko") - res = False + res = f"Input error: {error}" else: message = exchange_record._exchange_status_message("receive_ok") error = None state = "input_received" - res = True + res = message finally: exchange_record.write( { diff --git a/edi_oca/models/edi_exchange_record.py b/edi_oca/models/edi_exchange_record.py index 7ee502758..3c4d959a6 100644 --- a/edi_oca/models/edi_exchange_record.py +++ b/edi_oca/models/edi_exchange_record.py @@ -279,9 +279,9 @@ def _exchange_status_messages(self): "send_ko": _( "An error happened while sending. Please check exchange record info." ), - "process_ok": _("Exchange processed successfully "), + "process_ok": _("Exchange processed successfully"), "process_ko": _("Exchange processed with errors"), - "receive_ok": _("Exchange received successfully "), + "receive_ok": _("Exchange received successfully"), "receive_ko": _("Exchange not received"), "ack_received": _("ACK file received."), "ack_missing": _("ACK file is required for this exchange but not found."), diff --git a/edi_oca/tests/test_backend_jobs.py b/edi_oca/tests/test_backend_jobs.py index 8e2837fb0..5e6773cb9 100644 --- a/edi_oca/tests/test_backend_jobs.py +++ b/edi_oca/tests/test_backend_jobs.py @@ -2,6 +2,8 @@ # @author: Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import mock + from odoo.addons.queue_job.tests.common import JobMixin from .common import EDIBackendCommonTestCase @@ -19,14 +21,30 @@ def test_output(self): "res_id": self.partner.id, } record = self.backend.create_record("test_csv_output", vals) - self.backend.with_delay().exchange_generate(record) + self.assertEqual(record.edi_exchange_state, "new") + job = self.backend.with_delay().exchange_generate(record) created = job_counter.search_created() self.assertEqual(len(created), 1) self.assertEqual( created.name, "Generate output content for given exchange record." ) - self.backend.with_delay().exchange_send(record) + with mock.patch.object( + type(self.backend), "_exchange_generate" + ) as mocked_generate, mock.patch.object( + type(self.backend), "_validate_data" + ) as mocked_validate: + mocked_generate.return_value = "filecontent" + mocked_validate.return_value = None + res = job.perform() + self.assertEqual(res, None) + self.assertEqual(record.edi_exchange_state, "output_pending") + job = self.backend.with_delay().exchange_send(record) created = job_counter.search_created() + with mock.patch.object(type(self.backend), "_exchange_send") as mocked: + mocked.return_value = "ok" + res = job.perform() + self.assertEqual(res, "Exchange sent") + self.assertEqual(record.edi_exchange_state, "output_sent") self.assertEqual(created[0].name, "Send exchange file.") def test_input(self): @@ -36,10 +54,25 @@ def test_input(self): "res_id": self.partner.id, } record = self.backend.create_record("test_csv_input", vals) - self.backend.with_delay().exchange_receive(record) + job = self.backend.with_delay().exchange_receive(record) created = job_counter.search_created() self.assertEqual(len(created), 1) self.assertEqual(created.name, "Retrieve an incoming document.") - self.backend.with_delay().exchange_process(record) + with mock.patch.object( + type(self.backend), "_exchange_receive" + ) as mocked_receive, mock.patch.object( + type(self.backend), "_validate_data" + ) as mocked_validate: + mocked_receive.return_value = "filecontent" + mocked_validate.return_value = None + res = job.perform() + # the state is not input_pending hence there's nothing to do + self.assertEqual(res, "Nothing to do. Likely already received.") + record.edi_exchange_state = "input_pending" + res = job.perform() + # the state is not input_pending hence there's nothing to do + self.assertEqual(res, "Exchange received successfully") + self.assertEqual(record.edi_exchange_state, "input_received") + job = self.backend.with_delay().exchange_process(record) created = job_counter.search_created() self.assertEqual(created[0].name, "Process an incoming document.") From 026be89ec013af3d602411923e56a209a9d9324c Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 19 Oct 2023 08:54:21 +0200 Subject: [PATCH 12/25] edi: make send failed job retryable Send jobs might fail due to an external service being not responsive. If the job is simply failed, a new one will be spawned and might encour in the same error again, possibly leading to an high number of duplicated jobs for the same record. Yet, when a RetryableJobError is raised, the job will be set back into pending state and will be nicely retried based on jobs configuration. --- edi_oca/models/edi_backend.py | 14 ++++++++++++-- edi_oca/models/edi_exchange_record.py | 8 ++++++++ edi_oca/tests/test_backend_jobs.py | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 88df1eb8a..166adffcb 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -13,6 +13,7 @@ from odoo import _, exceptions, fields, models from odoo.addons.component.exception import NoComponentError +from odoo.addons.queue_job.exception import RetryableJobError from ..exceptions import EDIValidationError @@ -289,6 +290,11 @@ def exchange_send(self, exchange_record): state = "output_error_on_send" message = exchange_record._exchange_status_message("send_ko") res = f"Error: {error}" + except self._send_retryable_exceptions() as err: + error = _get_exception_msg() + raise RetryableJobError( + error, **exchange_record._job_retry_params() + ) from err else: # TODO: maybe the send handler should return desired message and state message = exchange_record._exchange_status_message("send_ok") @@ -321,6 +327,10 @@ def _swallable_exceptions(self): exceptions.ValidationError, ) + def _send_retryable_exceptions(self): + # IOError is a base class for all connection errors + return (IOError,) + def _output_check_send(self, exchange_record): if exchange_record.direction != "output": raise exceptions.UserError( @@ -450,7 +460,7 @@ def exchange_process(self, exchange_record): message = None try: res = self._exchange_process(exchange_record) - except self._swallable_exceptions() as err: + except self._swallable_exceptions(): if self.env.context.get("_edi_process_break_on_error"): raise error = _get_exception_msg() @@ -507,7 +517,7 @@ def exchange_receive(self, exchange_record): state = "validate_error" message = exchange_record._exchange_status_message("validate_ko") res = f"Validation error: {error}" - except self._swallable_exceptions() as err: + except self._swallable_exceptions(): if self.env.context.get("_edi_receive_break_on_error"): raise error = _get_exception_msg() diff --git a/edi_oca/models/edi_exchange_record.py b/edi_oca/models/edi_exchange_record.py index 3c4d959a6..0952279b6 100644 --- a/edi_oca/models/edi_exchange_record.py +++ b/edi_oca/models/edi_exchange_record.py @@ -573,3 +573,11 @@ def with_delay(self, **kw): params = self._job_delay_params() params.update(kw) return super().with_delay(**params) + + def delayable(self, **kw): + params = self._job_delay_params() + params.update(kw) + return super().delayable(**params) + + def _job_retry_params(self): + return {} diff --git a/edi_oca/tests/test_backend_jobs.py b/edi_oca/tests/test_backend_jobs.py index 5e6773cb9..aa2eae965 100644 --- a/edi_oca/tests/test_backend_jobs.py +++ b/edi_oca/tests/test_backend_jobs.py @@ -3,7 +3,9 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import mock +from requests.exceptions import ConnectionError as ReqConnectionError +from odoo.addons.queue_job.exception import RetryableJobError from odoo.addons.queue_job.tests.common import JobMixin from .common import EDIBackendCommonTestCase @@ -47,6 +49,22 @@ def test_output(self): self.assertEqual(record.edi_exchange_state, "output_sent") self.assertEqual(created[0].name, "Send exchange file.") + def test_output_fail_retry(self): + job_counter = self.job_counter() + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "edi_exchange_state": "output_pending", + } + record = self.backend.create_record("test_csv_output", vals) + record._set_file_content("ABC") + job = self.backend.with_delay().exchange_send(record) + job_counter.search_created() + with mock.patch.object(type(self.backend), "_exchange_send") as mocked: + mocked.side_effect = ReqConnectionError("Connection broken") + with self.assertRaises(RetryableJobError): + job.perform() + def test_input(self): job_counter = self.job_counter() vals = { From 8e494254fa077894115dcb0a37083b86d6f35778 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 21 Nov 2023 11:21:50 +0100 Subject: [PATCH 13/25] edi: test_same_code_same_backend mute sql logger --- edi_oca/tests/test_exchange_type.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/edi_oca/tests/test_exchange_type.py b/edi_oca/tests/test_exchange_type.py index db19930f0..7c62df9e5 100644 --- a/edi_oca/tests/test_exchange_type.py +++ b/edi_oca/tests/test_exchange_type.py @@ -5,6 +5,8 @@ from freezegun import freeze_time +from odoo.tools import mute_logger + from .common import EDIBackendCommonTestCase @@ -23,6 +25,7 @@ def test_ack_for(self): self.exchange_type_out_ack.ack_for_type_ids.ids, ) + @mute_logger("odoo.sql_db") def test_same_code_same_backend(self): with self.assertRaises(Exception) as err: self.exchange_type_in.copy({"code": "test_csv_input"}) From 16ed8fc51a1ce1d5aa1e1c94feba848766fd876c Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 5 Jul 2023 10:03:58 +0200 Subject: [PATCH 14/25] edi: add generate_ok message When actions are automated, is nice to see that data has been generated for an exchange on the related record. --- edi_oca/models/edi_backend.py | 1 + edi_oca/models/edi_exchange_record.py | 1 + edi_oca/tests/test_backend_jobs.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 166adffcb..8f8b9ba8b 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -216,6 +216,7 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw): } ) if output: + message = exchange_record._exchange_status_message("generate_ok") try: self._validate_data(exchange_record, output) except EDIValidationError: diff --git a/edi_oca/models/edi_exchange_record.py b/edi_oca/models/edi_exchange_record.py index 0952279b6..ce9efd2f2 100644 --- a/edi_oca/models/edi_exchange_record.py +++ b/edi_oca/models/edi_exchange_record.py @@ -275,6 +275,7 @@ def _constrain_backend(self): def _exchange_status_messages(self): return { # status: message + "generate_ok": _("Exchange data generated"), "send_ok": _("Exchange sent"), "send_ko": _( "An error happened while sending. Please check exchange record info." diff --git a/edi_oca/tests/test_backend_jobs.py b/edi_oca/tests/test_backend_jobs.py index aa2eae965..82c2e5538 100644 --- a/edi_oca/tests/test_backend_jobs.py +++ b/edi_oca/tests/test_backend_jobs.py @@ -38,7 +38,7 @@ def test_output(self): mocked_generate.return_value = "filecontent" mocked_validate.return_value = None res = job.perform() - self.assertEqual(res, None) + self.assertEqual(res, "Exchange data generated") self.assertEqual(record.edi_exchange_state, "output_pending") job = self.backend.with_delay().exchange_send(record) created = job_counter.search_created() From a47f195a51af96cc10720b605ab170203470ea01 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Thu, 6 Jul 2023 14:32:22 +0200 Subject: [PATCH 15/25] edi: fix backend jobs test old api The recommended way to execute actions on records is to call `action_exchange_*` on --- edi_oca/tests/test_backend_output.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index db4a966bf..4a9ebd87e 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -37,12 +37,14 @@ def setUp(self): FakeOutputChecker.reset_faked() def test_generate_record_output(self): - self.backend.with_context(fake_output="yeah!").exchange_generate(self.record) + self.record.with_context(fake_output="yeah!").action_exchange_generate() self.assertEqual(self.record._get_file_content(), "yeah!") def test_generate_record_output_pdf(self): - result = tools.file_open("addons/edi_oca/tests/result.pdf", mode="rb").read() - self.backend.with_context(fake_output=result).exchange_generate(self.record) + pdf_content = tools.file_open( + "result.pdf", subdir="addons/edi_oca/tests", mode="rb" + ).read() + self.record.with_context(fake_output=pdf_content).action_exchange_generate() def test_send_record(self): self.record.write({"edi_exchange_state": "output_pending"}) From 774900aca0cb1a7fb29e33b91a88aca56c198621 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 5 Jul 2023 10:06:28 +0200 Subject: [PATCH 16/25] edi: chain generate/send jobs Instead of waiting for the cron to pass again and send the file chain the 2 jobs so that it gets sent right after generation. --- edi_oca/models/edi_backend.py | 6 ++++- edi_oca/tests/test_backend_output.py | 35 +++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 8f8b9ba8b..f1894a83b 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -377,7 +377,11 @@ def _check_output_exchange_sync( len(new_records), ) for rec in new_records: - rec.with_delay().action_exchange_generate() + job1 = rec.delayable().action_exchange_generate() + if not skip_send: + # Chain send job + job1.on_done(rec.delayable().action_exchange_send()) + job1.delay() if skip_send: return diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index 4a9ebd87e..54724580a 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -1,4 +1,5 @@ # Copyright 2020 ACSONE +# Copyright 2021 Camptocamp # @author: Simone Orsi # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). @@ -9,6 +10,8 @@ from odoo import fields, tools from odoo.exceptions import UserError +from odoo.addons.queue_job.tests.common import trap_jobs + from .common import EDIBackendCommonComponentRegistryTestCase from .fake_components import FakeOutputChecker, FakeOutputGenerator, FakeOutputSender @@ -18,7 +21,6 @@ class EDIBackendTestOutputCase(EDIBackendCommonComponentRegistryTestCase): def setUpClass(cls): super().setUpClass() cls._build_components( - # TODO: test all components lookup cls, FakeOutputGenerator, FakeOutputSender, @@ -106,3 +108,34 @@ def test_send_not_generated_record(self): err.exception.args[0], "Record ID=%d has no file to send!" % record.id ) mocked.assert_not_called() + + +class EDIBackendTestOutputJobsCase(EDIBackendCommonComponentRegistryTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._build_components( + cls, + FakeOutputGenerator, + FakeOutputSender, + FakeOutputChecker, + ) + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_output", vals) + cls.record.type_id.exchange_file_auto_generate = True + + @classmethod + def _setup_context(cls): + # Re-enable jobs + return dict(super()._setup_context(), test_queue_job_no_delay=False) + + def test_job(self): + with trap_jobs() as trap: + self.backend._check_output_exchange_sync(record_ids=self.record.ids) + trap.assert_jobs_count(2) + trap.assert_enqueued_job( + self.backend.exchange_record_model.action_exchange_generate, + ) From 45815213d6e3109adeb9445b7732841f8f834864 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 24 Nov 2023 15:57:35 +0100 Subject: [PATCH 17/25] edi: improve send job retry * catch OSError * add debug log --- edi_oca/models/edi_backend.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index f1894a83b..16220b4b1 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -284,6 +284,13 @@ def exchange_send(self, exchange_record): res = "" try: self._exchange_send(exchange_record) + _logger.debug("%s sent", exchange_record.identifier) + except self._send_retryable_exceptions() as err: + error = _get_exception_msg() + _logger.debug("%s send failed. To be retried.", exchange_record.identifier) + raise RetryableJobError( + error, **exchange_record._job_retry_params() + ) from err except self._swallable_exceptions(): if self.env.context.get("_edi_send_break_on_error"): raise @@ -291,11 +298,9 @@ def exchange_send(self, exchange_record): state = "output_error_on_send" message = exchange_record._exchange_status_message("send_ko") res = f"Error: {error}" - except self._send_retryable_exceptions() as err: - error = _get_exception_msg() - raise RetryableJobError( - error, **exchange_record._job_retry_params() - ) from err + _logger.debug( + "%s send failed. Marked as errored.", exchange_record.identifier + ) else: # TODO: maybe the send handler should return desired message and state message = exchange_record._exchange_status_message("send_ok") @@ -330,7 +335,9 @@ def _swallable_exceptions(self): def _send_retryable_exceptions(self): # IOError is a base class for all connection errors - return (IOError,) + # OSError is a base class for all errors + # when dealing w/ internal or external systems or filesystems + return (IOError, OSError) def _output_check_send(self, exchange_record): if exchange_record.direction != "output": @@ -356,7 +363,6 @@ def _cron_check_output_exchange_sync(self, **kw): for backend in self: backend._check_output_exchange_sync(**kw) - # TODO: consider splitting cron in 2 (1 for receiving, 1 for processing) def _check_output_exchange_sync( self, skip_send=False, skip_sent=True, record_ids=None ): From a8d097003333ec8b532fab18df77afd0223779aa Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Fri, 24 Nov 2023 16:00:00 +0100 Subject: [PATCH 18/25] edi: add file checksum --- edi_oca/models/edi_exchange_record.py | 13 +++++++++++++ edi_oca/tests/test_record.py | 17 +++++++++++++++++ edi_oca/utils.py | 6 ++++++ edi_oca/views/edi_exchange_record_views.xml | 4 ++++ 4 files changed, 40 insertions(+) diff --git a/edi_oca/models/edi_exchange_record.py b/edi_oca/models/edi_exchange_record.py index ce9efd2f2..830e88747 100644 --- a/edi_oca/models/edi_exchange_record.py +++ b/edi_oca/models/edi_exchange_record.py @@ -9,6 +9,8 @@ from odoo import _, api, exceptions, fields, models +from ..utils import get_checksum + _logger = logging.getLogger(__name__) @@ -50,6 +52,9 @@ class EDIExchangeRecord(models.Model): exchange_filename = fields.Char( compute="_compute_exchange_filename", readonly=False, store=True ) + exchange_filechecksum = fields.Char( + compute="_compute_exchange_filechecksum", store=True + ) exchanged_on = fields.Datetime( help="Sent or received on this date.", compute="_compute_exchanged_on", @@ -133,6 +138,14 @@ def _compute_exchange_filename(self): if not rec.exchange_filename: rec.exchange_filename = rec.type_id._make_exchange_filename(rec) + @api.depends("exchange_file") + def _compute_exchange_filechecksum(self): + for rec in self: + content = rec.exchange_file or "" + if not isinstance(content, bytes): + content = content.encode() + rec.exchange_filechecksum = get_checksum(content) + @api.depends("edi_exchange_state") def _compute_exchanged_on(self): for rec in self: diff --git a/edi_oca/tests/test_record.py b/edi_oca/tests/test_record.py index a5aae1b44..0492ba800 100644 --- a/edi_oca/tests/test_record.py +++ b/edi_oca/tests/test_record.py @@ -10,6 +10,7 @@ from odoo import exceptions, fields from odoo.tools import mute_logger +from odoo.addons.edi_oca.utils import get_checksum from odoo.addons.queue_job.delay import DelayableRecordset from .common import EDIBackendCommonTestCase @@ -217,3 +218,19 @@ def test_retry(self): mocked.assert_not_called() self.assertEqual(record0.edi_exchange_state, "output_pending") self.assertFalse(record0.retryable) + + def test_checksum(self): + filecontent = base64.b64encode(b"ABC") + checksum1 = get_checksum(filecontent) + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "exchange_file": filecontent, + } + record0 = self.backend.create_record("test_csv_output", vals) + self.assertEqual(record0.exchange_filechecksum, checksum1) + filecontent = base64.b64encode(b"DEF") + checksum2 = get_checksum(filecontent) + record0.exchange_file = filecontent + self.assertEqual(record0.exchange_filechecksum, checksum2) + self.assertNotEqual(record0.exchange_filechecksum, checksum1) diff --git a/edi_oca/utils.py b/edi_oca/utils.py index 2c5271fc0..f69a8d6e2 100644 --- a/edi_oca/utils.py +++ b/edi_oca/utils.py @@ -2,9 +2,15 @@ # @author Simone Orsi # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import hashlib + from odoo.addons.http_routing.models.ir_http import slugify def normalize_string(a_string, sep="_"): """Normalize given string, replace dashes with given separator.""" return slugify(a_string).replace("-", sep) + + +def get_checksum(filecontent): + return hashlib.md5(filecontent).hexdigest() diff --git a/edi_oca/views/edi_exchange_record_views.xml b/edi_oca/views/edi_exchange_record_views.xml index 8b58539e9..531bdf3d5 100644 --- a/edi_oca/views/edi_exchange_record_views.xml +++ b/edi_oca/views/edi_exchange_record_views.xml @@ -114,6 +114,10 @@ name="exchange_filename" attrs="{'invisible': [('exchange_file', '!=', False)]}" /> + Date: Fri, 24 Nov 2023 16:05:46 +0100 Subject: [PATCH 19/25] edi: use job identity_key Prevents having duplicated jobs for the same record as far as possible. --- edi_oca/models/edi_exchange_record.py | 4 +++- edi_oca/tests/test_backend_output.py | 21 ++++++++++++++++++++- edi_oca/utils.py | 10 ++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/edi_oca/models/edi_exchange_record.py b/edi_oca/models/edi_exchange_record.py index 830e88747..b1dd68880 100644 --- a/edi_oca/models/edi_exchange_record.py +++ b/edi_oca/models/edi_exchange_record.py @@ -9,7 +9,7 @@ from odoo import _, api, exceptions, fields, models -from ..utils import get_checksum +from ..utils import exchange_record_job_identity_exact, get_checksum _logger = logging.getLogger(__name__) @@ -581,6 +581,8 @@ def _job_delay_params(self): channel = self.type_id.sudo().job_channel_id if channel: params["channel"] = channel.complete_name + # Avoid generating the same job for the same record if existing + params["identity_key"] = exchange_record_job_identity_exact return params def with_delay(self, **kw): diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index 54724580a..02bbf4c07 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -137,5 +137,24 @@ def test_job(self): self.backend._check_output_exchange_sync(record_ids=self.record.ids) trap.assert_jobs_count(2) trap.assert_enqueued_job( - self.backend.exchange_record_model.action_exchange_generate, + self.record.action_exchange_generate, ) + trap.assert_enqueued_job( + self.record.action_exchange_send, + ) + # No matter how many times we schedule jobs + self.record.with_delay().action_exchange_generate() + self.record.with_delay().action_exchange_generate() + self.record.with_delay().action_exchange_generate() + # identity key should prevent having new jobs for same record same file + trap.assert_jobs_count(2) + # but if we change the content + self.record._set_file_content("something different") + # 1st call will schedule another job + self.record.with_delay().action_exchange_generate() + # the 2nd one not + self.record.with_delay().action_exchange_generate() + trap.assert_jobs_count(3) + self.record.with_delay().action_exchange_send() + trap.assert_jobs_count(4) + # TODO: test input in the same way diff --git a/edi_oca/utils.py b/edi_oca/utils.py index f69a8d6e2..5ca764e0c 100644 --- a/edi_oca/utils.py +++ b/edi_oca/utils.py @@ -5,6 +5,7 @@ import hashlib from odoo.addons.http_routing.models.ir_http import slugify +from odoo.addons.queue_job.job import identity_exact_hasher def normalize_string(a_string, sep="_"): @@ -14,3 +15,12 @@ def normalize_string(a_string, sep="_"): def get_checksum(filecontent): return hashlib.md5(filecontent).hexdigest() + + +def exchange_record_job_identity_exact(job_): + hasher = identity_exact_hasher(job_) + # Include files checksum + hasher.update( + str(sorted(job_.recordset.mapped("exchange_filechecksum"))).encode("utf-8") + ) + return hasher.hexdigest() From f258f7c9152de6f52eb0a752f86d5460d45c35e7 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 29 Nov 2023 11:57:25 +0100 Subject: [PATCH 20/25] edi: raise send job prio to max Try to send out the file as fast as possible once the content is ready. --- edi_oca/models/edi_backend.py | 5 +++-- edi_oca/tests/test_backend_output.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/edi_oca/models/edi_backend.py b/edi_oca/models/edi_backend.py index 16220b4b1..80f141420 100644 --- a/edi_oca/models/edi_backend.py +++ b/edi_oca/models/edi_backend.py @@ -385,8 +385,9 @@ def _check_output_exchange_sync( for rec in new_records: job1 = rec.delayable().action_exchange_generate() if not skip_send: - # Chain send job - job1.on_done(rec.delayable().action_exchange_send()) + # Chain send job. + # Raise prio to max to send the record out as fast as possible. + job1.on_done(rec.delayable(priority=0).action_exchange_send()) job1.delay() if skip_send: diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index 02bbf4c07..c2fd14680 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -140,7 +140,7 @@ def test_job(self): self.record.action_exchange_generate, ) trap.assert_enqueued_job( - self.record.action_exchange_send, + self.record.action_exchange_send, properties=dict(priority=0) ) # No matter how many times we schedule jobs self.record.with_delay().action_exchange_generate() From 04e762f926af6d3a0de37b8d2447e7bb5e5b8fe4 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 29 Nov 2023 12:48:42 +0100 Subject: [PATCH 21/25] edi: fix consumer mixin copy origin exc record The origin record should never been copied. Its values should always be set specifically by a framework action. --- edi_oca/models/edi_exchange_consumer_mixin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/edi_oca/models/edi_exchange_consumer_mixin.py b/edi_oca/models/edi_exchange_consumer_mixin.py index 80a4efa46..5af519f82 100644 --- a/edi_oca/models/edi_exchange_consumer_mixin.py +++ b/edi_oca/models/edi_exchange_consumer_mixin.py @@ -25,6 +25,7 @@ class EDIExchangeConsumerMixin(models.AbstractModel): comodel_name="edi.exchange.record", ondelete="set null", help="EDI record that originated this document.", + copy=False, ) origin_exchange_type_id = fields.Many2one( string="EDI origin exchange type", @@ -33,6 +34,7 @@ class EDIExchangeConsumerMixin(models.AbstractModel): related="origin_exchange_record_id.type_id", # Store it to ease searching by type store=True, + copy=False, ) exchange_record_ids = fields.One2many( "edi.exchange.record", From 114b4cc453cad109afb741efc7b64f003637d0bd Mon Sep 17 00:00:00 2001 From: duongtq Date: Mon, 22 Jan 2024 14:34:48 +0700 Subject: [PATCH 22/25] [FIX] edi_oca: fixs after porting from 14.0 --- edi_oca/tests/test_backend_jobs.py | 5 +++-- edi_oca/tests/test_backend_output.py | 2 +- edi_oca/tests/test_record.py | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/edi_oca/tests/test_backend_jobs.py b/edi_oca/tests/test_backend_jobs.py index 82c2e5538..dcca0d90c 100644 --- a/edi_oca/tests/test_backend_jobs.py +++ b/edi_oca/tests/test_backend_jobs.py @@ -2,7 +2,8 @@ # @author: Simone Orsi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import mock +from unittest import mock + from requests.exceptions import ConnectionError as ReqConnectionError from odoo.addons.queue_job.exception import RetryableJobError @@ -14,7 +15,7 @@ class EDIBackendTestJobsCase(EDIBackendCommonTestCase, JobMixin): @classmethod def _setup_context(cls): - return dict(super()._setup_context(), queue_job__no_delay=None) + return dict(super()._setup_context(), test_queue_job_no_delay=None) def test_output(self): job_counter = self.job_counter() diff --git a/edi_oca/tests/test_backend_output.py b/edi_oca/tests/test_backend_output.py index c2fd14680..9f48d178d 100644 --- a/edi_oca/tests/test_backend_output.py +++ b/edi_oca/tests/test_backend_output.py @@ -44,7 +44,7 @@ def test_generate_record_output(self): def test_generate_record_output_pdf(self): pdf_content = tools.file_open( - "result.pdf", subdir="addons/edi_oca/tests", mode="rb" + "addons/edi_oca/tests/result.pdf", mode="rb" ).read() self.record.with_context(fake_output=pdf_content).action_exchange_generate() diff --git a/edi_oca/tests/test_record.py b/edi_oca/tests/test_record.py index 0492ba800..f74db8e36 100644 --- a/edi_oca/tests/test_record.py +++ b/edi_oca/tests/test_record.py @@ -3,6 +3,7 @@ # @author: Simone Orsi # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import base64 from unittest import mock from freezegun import freeze_time From 73f4983b13c41991c3cbbfda5d753713b2d567bb Mon Sep 17 00:00:00 2001 From: duongtq Date: Mon, 22 Jan 2024 15:36:21 +0700 Subject: [PATCH 23/25] Update blacklist of edi_oca --- .oca/oca-port/blacklist/edi_oca.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.oca/oca-port/blacklist/edi_oca.json b/.oca/oca-port/blacklist/edi_oca.json index 2be1f444a..53f0ff98a 100644 --- a/.oca/oca-port/blacklist/edi_oca.json +++ b/.oca/oca-port/blacklist/edi_oca.json @@ -24,6 +24,16 @@ "OCA/edi#774": "already ported", "OCA/edi#778": "already ported", "OCA/edi#797": "already ported", - "OCA/edi#790": "Nothing to port from PR #OCA/edi#790" + "OCA/edi#790": "Nothing to port from PR #OCA/edi#790", + "OCA/edi#808": "already ported", + "OCA/edi#819": "already ported", + "OCA/edi#822": "already ported", + "OCA/edi#824": "already ported", + "OCA/edi#830": "already ported", + "OCA/edi#852": "already ported", + "OCA/edi#855": "already ported", + "OCA/edi#796": "already ported", + "OCA/edi#880": "already ported", + "OCA/edi#897": "already ported" } } From fd63bae8e839f096a6fe7d336d404be6ada3ae0b Mon Sep 17 00:00:00 2001 From: duongtq Date: Mon, 22 Jan 2024 15:58:30 +0700 Subject: [PATCH 24/25] [FIX] edi_sale_oca: fix test --- edi_sale_oca/tests/test_process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_sale_oca/tests/test_process.py b/edi_sale_oca/tests/test_process.py index dbf05bbe3..be6d1e7e2 100644 --- a/edi_sale_oca/tests/test_process.py +++ b/edi_sale_oca/tests/test_process.py @@ -87,7 +87,7 @@ def test_existing_order(self): self.record.action_exchange_process() md_onchange.assert_called() md_btn.assert_called() - self.assertEqual(self.record.exchange_error, err_msg) + self.assertIn(err_msg, self.record.exchange_error) def test_new_order(self): # Create the order manully and use it via the mock on md_btn From 732f2bc55820ef49a1884ee5773eb0942c08e83f Mon Sep 17 00:00:00 2001 From: duongtq Date: Mon, 22 Jan 2024 15:59:44 +0700 Subject: [PATCH 25/25] [FIX] edi_sale_edifact_oca: fix test --- edi_sale_edifact_oca/tests/test_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edi_sale_edifact_oca/tests/test_order.py b/edi_sale_edifact_oca/tests/test_order.py index 69404d49c..245b53465 100644 --- a/edi_sale_edifact_oca/tests/test_order.py +++ b/edi_sale_edifact_oca/tests/test_order.py @@ -72,4 +72,4 @@ def test_existing_order(self): self.exc_record_in.action_exchange_process() self.assertEqual(self.exc_record_in.edi_exchange_state, "input_processed_error") err_msg = "Sales order has already been imported before" - self.assertEqual(self.exc_record_in.exchange_error, err_msg) + self.assertIn(err_msg, self.exc_record_in.exchange_error)