Skip to content

Commit

Permalink
Merge remote-tracking branch 'odoo/17.0' into 17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
OCA-git-bot committed Sep 17, 2024
2 parents b9cd595 + 42ab539 commit 3cf0c28
Show file tree
Hide file tree
Showing 34 changed files with 1,203 additions and 70 deletions.
2 changes: 1 addition & 1 deletion addons/account/views/report_payment_receipt_templates.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<table name="invoices"
t-if="values['display_invoices']"
class="table table-sm">
<t t-set="invoices" t-value="o.reconciled_invoice_ids"/>
<t t-set="invoices" t-value="o.reconciled_invoice_ids or o.reconciled_bill_ids"/>
<!-- Check if invoices include different currencies -->
<t t-foreach="invoices" t-as="inv">
<t t-if="any(inv.currency_id != par[2].currency_id for par in inv._get_reconciled_invoices_partials()[0])" t-set="otherCurrency" t-value="True"/>
Expand Down
3 changes: 2 additions & 1 deletion addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ def _import_retrieve_partner_vals(self, tree, role):
'vat': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:CompanyID[string-length(text()) > 5]', tree),
'phone': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:Telephone', tree),
'mail': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:ElectronicMail', tree),
'name': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:Name', tree),
'name': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:Name', tree) or
self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cbc:RegistrationName', tree),
'country_code': self._find_value(f'.//cac:Accounting{role}Party/cac:Party//cac:Country//cbc:IdentificationCode', tree),
}

Expand Down
3 changes: 3 additions & 0 deletions addons/account_edi_ubl_cii_tax_extension/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import models
18 changes: 18 additions & 0 deletions addons/account_edi_ubl_cii_tax_extension/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Tax extension for UBL/CII',
'version': '1.0',
'summary': 'Tax extension for UBL/CII',
'description': """
This module adds 2 useful fields on the taxes for electronic invoicing: the tax category code and the tax exemption reason code.
These fields will be read when generating Peppol Bis 3 or Factur-X xml, for instance.
""",
'category': 'Accounting/Accounting',
'website': 'https://www.odoo.com/app/invoicing',
'depends': ['account_edi_ubl_cii'],
'data': [
'views/account_tax_views.xml',
],
'installable': True,
'license': 'LGPL-3',
}

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions addons/account_edi_ubl_cii_tax_extension/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import account_tax
from . import account_edi_common
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from odoo import models

