From 2bbd8ad3c1a265a9ef3ed28615842f62a436d286 Mon Sep 17 00:00:00 2001 From: aliya Date: Thu, 18 Jan 2024 16:55:14 +0000 Subject: [PATCH] [FIX] account_peppol: peppol_eas compute dependencies 1. set a country like belgium 2. set a VAT number like BE123456789 => peppol_endpoint is not computed This is because some dependencies are missing on the field (current is: "country_code"). Step 1: Add the missing dependencies to the field (and improve the tests to make sure it works). Step 2: Try to make the code smarter! If the country_code is BE but the VAT number starts with FR, consider it's FR, not BE. If the codice_fiscale is set, it's IT whatever the country_code. If the VAT number has no ISO country code, use the country_code in that case. task-3690913 closes odoo/odoo#154375 X-original-commit: 46d2bf0dd08b1a925ad51c91aa37024cd5a6ee16 Signed-off-by: Laurent Smet (las) Signed-off-by: Aliya Tastemirova (alta) --- addons/account/models/partner.py | 14 ++++++ .../account_edi_ubl_cii/models/res_partner.py | 50 ++++++++++++------- .../account_edi_ubl_cii/tests/test_ubl_cii.py | 33 ++++++++++++ addons/l10n_be/models/res_partner.py | 2 +- .../tests/test_xml_oioubl_dk.py | 2 + addons/l10n_fr/models/res_partner.py | 9 ++++ addons/l10n_it_edi/models/res_partner.py | 9 ++++ addons/l10n_no/models/res_partner.py | 9 ++++ addons/l10n_sg/models/res_partner.py | 9 ++++ 9 files changed, 117 insertions(+), 20 deletions(-) diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index 45ad75f184e..4eb3c3cd4c0 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -890,3 +890,17 @@ class ResPartner(models.Model): if self.env['account.move.line'].sudo().search([('move_id.inalterable_hash', '!=', False), ('partner_id', 'in', source.ids)], limit=1): raise UserError(_('Partners that are used in hashed entries cannot be merged.')) return super()._merge_method(destination, source) + + def _deduce_country_code(self): + """ deduce the country code based on the information available. + we have three cases: + - country_code is BE but the VAT number starts with FR, the country code is FR, not BE + - if a country-specific field is set (e.g. the codice_fiscale), that country is used for the country code + - if the VAT number has no ISO country code, use the country_code in that case. + """ + self.ensure_one() + + country_code = self.country_code + if self.vat and self.vat[:2].isalpha(): + country_code = self.vat[:2].upper() + return country_code diff --git a/addons/account_edi_ubl_cii/models/res_partner.py b/addons/account_edi_ubl_cii/models/res_partner.py index c0ce84fd64c..0b23ec264da 100644 --- a/addons/account_edi_ubl_cii/models/res_partner.py +++ b/addons/account_edi_ubl_cii/models/res_partner.py @@ -135,38 +135,49 @@ class ResPartner(models.Model): if error: raise ValidationError(error) - @api.depends('country_code') + @api.model + def _get_ubl_cii_formats(self): + return { + 'DE': 'xrechnung', + 'AU': 'ubl_a_nz', + 'NZ': 'ubl_a_nz', + 'NL': 'nlcius', + 'FR': 'facturx', + 'SG': 'ubl_sg', + } + + def _peppol_eas_endpoint_depends(self): + # field dependencies of methods _compute_peppol_endpoint() and _compute_peppol_eas() + # because we need to extend depends in l10n modules + return ['country_code', 'vat', 'company_registry'] + + @api.depends(lambda self: self._peppol_eas_endpoint_depends()) def _compute_ubl_cii_format(self): + format_mapping = self._get_ubl_cii_formats() for partner in self: - if partner.country_code == 'DE': - partner.ubl_cii_format = 'xrechnung' - elif partner.country_code in ('AU', 'NZ'): - partner.ubl_cii_format = 'ubl_a_nz' - elif partner.country_code == 'NL': - partner.ubl_cii_format = 'nlcius' - elif partner.country_code == 'FR': - partner.ubl_cii_format = 'facturx' - elif partner.country_code == 'SG': - partner.ubl_cii_format = 'ubl_sg' - elif partner.country_code in EAS_MAPPING: + country_code = partner._deduce_country_code() + if country_code in format_mapping: + partner.ubl_cii_format = format_mapping[country_code] + elif country_code in EAS_MAPPING: partner.ubl_cii_format = 'ubl_bis3' else: partner.ubl_cii_format = partner.ubl_cii_format - @api.depends('peppol_eas') + @api.depends(lambda self: self._peppol_eas_endpoint_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: partner.peppol_endpoint = partner.peppol_endpoint - if partner.country_code in EAS_MAPPING: - field = EAS_MAPPING[partner.country_code].get(partner.peppol_eas) + country_code = partner._deduce_country_code() + if country_code in EAS_MAPPING: + field = EAS_MAPPING[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') + @api.depends(lambda self: self._peppol_eas_endpoint_depends()) def _compute_peppol_eas(self): """ If the country_code changes, recompute the EAS only if there is a country_code, it exists in the @@ -174,10 +185,11 @@ class ResPartner(models.Model): """ for partner in self: 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] + country_code = partner._deduce_country_code() + if country_code in EAS_MAPPING: + eas_to_field = EAS_MAPPING[country_code] if partner.peppol_eas not in eas_to_field.keys(): - new_eas = list(EAS_MAPPING[partner.country_code].keys())[0] + new_eas = next(iter(EAS_MAPPING[country_code].keys())) # 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]: diff --git a/addons/account_edi_ubl_cii/tests/test_ubl_cii.py b/addons/account_edi_ubl_cii/tests/test_ubl_cii.py index c8c4fde5501..c5b7e150d06 100644 --- a/addons/account_edi_ubl_cii/tests/test_ubl_cii.py +++ b/addons/account_edi_ubl_cii/tests/test_ubl_cii.py @@ -149,3 +149,36 @@ class TestAccountEdiUblCii(AccountTestInvoicingCommon): .with_context(default_journal_id=self.company_data["default_journal_purchase"].id)\ ._create_document_from_attachment(xml_attachment.id) self.assertEqual(bill.invoice_line_ids.tax_ids, new_tax_2) + + def test_peppol_eas_endpoint_compute(self): + partner = self.partner_a + partner.vat = 'DE123456788' + + self.assertRecordValues(partner, [{ + 'peppol_eas': '9930', + 'peppol_endpoint': 'DE123456788', + }]) + + partner.vat = 'FR23334175221' + + self.assertRecordValues(partner, [{ + 'peppol_eas': '9957', + 'peppol_endpoint': 'FR23334175221', + }]) + + partner.vat = '23334175221' + + self.assertRecordValues(partner, [{ + 'peppol_eas': '9957', + 'peppol_endpoint': 'FR23334175221', + }]) + + partner.write({ + 'vat': 'BE0477472701', + 'company_registry': '0477472701', + }) + + self.assertRecordValues(partner, [{ + 'peppol_eas': '0208', + 'peppol_endpoint': '0477472701', + }]) diff --git a/addons/l10n_be/models/res_partner.py b/addons/l10n_be/models/res_partner.py index b69e610299a..02b19697080 100644 --- a/addons/l10n_be/models/res_partner.py +++ b/addons/l10n_be/models/res_partner.py @@ -14,7 +14,7 @@ class ResPartner(models.Model): # OVERRIDE # If a belgian company has a VAT number then its company registry is its VAT Number (without country code). super()._compute_company_registry() - for partner in self.filtered(lambda p: p.country_id.code == 'BE' and p.vat): + for partner in self.filtered(lambda p: p._deduce_country_code() == 'BE' and p.vat): vat_country, vat_number = self._split_vat(partner.vat) if vat_country.isnumeric(): vat_country = 'be' diff --git a/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py b/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py index a83fef9d70a..84b2686614e 100644 --- a/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py +++ b/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py @@ -173,6 +173,7 @@ class TestUBLDK(TestUBLCommon, TestAccountMoveSendCommon): @freeze_time('2017-01-01') def test_export_partner_fr_without_siret_should_raise_an_error(self): self.partner_c.company_registry = False + self.partner_c.ubl_cii_format = 'oioubl_201' # default format for French partners is facturx with self.assertRaisesRegex(UserError, "The company registry is required for french partner:"): self.create_post_and_send_invoice(partner=self.partner_c) @@ -183,6 +184,7 @@ class TestUBLDK(TestUBLCommon, TestAccountMoveSendCommon): telling to the user that this field is missing. """ self.partner_b.vat = None + self.partner_b.ubl_cii_format = 'oioubl_201' # default format recomputes when vat is changed with self.assertRaises(UserError) as exception: self.create_post_and_send_invoice(partner=self.partner_b) self.assertIn(f"The field '{self.partner_b._fields['vat'].string}' is required", exception.exception.args[0]) diff --git a/addons/l10n_fr/models/res_partner.py b/addons/l10n_fr/models/res_partner.py index 0c0d734145f..73b7de06a2f 100644 --- a/addons/l10n_fr/models/res_partner.py +++ b/addons/l10n_fr/models/res_partner.py @@ -7,3 +7,12 @@ class ResPartner(models.Model): _inherit = 'res.partner' siret = fields.Char(string='SIRET', size=14) + + def _deduce_country_code(self): + if self.siret: + return 'FR' + return super()._deduce_country_code() + + def _peppol_eas_endpoint_depends(self): + # extends account_edi_ubl_cii + return super()._peppol_eas_endpoint_depends() + ['siret'] diff --git a/addons/l10n_it_edi/models/res_partner.py b/addons/l10n_it_edi/models/res_partner.py index 9d5089aa1e8..c86daa948c7 100644 --- a/addons/l10n_it_edi/models/res_partner.py +++ b/addons/l10n_it_edi/models/res_partner.py @@ -183,3 +183,12 @@ class ResPartner(models.Model): 'action': invalid_records._get_records_action(name=_("Check Partner(s)"), views=views), } return errors + + def _deduce_country_code(self): + if self.l10n_it_codice_fiscale: + return 'IT' + return super()._deduce_country_code() + + def _peppol_eas_endpoint_depends(self): + # extends account_edi_ubl_cii + return super()._peppol_eas_endpoint_depends() + ['l10n_it_codice_fiscale'] diff --git a/addons/l10n_no/models/res_partner.py b/addons/l10n_no/models/res_partner.py index c1f1b1c0553..059374b6c47 100644 --- a/addons/l10n_no/models/res_partner.py +++ b/addons/l10n_no/models/res_partner.py @@ -6,3 +6,12 @@ class ResPartner(models.Model): _inherit = 'res.partner' l10n_no_bronnoysund_number = fields.Char(string='Register of Legal Entities (Brønnøysund Register Center)', size=9) + + def _deduce_country_code(self): + if self.l10n_no_bronnoysund_number: + return 'NO' + return super()._deduce_country_code() + + def _peppol_eas_endpoint_depends(self): + # extends account_edi_ubl_cii + return super()._peppol_eas_endpoint_depends() + ['l10n_no_bronnoysund_number'] diff --git a/addons/l10n_sg/models/res_partner.py b/addons/l10n_sg/models/res_partner.py index 1bf9802a9a5..086824bbf20 100644 --- a/addons/l10n_sg/models/res_partner.py +++ b/addons/l10n_sg/models/res_partner.py @@ -7,3 +7,12 @@ class ResPartner(models.Model): _inherit = 'res.partner' l10n_sg_unique_entity_number = fields.Char(string='UEN') + + def _deduce_country_code(self): + if self.l10n_sg_unique_entity_number: + return 'SG' + return super()._deduce_country_code() + + def _peppol_eas_endpoint_depends(self): + # extends account_edi_ubl_cii + return super()._peppol_eas_endpoint_depends() + ['l10n_sg_unique_entity_number']