Skip to content
Snippets Groups Projects
Commit 88021358 authored by Brice bib Bartoletti's avatar Brice bib Bartoletti
Browse files

[FIX] l10n_eu_services:apply tag from localization

This commit aims to allow assigning tags to the taxes
created by the OSS feature by providing the xml_id of their
report.line in the eu_tag_map.py file.

Before this commit:
In l10n_be, the taxes created by OSS (l10n_eu_services) didn't set
the tag +47 on invoice_repartition_lines nor +49 on
refund_repartition_lines.
This make the VAT report for Belgium wrong.

After this commit:
Taxes created by OSS for a company using the belgian CoA will get
their tags set properly and thus will the taxes impact the
belgian tax report correctly.

task: 2770182
ticket: 2768622

Community-PR: https://github.com/odoo/odoo/pull/85607



Design choices:
This fix is currently solving the issue for l10n_be but we have
no doubt that it will be raised for other EU countries too.

In order to provide the tags, we decided to be consistent with
what as been done regarding the tax mapping. Thus we decided to
create and maintain a simple mapping file and to test it.

several other methods were explored:
- create a global variable and update it from all localization
modules. This method would work but is ugly and error prone.

- create a templating method and override it from localization
modules.
The problem is where to set the root of the template method?
The naïve solution would be to create a bridge module between
l10n_eu_services and l10n_be but that would lead to an explosion
in the number of bridge modules which we don't want.

In order to keep things simple and generic, we could put the
template method directly into the account module. But it is kind
of ugly because account shouldn't know anything about the oss
feature and it would encourage such a leaky design to happen
again in the future.

closes odoo/odoo#86295

