[ADD] l10n_ro_edi: CIUS-RO export

Description of the issue/feature this PR addresses:
- In Romania, generating and communicating XML invoices to the government is a requirement. This format is based on UBL/CII for which we already have a very strong base.
- This task aims at completing the localisation with a first step : make Odoo generate the required Romanian UBL format if activated
- Official documentation: https://mfinante.gov.ro/web/efactura/informatii-tehnice

Current behavior before PR:
- CIUS_RO XML export is not supported yet

Desired behavior after PR is merged:
- CIUS_RO XML export will be supported

closes odoo/odoo#130617

Task-id: 3388222
Signed-off-by: Laurent Smet (las) <las@odoo.com>
This commit is contained in:
Yosua Nicolaus
2023-09-19 16:37:12 +00:00
parent 3312b99978
commit 1479982247
12 changed files with 948 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
from . import models
+20
View File
@@ -0,0 +1,20 @@
{
'author': 'Odoo',
'name': 'Romania - E-invoicing',
'version': '1.0',
'category': 'Accounting/Localizations/EDI',
'description': """
E-invoice implementation for Romania
""",
'summary': """
E-Invoice implementation for Romania
""",
'countries': ['ro'],
'depends': [
'account_edi_ubl_cii',
'l10n_ro',
],
'installable': True,
'auto_install': True,
'license': 'LGPL-3',
}
+74
View File
@@ -0,0 +1,74 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_ro_edi
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.5alpha1+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-31 13:31+0000\n"
"PO-Revision-Date: 2023-08-31 13:31+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_ro_edi
#: model:ir.model,name:l10n_ro_edi.model_account_edi_xml_ubl_ro
msgid "CIUS RO"
msgstr ""
#. module: l10n_ro_edi
#: model:ir.model.fields.selection,name:l10n_ro_edi.selection__res_partner__ubl_cii_format__ciusro
msgid "CIUSRO"
msgstr ""
#. module: l10n_ro_edi
#: model:ir.model,name:l10n_ro_edi.model_res_partner
msgid "Contact"
msgstr ""
#. module: l10n_ro_edi
#: model:ir.model.fields,field_description:l10n_ro_edi.field_res_partner__ubl_cii_format
#: model:ir.model.fields,field_description:l10n_ro_edi.field_res_users__ubl_cii_format
msgid "Format"
msgstr ""
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner doesn't have a VAT nor Company ID: %s. At least one of"
" them is required. "
msgstr ""
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's city name is invalid: %s. If partner's state is "
"București, the city name must be 'SECTORX', where X is a number between 1-6."
" "
msgstr ""
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's doesn't have a country code prefix in their Company "
"ID: %s. "
msgstr ""
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's doesn't have a country code prefix in their VAT: %s."
" "
msgstr ""
+80
View File
@@ -0,0 +1,80 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_ro_edi
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.5alpha1+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-08-31 13:32+0000\n"
"PO-Revision-Date: 2023-08-31 13:32+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_ro_edi
#: model:ir.model,name:l10n_ro_edi.model_account_edi_xml_ubl_ro
msgid "CIUS RO"
msgstr "CIUS RO"
#. module: l10n_ro_edi
#: model:ir.model.fields.selection,name:l10n_ro_edi.selection__res_partner__ubl_cii_format__ciusro
msgid "CIUSRO"
msgstr "CIUSRO"
#. module: l10n_ro_edi
#: model:ir.model,name:l10n_ro_edi.model_res_partner
msgid "Contact"
msgstr "A lua legatura"
#. module: l10n_ro_edi
#: model:ir.model.fields,field_description:l10n_ro_edi.field_res_partner__ubl_cii_format
#: model:ir.model.fields,field_description:l10n_ro_edi.field_res_users__ubl_cii_format
msgid "Format"
msgstr "Format"
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner doesn't have a VAT nor Company ID: %s. At least one of"
" them is required. "
msgstr ""
"Următorul partener nu are TVA sau ID de companie: %s. Cel puțin unul dintre "
"ele este necesar. "
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's city name is invalid: %s. If partner's state is "
"București, the city name must be 'SECTORX', where X is a number between 1-6."
" "
msgstr ""
"Numele orașului următor partener este nevalid: %s. Dacă statul partenerului "
"este București, numele orașului trebuie să fie 'SECTORX', unde X este un "
"număr între 1-6. "
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's doesn't have a country code prefix in their Company "
"ID: %s. "
msgstr ""
"Următorii parteneri nu au un prefix de cod de țară în ID-ul companiei: %s. "
#. module: l10n_ro_edi
#. odoo-python
#: code:addons/l10n_ro_edi/models/account_edi_xml_ubl_ciusro.py:0
#, python-format
msgid ""
"The following partner's doesn't have a country code prefix in their VAT: %s."
" "
msgstr "Următorii parteneri nu au un prefix de cod de țară în TVA: %s. "
+2
View File
@@ -0,0 +1,2 @@
from . import account_edi_xml_ubl_ciusro
from . import res_partner
@@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
from odoo import models, _
SECTOR_RO_CODES = ('SECTOR1', 'SECTOR2', 'SECTOR3', 'SECTOR4', 'SECTOR5', 'SECTOR6')
class AccountEdiXmlUBLRO(models.AbstractModel):
_inherit = "account.edi.xml.ubl_bis3"
_name = "account.edi.xml.ubl_ro"
_description = "CIUS RO"
def _export_invoice_filename(self, invoice):
return f"{invoice.name.replace('/', '_')}_cius_ro.xml"
def _get_partner_address_vals(self, partner):
# EXTENDS 'account_edi_ubl_cii'
vals = super()._get_partner_address_vals(partner)
if partner.state_id:
vals["country_subentity"] = partner.country_code + '-' + partner.state_id.code
return vals
def _get_invoice_tax_totals_vals_list(self, invoice, taxes_vals):
# EXTENDS 'account_edi_ubl_cii'
vals_list = super()._get_invoice_tax_totals_vals_list(invoice, taxes_vals)
if invoice.currency_id.name != 'RON':
ron_currency = self.env.ref("base.RON")
vals_list.append({
'currency': ron_currency,
'currency_dp': ron_currency.decimal_places,
'tax_amount': taxes_vals['tax_amount'],
})
return vals_list
def _get_partner_party_tax_scheme_vals_list(self, partner, role):
# EXTENDS 'account_edi_ubl_cii'
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,
'TaxScheme_vals': {},
'tax_scheme_id': 'VAT',
}]
return vals_list
def _get_partner_party_legal_entity_vals_list(self, partner):
# EXTENDS 'account_edi_ubl_cii'
vals_list = super()._get_partner_party_legal_entity_vals_list(partner)
for vals in vals_list:
if not partner.vat and partner.company_registry:
vals['company_id'] = partner.company_registry
return vals_list
def _export_invoice_vals(self, invoice):
# EXTENDS 'account_edi_ubl_cii'
vals = super()._export_invoice_vals(invoice)
vals['vals'].update({
'customization_id': 'urn:cen.eu:en16931:2017#compliant#urn:efactura.mfinante.ro:CIUS-RO:1.0.1',
'tax_currency_code': 'RON',
})
return vals
def _export_invoice_constraints(self, invoice, vals):
# EXTENDS 'account_edi_ubl_cii'
constraints = super()._export_invoice_constraints(invoice, vals)
for partner_type in ('supplier', 'customer'):
partner = vals[partner_type]
constraints.update({
f"ciusro_{partner_type}_city_required": self._check_required_fields(partner, 'city'),
f"ciusro_{partner_type}_street_required": self._check_required_fields(partner, 'street'),
f"ciusro_{partner_type}_state_id_required": self._check_required_fields(partner, 'state_id'),
})
if not partner.vat and not partner.company_registry:
constraints[f"ciusro_{partner_type}_tax_identifier_required"] = _(
"The following partner doesn't have a VAT nor Company ID: %s. "
"At least one of them is required. "
) % partner.name
if partner.vat and not partner.vat.startswith(partner.country_code):
constraints[f"ciusro_{partner_type}_country_code_vat_required"] = _(
"The following partner's doesn't have a country code prefix in their VAT: %s. "
) % 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'
and partner.city not in SECTOR_RO_CODES):
constraints[f"ciusro_{partner_type}_invalid_city_name"] = _(
"The following partner's city name is invalid: %s. "
"If partner's state is București, the city name must be 'SECTORX', "
"where X is a number between 1-6. "
) % partner.name
return constraints
+19
View File
@@ -0,0 +1,19 @@
from odoo import api, fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
ubl_cii_format = fields.Selection(selection_add=[('ciusro', "CIUSRO")])
@api.depends('country_code')
def _compute_ubl_cii_format(self):
super()._compute_ubl_cii_format()
for partner in self:
if partner.country_code == 'RO':
partner.ubl_cii_format = 'ciusro'
def _get_edi_builder(self):
if self.ubl_cii_format == 'ciusro':
return self.env['account.edi.xml.ubl_ro']
return super()._get_edi_builder()
+1
View File
@@ -0,0 +1 @@
from . import test_xml_ubl_ro
@@ -0,0 +1,166 @@
<?xml version='1.0' encoding='UTF-8'?>
<Invoice xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-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">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Hudson Construction</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Roasted Romanian Roller</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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>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.00</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.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>
@@ -0,0 +1,169 @@
<?xml version='1.0' encoding='UTF-8'?>
<Invoice xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-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>FUS</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">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Hudson Construction</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Roasted Romanian Roller</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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>30% Advance End of Following Month</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="FUS">285.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="FUS">1500.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="FUS">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:TaxTotal>
<cbc:TaxAmount currencyID="RON">285.00</cbc:TaxAmount>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="FUS">1500.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="FUS">1500.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="FUS">1785.00</cbc:TaxInclusiveAmount>
<cbc:PrepaidAmount currencyID="FUS">0.00</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="FUS">1785.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>___ignore___</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">1.0</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="FUS">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="FUS">500.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>___ignore___</cbc:ID>
<cbc:InvoicedQuantity unitCode="DZN">1.0</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="FUS">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="FUS">1000.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>
@@ -0,0 +1,165 @@
<?xml version='1.0' encoding='UTF-8'?>
<CreditNote xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-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>RINV/2017/00001</cbc:ID>
<cbc:IssueDate>2017-01-01</cbc:IssueDate>
<cbc:CreditNoteTypeCode>381</cbc:CreditNoteTypeCode>
<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 xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<cbc:ID>RINV_2017_00001.pdf</cbc:ID>
<cac:Attachment>
<cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf" filename="RINV_2017_00001.pdf">___ignore___</cbc:EmbeddedDocumentBinaryObject>
</cac:Attachment>
</cac:AdditionalDocumentReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="9947">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Hudson Construction</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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">RO1234567897</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>RO1234567897</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Roasted Romanian Roller</cbc:RegistrationName>
<cbc:CompanyID>RO1234567897</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>RINV/2017/00001</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>RO98RNCB1234567890123456</cbc:ID>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>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:CreditNoteLine>
<cbc:ID>___ignore___</cbc:ID>
<cbc:CreditedQuantity unitCode="C62">1.0</cbc:CreditedQuantity>
<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.00</cbc:PriceAmount>
</cac:Price>
</cac:CreditNoteLine>
<cac:CreditNoteLine>
<cbc:ID>___ignore___</cbc:ID>
<cbc:CreditedQuantity unitCode="DZN">1.0</cbc:CreditedQuantity>
<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.00</cbc:PriceAmount>
</cac:Price>
</cac:CreditNoteLine>
</CreditNote>
+137
View File
@@ -0,0 +1,137 @@
from odoo import Command
from odoo.addons.l10n_account_edi_ubl_cii_tests.tests.common import TestUBLCommon
from odoo.exceptions import UserError
from odoo.tests import tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestUBLRO(TestUBLCommon):
@classmethod
def setUpClass(cls, chart_template_ref="ro"):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.company_data['company'].write({
'country_id': cls.env.ref('base.ro').id,
'state_id': cls.env.ref('base.RO_B').id,
'name': 'Hudson Construction',
'city': 'SECTOR1',
'zip': '010101',
'vat': 'RO1234567897',
'phone': '+40 123 456 789',
'street': "Strada Kunst, 3",
'invoice_is_ubl_cii': True,
})
cls.currency_data['currency'] = cls.env.ref('base.RON')
cls.env['res.partner.bank'].create({
'acc_type': 'iban',
'partner_id': cls.company_data['company'].partner_id.id,
'acc_number': 'RO98RNCB1234567890123456',
})
cls.partner_a = cls.env['res.partner'].create({
'country_id': cls.env.ref('base.ro').id,
'state_id': cls.env.ref('base.RO_B').id,
'name': 'Roasted Romanian Roller',
'city': 'SECTOR3',
'zip': '010101',
'vat': 'RO1234567897',
'phone': '+40 123 456 780',
'street': "Rolling Roast, 88",
'bank_ids': [(0, 0, {'acc_number': 'RO98RNCB1234567890123456'})],
'ref': 'ref_partner_a',
})
cls.tax_19 = cls.env['account.tax'].create({
'name': 'tax_19',
'amount_type': 'percent',
'amount': 19,
'type_tax_use': 'sale',
'country_id': cls.env.ref('base.ro').id,
})
####################################################
# Test export - import
####################################################
def create_move(self, move_type, send=True):
return self._generate_move(
self.env.company.partner_id,
self.partner_a,
send=send,
move_type=move_type,
invoice_line_ids=[
{
'product_id': self.product_a.id,
'price_unit': 500.0,
'tax_ids': [Command.set(self.tax_19.ids)],
},
{
'product_id': self.product_b.id,
'price_unit': 1000.0,
'tax_ids': [Command.set(self.tax_19.ids)],
},
],
)
def get_attachment(self, move):
self.assertTrue(move.ubl_cii_xml_id)
self.assertEqual(move.ubl_cii_xml_id.name[-11:], "cius_ro.xml")
return move.ubl_cii_xml_id
def test_export_invoice(self):
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_credit_note(self):
refund = self.create_move("out_refund")
attachment = self.get_attachment(refund)
self._assert_invoice_attachment(attachment, xpaths=None, expected_file_path='from_odoo/ciusro_out_refund.xml')
def test_export_invoice_different_currency(self):
self.currency_data['currency'] = self.env.ref('base.USD')
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_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, from_cron=False, allow_fallback_pdf=False)
def test_export_no_vat_but_have_company_id_with_prefix(self):
self.company_data['company'].write({
'vat': None,
'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_vat_without_prefix(self):
self.company_data['company'].vat = '1234567897'
invoice = self.create_move("out_invoice", send=False)
with self.assertRaisesRegex(UserError, "doesn't have a country code prefix in their VAT"):
invoice._generate_pdf_and_send_invoice(self.move_template, from_cron=False, allow_fallback_pdf=False)
def test_export_constraints(self):
self.company_data['company'].company_registry = None
for required_field in ('city', 'street', 'state_id', 'vat'):
prev_val = self.company_data["company"][required_field]
self.company_data["company"][required_field] = None
invoice = self.create_move("out_invoice", send=False)
with self.assertRaisesRegex(UserError, "required"):
invoice._generate_pdf_and_send_invoice(self.move_template, from_cron=False, allow_fallback_pdf=False)
self.company_data["company"][required_field] = prev_val
self.company_data["company"].city = "Bucharest"
invoice = self.create_move("out_invoice", send=False)
with self.assertRaisesRegex(UserError, "city name must be 'SECTORX'"):
invoice._generate_pdf_and_send_invoice(self.move_template, from_cron=False, allow_fallback_pdf=False)