TAX_EXEMPTION_MAPPING = {
'VATEX-EU-79-C': 'Exempt based on article 79, point c of Council Directive 2006/112/EC',
'VATEX-EU-132': 'Exempt based on article 132 of Council Directive 2006/112/EC',
'VATEX-EU-132-1A': 'Exempt based on article 132, section 1 (a) of Council Directive 2006/112/EC',
'VATEX-EU-132-1B': 'Exempt based on article 132, section 1 (b) of Council Directive 2006/112/EC',
'VATEX-EU-132-1C': 'Exempt based on article 132, section 1 (c) of Council Directive 2006/112/EC',
'VATEX-EU-132-1D': 'Exempt based on article 132, section 1 (d) of Council Directive 2006/112/EC',
'VATEX-EU-132-1E': 'Exempt based on article 132, section 1 (e) of Council Directive 2006/112/EC',
'VATEX-EU-132-1F': 'Exempt based on article 132, section 1 (f) of Council Directive 2006/112/EC',
'VATEX-EU-132-1G': 'Exempt based on article 132, section 1 (g) of Council Directive 2006/112/EC',
'VATEX-EU-132-1H': 'Exempt based on article 132, section 1 (h) of Council Directive 2006/112/EC',
'VATEX-EU-132-1I': 'Exempt based on article 132, section 1 (i) of Council Directive 2006/112/EC',
'VATEX-EU-132-1J': 'Exempt based on article 132, section 1 (j) of Council Directive 2006/112/EC',
'VATEX-EU-132-1K': 'Exempt based on article 132, section 1 (k) of Council Directive 2006/112/EC',
'VATEX-EU-132-1L': 'Exempt based on article 132, section 1 (l) of Council Directive 2006/112/EC',
'VATEX-EU-132-1M': 'Exempt based on article 132, section 1 (m) of Council Directive 2006/112/EC',
'VATEX-EU-132-1N': 'Exempt based on article 132, section 1 (n) of Council Directive 2006/112/EC',
'VATEX-EU-132-1O': 'Exempt based on article 132, section 1 (o) of Council Directive 2006/112/EC',
'VATEX-EU-132-1P': 'Exempt based on article 132, section 1 (p) of Council Directive 2006/112/EC',
'VATEX-EU-132-1Q': 'Exempt based on article 132, section 1 (q) of Council Directive 2006/112/EC',
'VATEX-EU-143': 'Exempt based on article 143 of Council Directive 2006/112/EC',
'VATEX-EU-143-1A': 'Exempt based on article 143, section 1 (a) of Council Directive 2006/112/EC',
'VATEX-EU-143-1B': 'Exempt based on article 143, section 1 (b) of Council Directive 2006/112/EC',
'VATEX-EU-143-1C': 'Exempt based on article 143, section 1 (c) of Council Directive 2006/112/EC',
'VATEX-EU-143-1D': 'Exempt based on article 143, section 1 (d) of Council Directive 2006/112/EC',
'VATEX-EU-143-1E': 'Exempt based on article 143, section 1 (e) of Council Directive 2006/112/EC',
'VATEX-EU-143-1F': 'Exempt based on article 143, section 1 (f) of Council Directive 2006/112/EC',
'VATEX-EU-143-1FA': 'Exempt based on article 143, section 1 (fa) of Council Directive 2006/112/EC',
'VATEX-EU-143-1G': 'Exempt based on article 143, section 1 (g) of Council Directive 2006/112/EC',
'VATEX-EU-143-1H': 'Exempt based on article 143, section 1 (h) of Council Directive 2006/112/EC',
'VATEX-EU-143-1I': 'Exempt based on article 143, section 1 (i) of Council Directive 2006/112/EC',
'VATEX-EU-143-1J': 'Exempt based on article 143, section 1 (j) of Council Directive 2006/112/EC',
'VATEX-EU-143-1K': 'Exempt based on article 143, section 1 (k) of Council Directive 2006/112/EC',
'VATEX-EU-143-1L': 'Exempt based on article 143, section 1 (l) of Council Directive 2006/112/EC',
'VATEX-EU-148': 'Exempt based on article 148 of Council Directive 2006/112/EC',
'VATEX-EU-148-A': 'Exempt based on article 148, section (a) of Council Directive 2006/112/EC',
'VATEX-EU-148-B': 'Exempt based on article 148, section (b) of Council Directive 2006/112/EC',
'VATEX-EU-148-C': 'Exempt based on article 148, section (c) of Council Directive 2006/112/EC',
'VATEX-EU-148-D': 'Exempt based on article 148, section (d) of Council Directive 2006/112/EC',
'VATEX-EU-148-E': 'Exempt based on article 148, section (e) of Council Directive 2006/112/EC',
'VATEX-EU-148-F': 'Exempt based on article 148, section (f) of Council Directive 2006/112/EC',
'VATEX-EU-148-G': 'Exempt based on article 148, section (g) of Council Directive 2006/112/EC',
'VATEX-EU-151': 'Exempt based on article 151 of Council Directive 2006/112/EC',
'VATEX-EU-151-1A': 'Exempt based on article 151, section 1 (a) of Council Directive 2006/112/EC',
'VATEX-EU-151-1AA': 'Exempt based on article 151, section 1 (aa) of Council Directive 2006/112/EC',
'VATEX-EU-151-1B': 'Exempt based on article 151, section 1 (b) of Council Directive 2006/112/EC',
'VATEX-EU-151-1C': 'Exempt based on article 151, section 1 (c) of Council Directive 2006/112/EC',
'VATEX-EU-151-1D': 'Exempt based on article 151, section 1 (d) of Council Directive 2006/112/EC',
'VATEX-EU-151-1E': 'Exempt based on article 151, section 1 (e) of Council Directive 2006/112/EC',
'VATEX-EU-309': 'Exempt based on article 309 of Council Directive 2006/112/EC',
'VATEX-EU-AE': 'Reverse charge',
'VATEX-EU-D': 'Intra-Community acquisition from second hand means of transport',
'VATEX-EU-F': 'Intra-Community acquisition of second hand goods',
'VATEX-EU-G': 'Export outside the EU',
'VATEX-EU-I': 'Intra-Community acquisition of works of art',
'VATEX-EU-IC': 'Intra-Community supply',
'VATEX-EU-O': 'Not subject to VAT',
'VATEX-EU-J': 'Intra-Community acquisition of collectors items and antiques',
'VATEX-FR-FRANCHISE': 'France domestic VAT franchise in base',
'VATEX-FR-CNWVAT': 'France domestic Credit Notes without VAT, due to supplier forfeit of VAT for discount',
}


