[FIX] l10n_ro_edi: country code prefix on VAT / company_registry
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 <rule context="//cac:PartyTaxScheme[cac:TaxScheme/normalize-space(upper-case(cbc:ID))='VAT']"> <assert id="BR-CO-09" flag="fatal" test="( contains( ' 1A AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH EL ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS XI YE YT ZA ZM ZW ',substring(cbc:CompanyID,1,2) ) )">[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’.</assert> </rule> ``` 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) <bib@odoo.com>
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
||||
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:efactura.mfinante.ro:CIUS-RO:1.0.1</cbc:CustomizationID>
|
||||
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
|
||||
<cbc:ID>INV/2017/00001</cbc:ID>
|
||||
<cbc:IssueDate>2017-01-01</cbc:IssueDate>
|
||||
<cbc:DueDate>2017-02-28</cbc:DueDate>
|
||||
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
|
||||
<cbc:Note>test narration</cbc:Note>
|
||||
<cbc:DocumentCurrencyCode>RON</cbc:DocumentCurrencyCode>
|
||||
<cbc:TaxCurrencyCode>RON</cbc:TaxCurrencyCode>
|
||||
<cbc:BuyerReference>ref_partner_a</cbc:BuyerReference>
|
||||
<cac:OrderReference>
|
||||
<cbc:ID>ref_move</cbc:ID>
|
||||
</cac:OrderReference>
|
||||
<cac:AdditionalDocumentReference>
|
||||
<cbc:ID>INV_2017_00001.pdf</cbc:ID>
|
||||
<cac:Attachment>
|
||||
<cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="INV_2017_00001.pdf">___ignore___</cbc:EmbeddedDocumentBinaryObject>
|
||||
</cac:Attachment>
|
||||
</cac:AdditionalDocumentReference>
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="9947">___ignore___</cbc:EndpointID>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Hudson Construction</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:StreetName>Strada Kunst, 3</cbc:StreetName>
|
||||
<cbc:CityName>SECTOR1</cbc:CityName>
|
||||
<cbc:PostalZone>010101</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RO-B</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>RO</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID>1234567897</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>NO_VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>Hudson Construction</cbc:RegistrationName>
|
||||
<cbc:CompanyID>1234567897</cbc:CompanyID>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Name>Hudson Construction</cbc:Name>
|
||||
<cbc:Telephone>+40 123 456 789</cbc:Telephone>
|
||||
</cac:Contact>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:AccountingCustomerParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="9947">___ignore___</cbc:EndpointID>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Roasted Romanian Roller</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:StreetName>Rolling Roast, 88</cbc:StreetName>
|
||||
<cbc:CityName>SECTOR3</cbc:CityName>
|
||||
<cbc:PostalZone>010101</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RO-B</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>RO</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID>1234567897</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>NO_VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>Roasted Romanian Roller</cbc:RegistrationName>
|
||||
<cbc:CompanyID>1234567897</cbc:CompanyID>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Name>Roasted Romanian Roller</cbc:Name>
|
||||
<cbc:Telephone>+40 123 456 780</cbc:Telephone>
|
||||
</cac:Contact>
|
||||
</cac:Party>
|
||||
</cac:AccountingCustomerParty>
|
||||
<cac:Delivery>
|
||||
<cac:DeliveryLocation>
|
||||
<cac:Address>
|
||||
<cbc:StreetName>Rolling Roast, 88</cbc:StreetName>
|
||||
<cbc:CityName>SECTOR3</cbc:CityName>
|
||||
<cbc:PostalZone>010101</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RO-B</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>RO</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:Address>
|
||||
</cac:DeliveryLocation>
|
||||
</cac:Delivery>
|
||||
<cac:PaymentMeans>
|
||||
<cbc:PaymentMeansCode name="credit transfer">30</cbc:PaymentMeansCode>
|
||||
<cbc:PaymentID>INV/2017/00001</cbc:PaymentID>
|
||||
<cac:PayeeFinancialAccount>
|
||||
<cbc:ID>RO98RNCB1234567890123456</cbc:ID>
|
||||
</cac:PayeeFinancialAccount>
|
||||
</cac:PaymentMeans>
|
||||
<cac:PaymentTerms>
|
||||
<cbc:Note>Payment terms: 30% Advance End of Following Month</cbc:Note>
|
||||
</cac:PaymentTerms>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="RON">285.00</cbc:TaxAmount>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="RON">1500.00</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="RON">285.00</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID>S</cbc:ID>
|
||||
<cbc:Percent>19.0</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
</cac:TaxTotal>
|
||||
<cac:LegalMonetaryTotal>
|
||||
<cbc:LineExtensionAmount currencyID="RON">1500.00</cbc:LineExtensionAmount>
|
||||
<cbc:TaxExclusiveAmount currencyID="RON">1500.00</cbc:TaxExclusiveAmount>
|
||||
<cbc:TaxInclusiveAmount currencyID="RON">1785.00</cbc:TaxInclusiveAmount>
|
||||
<cbc:PrepaidAmount currencyID="RON">0.00</cbc:PrepaidAmount>
|
||||
<cbc:PayableAmount currencyID="RON">1785.00</cbc:PayableAmount>
|
||||
</cac:LegalMonetaryTotal>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>___ignore___</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="C62">1.0</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="RON">500.00</cbc:LineExtensionAmount>
|
||||
<cac:Item>
|
||||
<cbc:Description>product_a</cbc:Description>
|
||||
<cbc:Name>product_a</cbc:Name>
|
||||
<cac:ClassifiedTaxCategory>
|
||||
<cbc:ID>S</cbc:ID>
|
||||
<cbc:Percent>19.0</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="RON">500.0</cbc:PriceAmount>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>___ignore___</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="DZN">1.0</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="RON">1000.00</cbc:LineExtensionAmount>
|
||||
<cac:Item>
|
||||
<cbc:Description>product_b</cbc:Description>
|
||||
<cbc:Name>product_b</cbc:Name>
|
||||
<cac:ClassifiedTaxCategory>
|
||||
<cbc:ID>S</cbc:ID>
|
||||
<cbc:Percent>19.0</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID>VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="RON">1000.0</cbc:PriceAmount>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
</Invoice>
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user