[FIX] account_edi_ubl_cii: fix peppol fields computes & update BE EAS

Replace every mentions of 9925 to 0208 for Belgian companies (public
administration also have this 10 digits number). The EAS 0208
corresponds to the company_registry.

Introduce a mechanism to validate the `peppol_endpoint` and refactor the
computes for the `peppol_endpoint` and `peppol_eas` fields.

See: https://openpeppol.atlassian.net/wiki/spaces/Belgium/overview

task-3297311

closes odoo/odoo#138343

X-original-commit: 744bfa9e5f087a2aa9d714351a559c3320ecfda4
Signed-off-by: Laurent Smet (las) <las@odoo.com>
Signed-off-by: Julien Van Roy (juvr) <juvr@odoo.com>
This commit is contained in:
Julien Van Roy
2023-10-11 18:03:22 +00:00
parent 08a561d588
commit 484ab5dfcb
7 changed files with 148 additions and 28 deletions
@@ -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
@@ -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'},
@@ -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()
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import test_partner_peppol_fields
@@ -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'))
@@ -14,7 +14,7 @@
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="9925">BE0202239951</cbc:EndpointID>
<cbc:EndpointID schemeID="0208">0202239951</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>partner_1</cbc:Name>
</cac:PartyName>
@@ -43,7 +43,7 @@
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="9925">BE0477472701</cbc:EndpointID>
<cbc:EndpointID schemeID="0208">0477472701</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>partner_2</cbc:Name>
</cac:PartyName>
@@ -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,
})