class AccountEdiCommon(models.AbstractModel):
_inherit = "account.edi.common"

def _get_tax_unece_codes(self, invoice, tax):
if tax.ubl_cii_tax_category_code:
tax_exemption_reason = TAX_EXEMPTION_MAPPING.get(tax.ubl_cii_tax_exemption_reason_code)
return {
'tax_category_code': tax.ubl_cii_tax_category_code,
'tax_exemption_reason_code': tax.ubl_cii_tax_exemption_reason_code,
'tax_exemption_reason': tax_exemption_reason,
}
return super()._get_tax_unece_codes(invoice, tax)
97 changes: 97 additions & 0 deletions addons/account_edi_ubl_cii_tax_extension/models/account_tax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from odoo import api, fields, models


class AccountTax(models.Model):
_inherit = 'account.tax'

ubl_cii_tax_category_code = fields.Selection(
help="The VAT category code used for electronic invoicing purposes.",
string="Tax Category Code",
selection=[
('AE', 'AE - Vat Reverse Charge'),
('E', 'E - Exempt from Tax'),
('S', 'S - Standard rate'),
('Z', 'Z - Zero rated goods'),
('G', 'G - Free export item, VAT not charged'),
('O', 'O - Services outside scope of tax'),
('K', 'K - VAT exempt for EEA intra-community supply of goods and services'),
('L', 'L - Canary Islands general indirect tax'),
('M', 'M - Tax for production, services and importation in Ceuta and Melilla'),
('B', 'B - Transferred (VAT), In Italy')
]
)
ubl_cii_tax_exemption_reason_code = fields.Selection(
help="The reason why the amount is exempted from VAT or why no VAT is being charged, used for electronic invoicing purposes.",
string="Tax Exemption Reason Code",
selection=[
('VATEX-EU-79-C', 'VATEX-EU-79-C - Exempt based on article 79, point c of Council Directive 2006/112/EC'),
('VATEX-EU-132', 'VATEX-EU-132 - Exempt based on article 132 of Council Directive 2006/112/EC'),
('VATEX-EU-132-1A', 'VATEX-EU-132-1A - Exempt based on article 132, section 1 (a) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1B', 'VATEX-EU-132-1B - Exempt based on article 132, section 1 (b) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1C', 'VATEX-EU-132-1C - Exempt based on article 132, section 1 (c) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1D', 'VATEX-EU-132-1D - Exempt based on article 132, section 1 (d) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1E', 'VATEX-EU-132-1E - Exempt based on article 132, section 1 (e) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1F', 'VATEX-EU-132-1F - Exempt based on article 132, section 1 (f) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1G', 'VATEX-EU-132-1G - Exempt based on article 132, section 1 (g) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1H', 'VATEX-EU-132-1H - Exempt based on article 132, section 1 (h) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1I', 'VATEX-EU-132-1I - Exempt based on article 132, section 1 (i) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1J', 'VATEX-EU-132-1J - Exempt based on article 132, section 1 (j) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1K', 'VATEX-EU-132-1K - Exempt based on article 132, section 1 (k) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1L', 'VATEX-EU-132-1L - Exempt based on article 132, section 1 (l) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1M', 'VATEX-EU-132-1M - Exempt based on article 132, section 1 (m) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1N', 'VATEX-EU-132-1N - Exempt based on article 132, section 1 (n) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1O', 'VATEX-EU-132-1O - Exempt based on article 132, section 1 (o) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1P', 'VATEX-EU-132-1P - Exempt based on article 132, section 1 (p) of Council Directive 2006/112/EC'),
('VATEX-EU-132-1Q', 'VATEX-EU-132-1Q - Exempt based on article 132, section 1 (q) of Council Directive 2006/112/EC'),
('VATEX-EU-143', 'VATEX-EU-143 - Exempt based on article 143 of Council Directive 2006/112/EC'),
('VATEX-EU-143-1A', 'VATEX-EU-143-1A - Exempt based on article 143, section 1 (a) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1B', 'VATEX-EU-143-1B - Exempt based on article 143, section 1 (b) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1C', 'VATEX-EU-143-1C - Exempt based on article 143, section 1 (c) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1D', 'VATEX-EU-143-1D - Exempt based on article 143, section 1 (d) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1E', 'VATEX-EU-143-1E - Exempt based on article 143, section 1 (e) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1F', 'VATEX-EU-143-1F - Exempt based on article 143, section 1 (f) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1FA', 'VATEX-EU-143-1FA - Exempt based on article 143, section 1 (fa) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1G', 'VATEX-EU-143-1G - Exempt based on article 143, section 1 (g) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1H', 'VATEX-EU-143-1H - Exempt based on article 143, section 1 (h) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1I', 'VATEX-EU-143-1I - Exempt based on article 143, section 1 (i) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1J', 'VATEX-EU-143-1J - Exempt based on article 143, section 1 (j) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1K', 'VATEX-EU-143-1K - Exempt based on article 143, section 1 (k) of Council Directive 2006/112/EC'),
('VATEX-EU-143-1L', 'VATEX-EU-143-1L - Exempt based on article 143, section 1 (l) of Council Directive 2006/112/EC'),
('VATEX-EU-148', 'VATEX-EU-148 - Exempt based on article 148 of Council Directive 2006/112/EC'),
('VATEX-EU-148-A', 'VATEX-EU-148-A - Exempt based on article 148, section (a) of Council Directive 2006/112/EC'),
('VATEX-EU-148-B', 'VATEX-EU-148-B - Exempt based on article 148, section (b) of Council Directive 2006/112/EC'),
('VATEX-EU-148-C', 'VATEX-EU-148-C - Exempt based on article 148, section (c) of Council Directive 2006/112/EC'),
('VATEX-EU-148-D', 'VATEX-EU-148-D - Exempt based on article 148, section (d) of Council Directive 2006/112/EC'),
('VATEX-EU-148-E', 'VATEX-EU-148-E - Exempt based on article 148, section (e) of Council Directive 2006/112/EC'),
('VATEX-EU-148-F', 'VATEX-EU-148-F - Exempt based on article 148, section (f) of Council Directive 2006/112/EC'),
('VATEX-EU-148-G', 'VATEX-EU-148-G - Exempt based on article 148, section (g) of Council Directive 2006/112/EC'),
('VATEX-EU-151', 'VATEX-EU-151 - Exempt based on article 151 of Council Directive 2006/112/EC'),
('VATEX-EU-151-1A', 'VATEX-EU-151-1A - Exempt based on article 151, section 1 (a) of Council Directive 2006/112/EC'),
('VATEX-EU-151-1AA', 'VATEX-EU-151-1AA - Exempt based on article 151, section 1 (aa) of Council Directive 2006/112/EC'),
('VATEX-EU-151-1B', 'VATEX-EU-151-1B - Exempt based on article 151, section 1 (b) of Council Directive 2006/112/EC'),
('VATEX-EU-151-1C', 'VATEX-EU-151-1C - Exempt based on article 151, section 1 (c) of Council Directive 2006/112/EC'),
('VATEX-EU-151-1D', 'VATEX-EU-151-1D - Exempt based on article 151, section 1 (d) of Council Directive 2006/112/EC'),
('VATEX-EU-151-1E', 'VATEX-EU-151-1E - Exempt based on article 151, section 1 (e) of Council Directive 2006/112/EC'),
('VATEX-EU-309', 'VATEX-EU-309 - Exempt based on article 309 of Council Directive 2006/112/EC'),
('VATEX_EU_AE', 'VATEX-EU-AE - Reverse charge'),
('VATEX_EU_D', 'VATEX-EU-D - Intra-Community acquisition from second hand means of transport'),
('VATEX_EU_F', 'VATEX-EU-F - Intra-Community acquisition of second hand goods'),
('VATEX_EU_G', 'VATEX-EU-G - Export outside the EU'),
('VATEX_EU_I', 'VATEX-EU-I - Intra-Community acquisition of works of art'),
('VATEX_EU_IC', 'VATEX-EU-IC - Intra-Community supply'),
('VATEX_EU_O', 'VATEX-EU-O - Not subject to VAT'),
('VATEX_EU_J', 'VATEX-EU-J - Intra-Community acquisition of collectors items and antiques'),
('VATEX_FR-FRANCHISE', 'VATEX-FR-FRANCHISE - France domestic VAT franchise in base'),
('VATEX_FR-CNWVAT', 'VATEX-FR-CNWVAT - France domestic Credit Notes without VAT, due to supplier forfeit of VAT for discount'),
]
)

