diff --git a/edi_core_oca/models/edi_backend.py b/edi_core_oca/models/edi_backend.py index d4af65ff6..937a143d5 100644 --- a/edi_core_oca/models/edi_backend.py +++ b/edi_core_oca/models/edi_backend.py @@ -370,6 +370,8 @@ def _output_new_records_domain(self, record_ids=None): ("backend_id", "=", self.id), ("type_id.exchange_file_auto_generate", "=", True), ("type_id.direction", "=", "output"), + ("type_id.generate_model_id", "!=", False), + ("type_id.send_model_id", "!=", False), ("edi_exchange_state", "=", "new"), ("exchange_file", "=", False), ] @@ -388,6 +390,7 @@ def _output_pending_records_domain(self, skip_sent=True, record_ids=None): states += ("output_sent",) domain = [ ("type_id.direction", "=", "output"), + ("type_id.send_model_id", "!=", False), ("backend_id", "=", self.id), ("edi_exchange_state", "in", states), ] @@ -575,6 +578,7 @@ def _input_pending_records_domain(self, record_ids=None): domain = [ ("backend_id", "=", self.id), ("type_id.direction", "=", "input"), + ("type_id.receive_model_id", "!=", False), ("edi_exchange_state", "=", "input_pending"), ("exchange_file", "=", False), ] @@ -587,6 +591,7 @@ def _input_pending_process_records_domain(self, record_ids=None): domain = [ ("backend_id", "=", self.id), ("type_id.direction", "=", "input"), + ("type_id.process_model_id", "!=", False), ("edi_exchange_state", "in", states), ] if record_ids: diff --git a/edi_core_oca/models/edi_exchange_type.py b/edi_core_oca/models/edi_exchange_type.py index d6a2cab96..2f602ac6a 100644 --- a/edi_core_oca/models/edi_exchange_type.py +++ b/edi_core_oca/models/edi_exchange_type.py @@ -240,6 +240,32 @@ def get_settings(self): def set_settings(self, val): self.advanced_settings_edit = val + @api.constrains( + "direction", + "send_model_id", + "process_model_id", + "generate_model_id", + "exchange_file_auto_generate", + ) + def _check_direction_handlers(self): + for rec in self: + if rec.direction == "output": + if not rec.send_model_id: + raise exceptions.ValidationError( + self.env._("Output exchange types require a Sender handler.") + ) + if rec.exchange_file_auto_generate and not rec.generate_model_id: + raise exceptions.ValidationError( + self.env._( + "Output exchange types with 'Auto Generate' enabled " + "require a Generator handler." + ) + ) + elif rec.direction == "input" and not rec.process_model_id: + raise exceptions.ValidationError( + self.env._("Input exchange types require a Processor handler.") + ) + @api.constrains("backend_id", "backend_type_id") def _check_backend(self): for rec in self: diff --git a/edi_core_oca/tests/common.py b/edi_core_oca/tests/common.py index fe9bdb9d1..0ed40d75a 100644 --- a/edi_core_oca/tests/common.py +++ b/edi_core_oca/tests/common.py @@ -5,6 +5,7 @@ import os +from odoo.orm.model_classes import add_to_registry from odoo.tests.common import TransactionCase @@ -19,6 +20,24 @@ def _setup_context(cls, **kw): def _setup_env(cls, ctx=None): ctx = ctx or {} cls.env = cls.env(context=cls._setup_context(**ctx)) + # Register EdiTestExecution early so _create_exchange_type can set default + # handler models — the new @api.constrains on edi.exchange.type requires + # send_model_id (output) and process_model_id (input) to be set. + # Guard prevents double-registration when individual test classes also call + # add_to_registry; pop() in cleanup is safe even if __delitem__ already ran. + if "edi.framework.test.execution" not in cls.registry: + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup( + lambda: cls.registry.__delitem__("edi.framework.test.execution") + if "edi.framework.test.execution" in cls.registry + else None + ) @classmethod def _setup_records(cls): @@ -84,6 +103,19 @@ def _get_backend(cls): @classmethod def _create_exchange_type(cls, **kw): + # Mirror the pattern in edi_component_oca/tests/common.py: provide default + # handler models so every test exchange type satisfies the new constraint. + # Callers can override individual fields by passing explicit values. + if "edi.framework.test.execution" in cls.registry: + handler_model = cls.env["ir.model"]._get("edi.framework.test.execution") + if handler_model: + kw.setdefault("receive_model_id", handler_model.id) + kw.setdefault("generate_model_id", handler_model.id) + kw.setdefault("input_validate_model_id", handler_model.id) + kw.setdefault("output_validate_model_id", handler_model.id) + kw.setdefault("send_model_id", handler_model.id) + kw.setdefault("process_model_id", handler_model.id) + kw.setdefault("check_model_id", handler_model.id) model = cls.env["edi.exchange.type"] vals = { "name": "Test CSV exchange", diff --git a/edi_core_oca/tests/fake_models.py b/edi_core_oca/tests/fake_models.py index df7d5e25c..badea718a 100644 --- a/edi_core_oca/tests/fake_models.py +++ b/edi_core_oca/tests/fake_models.py @@ -33,6 +33,7 @@ def _get_edi_exchange_record_name(self, exchange_record): class EdiTestExecution(models.AbstractModel): _name = "edi.framework.test.execution" _inherit = [ + "edi.oca.handler.generate", "edi.oca.handler.process", "edi.oca.handler.check", "edi.oca.handler.send", diff --git a/edi_core_oca/tests/test_backend_base.py b/edi_core_oca/tests/test_backend_base.py index 87219bea0..dc401f7c0 100644 --- a/edi_core_oca/tests/test_backend_base.py +++ b/edi_core_oca/tests/test_backend_base.py @@ -85,9 +85,15 @@ def test_get_handler_avg_user(self): self._test_get_handler(user=user) def test_get_handler_no_handler(self): - self.exchange_type_in.process_model_id = False - self.exchange_type_in.input_validate_model_id = False - self.exchange_type_in.receive_model_id = False + self.env.cr.execute( + """UPDATE edi_exchange_type + SET process_model_id = NULL, + input_validate_model_id = NULL, + receive_model_id = NULL + WHERE id = %s""", + [self.exchange_type_in.id], + ) + self.exchange_type_in.invalidate_recordset() vals = { "model": self.partner._name, "res_id": self.partner.id, diff --git a/edi_core_oca/views/edi_exchange_type_views.xml b/edi_core_oca/views/edi_exchange_type_views.xml index e7f0be5e1..549494b30 100644 --- a/edi_core_oca/views/edi_exchange_type_views.xml +++ b/edi_core_oca/views/edi_exchange_type_views.xml @@ -63,6 +63,7 @@