diff --git a/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot b/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot
index 89604145bf6..b3a5c6f8357 100644
--- a/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot
+++ b/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot
@@ -780,6 +780,21 @@ msgstr ""
msgid "Tax '%s' is invalid: %s"
msgstr ""
+#. module: account_edi_ubl_cii
+#. odoo-python
+#: code:addons/account_edi_ubl_cii/models/res_partner.py:0
+#, python-format
+msgid "The Peppol endpoint is not valid. The expected format is: 0239843188"
+msgstr ""
+
+#. module: account_edi_ubl_cii
+#. odoo-python
+#: code:addons/account_edi_ubl_cii/models/res_partner.py:0
+#, python-format
+msgid ""
+"The Peppol endpoint is not valid. The expected format is: 73282932000074"
+msgstr ""
+
#. module: account_edi_ubl_cii
#. odoo-python
#: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_bis3.py:0
diff --git a/addons/account_edi_ubl_cii/models/account_edi_common.py b/addons/account_edi_ubl_cii/models/account_edi_common.py
index 790e51bceb6..4b9e78d914f 100644
--- a/addons/account_edi_ubl_cii/models/account_edi_common.py
+++ b/addons/account_edi_ubl_cii/models/account_edi_common.py
@@ -44,7 +44,7 @@ EAS_MAPPING = {
'AT': {'9915': 'vat'},
'AU': {'0151': 'vat'},
'BA': {'9924': 'vat'},
- 'BE': {'9925': 'vat', '0208': 'company_registry'},
+ 'BE': {'0208': 'company_registry'},
'BG': {'9926': 'vat'},
'CH': {'9927': 'vat'},
'CY': {'9928': 'vat'},
@@ -54,7 +54,7 @@ EAS_MAPPING = {
'EE': {'9931': 'vat'},
'ES': {'9920': 'vat'},
'FI': {'0213': 'vat'},
- 'FR': {'9957': 'vat', '0009': 'siret'},
+ 'FR': {'0009': 'siret', '9957': 'vat'},
'SG': {'0195': 'l10n_sg_unique_entity_number'},
'GB': {'9932': 'vat'},
'GR': {'9933': 'vat'},
diff --git a/addons/account_edi_ubl_cii/models/res_partner.py b/addons/account_edi_ubl_cii/models/res_partner.py
index b5c2c9159fb..92ad2cabe78 100644
--- a/addons/account_edi_ubl_cii/models/res_partner.py
+++ b/addons/account_edi_ubl_cii/models/res_partner.py
@@ -1,7 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import models, fields, api
+import re
+from stdnum.fr import siret
+
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
from odoo.addons.account_edi_ubl_cii.models.account_edi_common import EAS_MAPPING
@@ -121,6 +125,14 @@ class ResPartner(models.Model):
]
)
+ @api.constrains('peppol_endpoint')
+ def _check_peppol_fields(self):
+ for partner in self:
+ if partner.peppol_endpoint and partner.peppol_eas:
+ error = self._build_error_peppol_endpoint(partner.peppol_eas, partner.peppol_endpoint)
+ if error:
+ raise ValidationError(error)
+
@api.depends('country_code')
def _compute_ubl_cii_format(self):
for partner in self:
@@ -139,35 +151,45 @@ class ResPartner(models.Model):
else:
partner.ubl_cii_format = partner.ubl_cii_format
- @api.depends('country_code')
+ @api.depends('peppol_eas')
def _compute_peppol_endpoint(self):
+ """ If the EAS changes and a valid endpoint is available, set it. Otherwise, keep the existing value."""
for partner in self:
- if partner.ubl_cii_format != 'facturx' and partner.country_code in EAS_MAPPING:
- eas_to_field = EAS_MAPPING[partner.country_code]
- # Try to set both the peppol_eas and the peppol_endpoint
- for field_name in eas_to_field.values():
- if field_name and field_name in partner._fields and partner[field_name]:
- partner.peppol_endpoint = partner[field_name]
- # If it's not possible to set the peppol_endpoint, just set the peppol_eas
- if not partner.peppol_endpoint:
- partner.peppol_endpoint = partner.peppol_endpoint
- else:
- partner.peppol_endpoint = partner.peppol_endpoint
+ partner.peppol_endpoint = partner.peppol_endpoint
+ if partner.country_code in EAS_MAPPING:
+ field = EAS_MAPPING[partner.country_code].get(partner.peppol_eas)
+ if field \
+ and field in partner._fields \
+ and partner[field] \
+ and not partner._build_error_peppol_endpoint(partner.peppol_eas, partner[field]):
+ partner.peppol_endpoint = partner[field]
@api.depends('country_code')
def _compute_peppol_eas(self):
+ """
+ If the country_code changes, recompute the EAS only if there is a country_code, it exists in the
+ EAS_MAPPING, and the current EAS is not consistent with the new country_code.
+ """
for partner in self:
- if partner.ubl_cii_format != 'facturx' and partner.country_code in EAS_MAPPING:
+ partner.peppol_eas = partner.peppol_eas
+ if partner.country_code and partner.country_code in EAS_MAPPING:
eas_to_field = EAS_MAPPING[partner.country_code]
- # Try to set both the peppol_eas and the peppol_endpoint
- for eas, field_name in eas_to_field.items():
- if field_name in partner._fields and partner[field_name]:
- partner.peppol_eas = eas
- # If it's not possible to set the peppol_endpoint, just set the peppol_eas
- if not partner.peppol_eas:
- partner.peppol_eas = list(eas_to_field.keys())[0]
- else:
- partner.peppol_eas = partner.peppol_eas
+ if partner.peppol_eas not in eas_to_field.keys():
+ new_eas = list(EAS_MAPPING[partner.country_code].keys())[0]
+ # Iterate on the possible EAS until a valid one is found
+ for eas, field in eas_to_field.items():
+ if field and field in partner._fields and partner[field]:
+ if not partner._build_error_peppol_endpoint(eas, partner[field]):
+ new_eas = eas
+ break
+ partner.peppol_eas = new_eas
+
+ def _build_error_peppol_endpoint(self, eas, endpoint):
+ """ This function contains all the rules regarding the peppol_endpoint."""
+ if eas == '0208' and not re.match(r"^\d{10}$", endpoint):
+ return _("The Peppol endpoint is not valid. The expected format is: 0239843188")
+ if eas == '0009' and not siret.is_valid(endpoint):
+ return _("The Peppol endpoint is not valid. The expected format is: 73282932000074")
def _get_edi_builder(self):
self.ensure_one()
diff --git a/addons/account_edi_ubl_cii/tests/__init__.py b/addons/account_edi_ubl_cii/tests/__init__.py
new file mode 100644
index 00000000000..5b6a56d0275
--- /dev/null
+++ b/addons/account_edi_ubl_cii/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import test_partner_peppol_fields
diff --git a/addons/account_edi_ubl_cii/tests/test_partner_peppol_fields.py b/addons/account_edi_ubl_cii/tests/test_partner_peppol_fields.py
new file mode 100644
index 00000000000..4febe29ff46
--- /dev/null
+++ b/addons/account_edi_ubl_cii/tests/test_partner_peppol_fields.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+
+from contextlib import contextmanager
+from unittest.mock import patch
+
+from odoo.tests import tagged
+from odoo.addons.account.tests.common import AccountTestInvoicingCommon
+
+
+@tagged('post_install', '-at_install')
+class TestAccountUblCii(AccountTestInvoicingCommon):
+
+ @contextmanager
+ def check_peppol_vals(self, partner, expected, reset=True):
+ if reset:
+ partner.write({
+ 'country_id': False,
+ 'peppol_eas': False,
+ 'peppol_endpoint': False,
+ })
+ yield
+ partner.country_id = self.env.ref('base.dk')
+ self.assertEqual((partner.peppol_eas, partner.peppol_endpoint), expected)
+
+ def _build_error_peppol_endpoint(self, eas, endpoint):
+ """ Mock _build_error_peppol_endpoint"""
+ if eas == "0184" and endpoint != "digstorg":
+ return f"(0184, {endpoint}) is not a valid peppol couple."
+
+ @patch(
+ 'odoo.addons.account_edi_ubl_cii.models.res_partner.ResPartner._build_error_peppol_endpoint',
+ _build_error_peppol_endpoint,
+ )
+ def test_peppol_eas_endpoint(self):
+ # NB: the EAS_MAPPING for DK is: {'DK': {'0184': 'company_registry', '0198': 'vat'}}
+ partner = self.company_data['company'].partner_id
+
+ partner.company_registry = "digstorg"
+ partner.vat = "DK12345674"
+
+ # Base case -> (0184, company_registry)
+ with self.check_peppol_vals(partner, expected=("0184", partner.company_registry)):
+ pass
+
+ # No company_registry -> (0198, vat)
+ with self.check_peppol_vals(partner, expected=("0198", partner.vat)):
+ partner.company_registry = False
+
+ # Invalid company_registry -> (0198, vat)
+ with self.check_peppol_vals(partner, expected=("0198", partner.vat)):
+ partner.company_registry = "turlututu"
+
+ # No company_registry nor vat -> (0184, False)
+ with self.check_peppol_vals(partner, expected=("0184", False)):
+ partner.write({
+ 'company_registry': False,
+ 'vat': False,
+ })
+
+ # Create a partner, fill the peppol fields, then set the country
+ partner_1 = self.env['res.partner'].create({
+ 'name': "A new partner",
+ 'peppol_eas': '0184',
+ 'peppol_endpoint': 'digstorg'
+ })
+ with self.check_peppol_vals(partner_1, expected=("0184", 'digstorg'), reset=False):
+ pass
+
+ # Create a partner, set the country, then fill the peppol fields
+ partner_2 = self.env['res.partner'].create({
+ 'name': "A new partner",
+ 'country_id': self.env.ref('base.dk').id,
+ })
+ with self.check_peppol_vals(partner_2, expected=("0184", 'digstorg'), reset=False):
+ partner_2.peppol_eas = '0184'
+ partner_2.peppol_endpoint = 'digstorg'
+
+ # Change the country, the EAS changes but we do not overwrite the existing endpoint
+ partner_2.country_id = self.env.ref('base.be')
+ self.assertEqual((partner_2.peppol_eas, partner_2.peppol_endpoint), ('0208', 'digstorg'))
diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/bis3_out_invoice_no_prices.xml b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/bis3_out_invoice_no_prices.xml
index b1086f5c5cb..fa7629599b4 100644
--- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/bis3_out_invoice_no_prices.xml
+++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_files/from_odoo/bis3_out_invoice_no_prices.xml
@@ -14,7 +14,7 @@
- BE0202239951
+ 0202239951
partner_1
@@ -43,7 +43,7 @@
- BE0477472701
+ 0477472701
partner_2
diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py
index a2749457576..028ca96bbb6 100644
--- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py
+++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py
@@ -238,8 +238,8 @@ class TestUBLBE(TestUBLCommon, TestAccountMoveSendCommon):
# Setup a public admin in Luxembourg
self.partner_2.write({
'vat': None,
- 'peppol_endpoint': '00005000041',
'peppol_eas': '9938',
+ 'peppol_endpoint': '00005000041',
'country_id': self.env.ref('base.lu').id,
})