def _requires_exemption_reason(self):
self.ensure_one()
return self.ubl_cii_tax_category_code in ['AE', 'E', 'G', 'O', 'K']

@api.onchange("ubl_cii_tax_category_code")
def _onchange_ubl_cii_tax_category_code(self):
for tax in self:
if not tax._requires_exemption_reason():
tax.ubl_cii_tax_exemption_reason_code = False
1 change: 1 addition & 0 deletions addons/account_edi_ubl_cii_tax_extension/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_ubl_cii_tax_extension
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from lxml import etree
from odoo import Command
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged


@tagged('post_install', '-at_install')
class TestAccountEdiUblCiiTaxExtension(AccountTestInvoicingCommon):

@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)

cls.reverse_charge_tax = cls.company_data['default_tax_sale'].copy({'name': 'Reverse charge tax', 'ubl_cii_tax_category_code': 'AE', 'ubl_cii_tax_exemption_reason_code': 'VATEX_EU_AE'})
cls.zero_rated_tax = cls.company_data['default_tax_sale'].copy({'name': 'Zero rated tax', 'ubl_cii_tax_category_code': 'Z'})
cls.prod_tax = cls.company_data['default_tax_sale'].copy({'name': 'Production tax', 'ubl_cii_tax_category_code': 'M'})
cls.free_export_tax = cls.company_data['default_tax_sale'].copy({'name': 'Free export tax', 'ubl_cii_tax_category_code': 'G', 'ubl_cii_tax_exemption_reason_code': 'VATEX-EU-132-1G'})

def test_classified_tax_category_codes(self):
ubl_taxes = (self.reverse_charge_tax + self.zero_rated_tax + self.prod_tax + self.free_export_tax)
# test tax by tax then with multiple taxes
tax_list = list(ubl_taxes) + [ubl_taxes]
for taxes in tax_list:
invoice = self.env["account.move"].create({
"partner_id": self.partner_a.id,
"move_type": "out_invoice",
"invoice_line_ids": [Command.create({"name": "Test product", "price_unit": 100, "tax_ids": [Command.set(taxes.ids)]})],
})
invoice.action_post()
xml = self.env['account.edi.xml.ubl_bis3']._export_invoice(invoice)[0]
root = etree.fromstring(xml)
for tax, node in zip(taxes, root.findall('.//{*}Item/{*}ClassifiedTaxCategory')):
self.assertEqual(node.findtext('.//{*}ID') or False, tax.ubl_cii_tax_category_code)
self.assertEqual(node.findtext('.//{*}TaxExemptionReasonCode') or False, tax.ubl_cii_tax_exemption_reason_code)
Loading

0 comments on commit 3cf0c28

Please sign in to comment.