Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions edi_core_oca/models/edi_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]
Expand All @@ -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),
]
Expand Down Expand Up @@ -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),
]
Expand All @@ -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:
Expand Down
26 changes: 26 additions & 0 deletions edi_core_oca/models/edi_exchange_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
32 changes: 32 additions & 0 deletions edi_core_oca/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import os

from odoo.orm.model_classes import add_to_registry
from odoo.tests.common import TransactionCase


Expand All @@ -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):
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions edi_core_oca/tests/fake_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 9 additions & 3 deletions edi_core_oca/tests/test_backend_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions edi_core_oca/views/edi_exchange_type_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<field
name="process_model_id"
options="{'no_create': True}"
required="direction == 'input'"
/>
<field
name="input_validate_model_id"
Expand All @@ -76,10 +77,12 @@
<field
name="generate_model_id"
options="{'no_create': True}"
required="direction == 'output' and exchange_file_auto_generate"
/>
<field
name="send_model_id"
options="{'no_create': True}"
required="direction == 'output'"
/>
<field
name="output_validate_model_id"
Expand Down
18 changes: 18 additions & 0 deletions edi_endpoint_oca/tests/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2026 Camptocamp SA
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo.orm.model_classes import add_to_registry
from odoo.tests.common import TransactionCase


Expand All @@ -13,6 +14,21 @@ 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 so _get_exchange_type can satisfy the
# _check_direction_handlers constraint (input types need process_model_id).
if "edi.framework.test.execution" not in cls.registry:
from odoo.addons.edi_core_oca.tests.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):
Expand Down Expand Up @@ -75,12 +91,14 @@ def _get_backend(cls):

@classmethod
def _get_exchange_type(cls):
handler_model = cls.env["ir.model"]._get("edi.framework.test.execution")
return cls.env["edi.exchange.type"].create(
{
"name": "EDI exchange demo",
"code": "demo_endpoint",
"backend_type_id": cls.backend_type.id,
"direction": "input",
"process_model_id": handler_model.id,
}
)

Expand Down
16 changes: 16 additions & 0 deletions edi_queue_oca/tests/test_backend_output_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


from odoo.orm.model_classes import add_to_registry
from odoo.tests import tagged

from odoo.addons.edi_core_oca.tests.common import EDIBackendCommonTestCase
Expand All @@ -12,6 +13,21 @@

@tagged("-at_install", "post_install")
class EDIBackendTestOutputJobsCase(EDIBackendCommonTestCase):
@classmethod
def _setup_records(cls): # pylint:disable=missing-return
super()._setup_records()
from odoo.addons.edi_core_oca.tests.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(cls.registry.__delitem__, "edi.framework.test.execution")
model = cls.env["ir.model"]._get("edi.framework.test.execution")
cls.exchange_type_out.generate_model_id = model
cls.exchange_type_out.send_model_id = model

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
Loading