From 95a9cd2a46b3de76c87b751918ba56da39471edd Mon Sep 17 00:00:00 2001 From: Yosua Nicolaus Date: Fri, 23 Feb 2024 16:29:40 +0100 Subject: [PATCH] [FIX] l10n_ro_edi: country code prefix on VAT / company_registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit makes the CIUS-RO e-invoice emitted by a Romanian company without VAT number compliant with the CIUS-RO requirements. The validator for CIUS-RO (which extends the validations from the BIS3 Schematron) asserts a rule `[BR-CO-09]` where: if the PartyTaxScheme/TaxScheme/ID == 'VAT', CompanyID must start with a country code prefix. In Romania however, there are multiple types of "Tax IDs", and it is perfectly valid in Romania to have a Tax ID without RO (country code prefix) in front of them. They are not a subject to paying VAT, and it should still be possible to generate CIUS-RO XML with their tax identifications. We have to handle their cases by changing the TaxScheme/ID to 'something other than VAT', preventing the trigger of the rule and allow Romanian companies without prefixed VAT to use CIUS-RO. ```xml [BR-CO-09]-The Seller VAT identifier (BT-31), the Seller tax representative VAT identifier (BT-63) and the Buyer VAT identifier (BT-48) shall have a prefix in accordance with ISO code ISO 3166-1 alpha-2 by which the country of issue may be identified. Nevertheless, Greece may use the prefix ‘EL’. ``` This commit also fixes and clean some of the irrelevant constraints and tests previously written in `l10n_ro_edi`. closes odoo/odoo#155252 Task-id: 3649426 Signed-off-by: Brice Bartoletti (bib) --- .../models/account_edi_xml_ubl_ciusro.py | 23 ++- .../ciusro_out_invoice_no_prefix_vat.xml | 166 ++++++++++++++++++ addons/l10n_ro_edi/tests/test_xml_ubl_ro.py | 40 +++-- 3 files changed, 202 insertions(+), 27 deletions(-) create mode 100644 addons/l10n_ro_edi/tests/test_files/from_odoo/ciusro_out_invoice_no_prefix_vat.xml diff --git a/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py b/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py index 8ec19ea3e6e..e3d94cb2c64 100644 --- a/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py +++ b/addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py @@ -42,12 +42,17 @@ class AccountEdiXmlUBLRO(models.AbstractModel): vals_list = super()._get_partner_party_tax_scheme_vals_list(partner, role) if not partner.vat and partner.company_registry: - return [{ - 'company_id': partner.company_registry, - 'tax_scheme_vals': { - 'id': 'VAT', - }, - }] + # Use company_registry (Company ID) as the VAT replacement + vals_list = [{'company_id': partner.company_registry, 'tax_scheme_vals': {'id': 'VAT'}}] + + # The validator for CIUS-RO (which extends the validations from the BIS3 Schematron) asserts a rule where: + # [BR-CO-09] if the PartyTaxScheme/TaxScheme/ID == 'VAT', CompanyID must start with a country code prefix. + # In Romania however, the CompanyID can be with or without country code prefix and still be perfectly valid. + # We have to handle their cases by changing the TaxScheme/ID to 'something other than VAT', + # preventing the trigger of the rule and allow Romanian companies without prefixed VAT to use CIUS-RO. + for vals in vals_list: + if partner.country_code == 'RO' and not vals['company_id'].upper().startswith('RO'): + vals['tax_scheme_vals']['id'] = 'NO_VAT' return vals_list @@ -98,12 +103,6 @@ class AccountEdiXmlUBLRO(models.AbstractModel): "At least one of them is required. ", partner.name) - if (not partner.vat and partner.company_registry - and not partner.company_registry.startswith(partner.country_code)): - constraints[f"ciusro_{partner_type}_country_code_company_registry_required"] = _( - "The following partner's doesn't have a country code prefix in their Company ID: %s.", - partner.name) - if (partner.country_code == 'RO' and partner.state_id and partner.state_id.code == 'B' diff --git a/addons/l10n_ro_edi/tests/test_files/from_odoo/ciusro_out_invoice_no_prefix_vat.xml b/addons/l10n_ro_edi/tests/test_files/from_odoo/ciusro_out_invoice_no_prefix_vat.xml new file mode 100644 index 00000000000..bc730648a94 --- /dev/null +++ b/addons/l10n_ro_edi/tests/test_files/from_odoo/ciusro_out_invoice_no_prefix_vat.xml @@ -0,0 +1,166 @@ + + + urn:cen.eu:en16931:2017#compliant#urn:efactura.mfinante.ro:CIUS-RO:1.0.1 + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + INV/2017/00001 + 2017-01-01 + 2017-02-28 + 380 + test narration + RON + RON + ref_partner_a + + ref_move + + + INV_2017_00001.pdf + + ___ignore___ + + + + + ___ignore___ + + Hudson Construction + + + Strada Kunst, 3 + SECTOR1 + 010101 + RO-B + + RO + + + + 1234567897 + + NO_VAT + + + + Hudson Construction + 1234567897 + + + Hudson Construction + +40 123 456 789 + + + + + + ___ignore___ + + Roasted Romanian Roller + + + Rolling Roast, 88 + SECTOR3 + 010101 + RO-B + + RO + + + + 1234567897 + + NO_VAT + + + + Roasted Romanian Roller + 1234567897 + + + Roasted Romanian Roller + +40 123 456 780 + + + + + + + Rolling Roast, 88 + SECTOR3 + 010101 + RO-B + + RO + + + + + + 30 + INV/2017/00001 + + RO98RNCB1234567890123456 + + + + Payment terms: 30% Advance End of Following Month + + + 285.00 + + 1500.00 + 285.00 + + S + 19.0 + + VAT + + + + + + 1500.00 + 1500.00 + 1785.00 + 0.00 + 1785.00 + + + ___ignore___ + 1.0 + 500.00 + + product_a + product_a + + S + 19.0 + + VAT + + + + + 500.0 + + + + ___ignore___ + 1.0 + 1000.00 + + product_b + product_b + + S + 19.0 + + VAT + + + + + 1000.0 + + + diff --git a/addons/l10n_ro_edi/tests/test_xml_ubl_ro.py b/addons/l10n_ro_edi/tests/test_xml_ubl_ro.py index 1dc0f6ed518..c830f6bb76c 100644 --- a/addons/l10n_ro_edi/tests/test_xml_ubl_ro.py +++ b/addons/l10n_ro_edi/tests/test_xml_ubl_ro.py @@ -97,29 +97,39 @@ class TestUBLRO(TestUBLCommon): self._assert_invoice_attachment(attachment, xpaths=None, expected_file_path='from_odoo/ciusro_out_invoice_different_currency.xml') self.currency_data['currency'] = self.env.ref('base.RON') - def test_export_no_vat_but_have_company_id_without_prefix(self): - self.company_data['company'].write({ - 'vat': None, - 'company_registry': '1234567897', - }) - invoice = self.create_move("out_invoice", send=False) - with self.assertRaisesRegex(UserError, "doesn't have a country code prefix in their Company ID"): - invoice._generate_pdf_and_send_invoice(self.move_template, allow_fallback_pdf=False) + def test_export_invoice_without_country_code_prefix_in_vat(self): + self.company_data['company'].write({'vat': '1234567897'}) + self.partner_a.write({'vat': '1234567897'}) + invoice = self.create_move("out_invoice") + attachment = self.get_attachment(invoice) + self._assert_invoice_attachment(attachment, xpaths=None, expected_file_path='from_odoo/ciusro_out_invoice_no_prefix_vat.xml') - def test_export_no_vat_but_have_company_id_with_prefix(self): - self.company_data['company'].write({ - 'vat': None, - 'company_registry': 'RO1234567897', - }) + def test_export_no_vat_but_have_company_registry(self): + self.company_data['company'].write({'vat': False, 'company_registry': 'RO1234567897'}) + self.partner_a.write({'vat': False, 'company_registry': 'RO1234567897'}) invoice = self.create_move("out_invoice") attachment = self.get_attachment(invoice) self._assert_invoice_attachment(attachment, xpaths=None, expected_file_path='from_odoo/ciusro_out_invoice.xml') + def test_export_no_vat_but_have_company_registry_without_prefix(self): + self.company_data['company'].write({'vat': False, 'company_registry': '1234567897'}) + self.partner_a.write({'vat': False, 'company_registry': '1234567897'}) + invoice = self.create_move("out_invoice") + attachment = self.get_attachment(invoice) + self._assert_invoice_attachment(attachment, xpaths=None, expected_file_path='from_odoo/ciusro_out_invoice_no_prefix_vat.xml') + + def test_export_no_vat_and_no_company_registry_raises_error(self): + self.company_data['company'].write({'vat': False, 'company_registry': False}) + self.partner_a.write({'vat': False, 'company_registry': False}) + invoice = self.create_move("out_invoice", send=False) + with self.assertRaisesRegex(UserError, "doesn't have a VAT nor Company ID"): + invoice._generate_pdf_and_send_invoice(self.move_template, allow_fallback_pdf=False) + def test_export_constraints(self): - self.company_data['company'].company_registry = None + self.company_data['company'].company_registry = False for required_field in ('city', 'street', 'state_id', 'vat'): prev_val = self.company_data["company"][required_field] - self.company_data["company"][required_field] = None + self.company_data["company"][required_field] = False invoice = self.create_move("out_invoice", send=False) with self.assertRaisesRegex(UserError, "required"): invoice._generate_pdf_and_send_invoice(self.move_template, allow_fallback_pdf=False)