Signed-off-by: default avatarWilliam André (wan) <wan@odoo.com>
Signed-off-by: default avatarBrice Bartoletti <bib@odoo.com>
parent 61249a43
No related branches found
No related tags found
No related merge requests found
......@@ -4,5 +4,6 @@
from . import chart_template
from . import eu_service_tax_rate
from . import eu_tax_map
from . import eu_tag_map
from . import res_company
from . import res_config_settings
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
"""
The EU_TAG_MAP answers the question: "which tag should I apply on the OSS tax repartition line?"
{
'fiscal_country_code': {
'invoice_base_tag': xml_id_of_the_tag or None,
'invoice_tax_tag': xml_id_of_the_tag or None,
'refund_base_tag': xml_id_of_the_tag or None,
'refund_tax_tag': xml_id_of_the_tag or None,
},
}
"""
EU_TAG_MAP = {
# Austria
'l10n_at.l10n_at_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Belgium
'l10n_be.l10nbe_chart_template': {
'invoice_base_tag': 'l10n_be.tax_report_line_47',
'invoice_tax_tag': None,
'refund_base_tag': 'l10n_be.tax_report_line_49',
'refund_tax_tag': None,
},
# Bulgaria
'BG': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Croatia
'l10n_hr.l10n_hr_chart_template_rrif': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Cyprus
'CY': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Czech - Done in 13.0 - CoA not available yet
'l10n_cz.cz_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Denmark
'l10n_dk.dk_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Estonia - Done in 13.0 - CoA not available yet
'EE': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Finland
'l10n_fi.fi_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# France
'l10n_fr.l10n_fr_pcg_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Germany SKR03
'l10n_de_skr03.l10n_de_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Germany SKR04
'l10n_de_skr04.l10n_chart_de_skr04': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Greece
'l10n_gr.l10n_gr_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Hungary
'l10n_hu.hungarian_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Ireland
'l10n_ie.l10n_ie': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Italy
'l10n_it.l10n_it_chart_template_generic': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Latvia
'LV': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Lithuania
'l10n_lt.account_chart_template_lithuania': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Luxembourg
'l10n_lu.lu_2011_chart_1': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Malta - Done in 13.0 - CoA not available yet
'MT': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Netherlands
'l10n_nl.l10nnl_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Poland
'l10n_pl.pl_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Portugal
'l10n_pt.pt_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Romania
'l10n_ro.ro_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Slovakia - Done in 13.0 - CoA not available yet
'l10n_sk.sk_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Slovenia
'l10n_si.gd_chart': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Spain
'l10n_es.account_chart_template_common': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
# Sweden
'l10n_se.l10nse_chart_template': {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
},
}
......@@ -3,6 +3,7 @@
from odoo import api, fields, models, _
from .eu_tax_map import EU_TAX_MAP
from .eu_tag_map import EU_TAG_MAP
class Company(models.Model):
......@@ -80,15 +81,19 @@ class Company(models.Model):
def _get_repartition_lines_oss(self):
self.ensure_one()
defaults = self.env['account.tax'].with_company(self).default_get(['invoice_repartition_line_ids', 'refund_repartition_line_ids'])
oss_account = self._get_oss_account()
if oss_account:
defaults['invoice_repartition_line_ids'][1][2]['account_id'] = oss_account.id
defaults['refund_repartition_line_ids'][1][2]['account_id'] = oss_account.id
oss_account, oss_tags = self._get_oss_account(), self._get_oss_tags()
base_line, tax_line, vals = 0, 1, 2
for doc_type in 'invoice', 'refund':
if oss_account:
defaults[f'{doc_type}_repartition_line_ids'][tax_line][vals]['account_id'] = oss_account.id
if oss_tags:
defaults[f'{doc_type}_repartition_line_ids'][base_line][vals]['tag_ids'] += [(4, oss_tags[f'{doc_type}_base_tag'].id, 0)] if oss_tags[f'{doc_type}_base_tag'] else []
defaults[f'{doc_type}_repartition_line_ids'][tax_line][vals]['tag_ids'] += [(4, oss_tags[f'{doc_type}_tax_tag'].id, 0)] if oss_tags[f'{doc_type}_tax_tag'] else []
return defaults['invoice_repartition_line_ids'], defaults['refund_repartition_line_ids']
def _get_oss_account(self):
self.ensure_one()
if not self.env['ir.model.data'].xmlid_to_object('l10n_eu_service.oss_tax_account_company_%s' % self.id):
if not self.env['ir.model.data'].xmlid_to_object(f'l10n_eu_service.oss_tax_account_company_{self.id}'):
sales_tax_accounts = self.env['account.tax'].search([
('type_tax_use', '=', 'sale'),
('company_id', '=', self.id)
......@@ -110,3 +115,21 @@ class Company(models.Model):
'noupdate': True,
})
return self.env.ref(f'l10n_eu_service.oss_tax_account_company_{self.id}')
def _get_oss_tags(self):
[chart_template_xml_id] = self.chart_template_id.get_xml_id().values()
tag_for_country = EU_TAG_MAP.get(chart_template_xml_id, {
'invoice_base_tag': None,
'invoice_tax_tag': None,
'refund_base_tag': None,
'refund_tax_tag': None,
})
return {
repartition_line_key: (
self.env.ref(tag_xml_id).tag_ids.filtered(lambda t: not t.tax_negate)
if tag_xml_id
else None
)
for repartition_line_key, tag_xml_id in tag_for_country.items()
}
# -*- coding: utf-8 -*-
from . import test_oss
# -*- coding: utf-8 -*-
from odoo.addons.l10n_eu_service.models.eu_tag_map import EU_TAG_MAP
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSBelgium(AccountTestInvoicingCommon):
@classmethod
def setUpClass(self, chart_template_ref='l10n_be.l10nbe_chart_template'):
try:
super().setUpClass(chart_template_ref=chart_template_ref)
except ValueError as e:
if e.args[0] == "External ID not found in the system: l10n_be.l10nbe_chart_template":
self.skipTest(self, reason="Belgian CoA is required for this testSuite but l10n_be isn't installed")
else:
raise e
self.company_data['company'].country_id = self.env.ref('base.be')
self.company_data['company']._map_eu_taxes()
def test_country_tag_from_belgium(self):
# get an eu country which isn't the current one:
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
for doc_type, report_line_xml_id in (
("invoice", "l10n_be.tax_report_line_47"),
("refund", "l10n_be.tax_report_line_49"),
):
with self.subTest(doc_type=doc_type, report_line_xml_id=report_line_xml_id):
oss_tag_id = tax_oss[f"{doc_type}_repartition_line_ids"]\
.filtered(lambda x: x.repartition_type == 'base')\
.tag_ids
expected_tag_id = self.env.ref(report_line_xml_id)\
.tag_ids\
.filtered(lambda t: not t.tax_negate)
self.assertIn(expected_tag_id, oss_tag_id, f"{doc_type} tag from Belgian CoA not correctly linked")
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSUSA(AccountTestInvoicingCommon):
@classmethod
def setUpClass(self, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
self.company_data['company'].country_id = self.env.ref('base.us')
self.company_data['company']._map_eu_taxes()
def test_no_oss_tax(self):
# get an eu country which isn't the current one:
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
self.assertFalse(len(tax_oss), "OSS tax shouldn't be instanced on a US company")
@tagged('post_install', 'post_install_l10n', '-at_install')
class TestOSSMap(AccountTestInvoicingCommon):
def test_oss_eu_tag_map(self):
""" Checks that the xml_id referenced in the map are correct.
In case of failure display the couple (chart_template_xml_id, tax_report_line_xml_id).
The test doesn't fail for unreferenced char_template or unreferenced tax_report_line.
"""
chart_templates = self.env['account.chart.template'].search([])
for chart_template in chart_templates:
[chart_template_xml_id] = chart_template.get_xml_id().values()
oss_tags = EU_TAG_MAP.get(chart_template_xml_id, {})
for tax_report_line_xml_id in filter(lambda d: d, oss_tags.values()):
with self.subTest(chart_template_xml_id=chart_template_xml_id, tax_report_line_xml_id=tax_report_line_xml_id):
tag = self.env.ref(tax_report_line_xml_id, raise_if_not_found=False)
self.assertIsNotNone(tag, f"The following xml_id is incorrect in EU_TAG_MAP.py:{tax_report_line_xml_id}")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment