[IMP] account: tax tag on any journal item

The aim of this commit is to allow any tax tag to be set on a journal
item and to allow the user to put tax tag from foreign countries in
which they have a VAT number on any tax.

The end goal is to allow the user to impact tax reports from several
country with the same journal item, avoiding the creation of a whole
journal entry and setting the path for some more automation.

Context:
Our user has a company based in Belgium but has a warehouse in
Netherland. Thus, when the company sells anything from that warehouse, it
needs to fill the Dutch tax report directly.
Moreover, the company needs to fill a grid on the Belgian VAT tax report.
That means that the user would need to gather some journal items on both
the Belgian tax report and the Dutch tax report.

Before this commit:
The user can't set tax tag from another country on its tax nor in the same move.

After this commit:
The user can set tax tags from any country for which its company has a
foreign vat number on any tax and in a move

Task-id: 3196547
opw-2810712

closes odoo/odoo#113027

Related: odoo/enterprise#37247
Signed-off-by: Olivier Colson (oco) <oco@odoo.com>
This commit is contained in:
Brice bib Bartoletti
2023-03-24 00:20:39 +01:00
parent f39c28ec45
commit c45a164b13
7 changed files with 63 additions and 9 deletions
+13 -1
View File
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo import osv
@@ -15,6 +14,19 @@ class AccountAccountTag(models.Model):
tax_negate = fields.Boolean(string="Negate Tax Balance", help="Check this box to negate the absolute value of the balance of the lines associated with this tag in tax report computation.")
country_id = fields.Many2one(string="Country", comodel_name='res.country', help="Country for which this tag is available, when applied on taxes.")
def name_get(self):
if not self.env.company.multi_vat_foreign_country_ids:
return super().name_get()
res = []
for tag in self:
name = tag.name
if tag.applicability == "taxes" and tag.country_id and tag.country_id != self.env.company.account_fiscal_country_id:
name = _("%s (%s)", tag.name, tag.country_id.code)
res.append((tag.id, name,))
return res
@api.model
def _get_tax_tags(self, tag_name, country_id):
""" Returns all the tax tags corresponding to the tag name given in parameter
+1 -1
View File
@@ -1820,7 +1820,7 @@ class AccountMove(models.Model):
self._compute_tax_country_id() # We need to ensure this field has been computed, as we use it in our check
for record in self:
amls = record.line_ids
impacted_countries = amls.tax_ids.country_id | amls.tax_line_id.country_id | amls.tax_tag_ids.country_id
impacted_countries = amls.tax_ids.country_id | amls.tax_line_id.country_id
if impacted_countries and impacted_countries != record.tax_country_id:
if record.fiscal_position_id and impacted_countries != record.fiscal_position_id.country_id:
raise ValidationError(_("This entry contains taxes that are not compatible with your fiscal position. Check the country set in fiscal position and in your tax configuration."))
+8 -6
View File
@@ -1305,6 +1305,14 @@ class AccountTaxRepartitionLine(models.Model):
help="The order in which distribution lines are displayed and matched. For refunds to work properly, invoice distribution lines should be arranged in the same order as the credit note distribution lines they correspond to.")
use_in_tax_closing = fields.Boolean(string="Tax Closing Entry", default=True)
tag_ids_domain = fields.Binary(string="tag domain", help="Dynamic domain used for the tag that can be set on tax", compute="_compute_tag_ids_domain")
@api.depends('company_id.multi_vat_foreign_country_ids', 'company_id.account_fiscal_country_id')
def _compute_tag_ids_domain(self):
for rep_line in self:
allowed_country_ids = (False, rep_line.company_id.account_fiscal_country_id.id, *rep_line.company_id.multi_vat_foreign_country_ids.ids,)
rep_line.tag_ids_domain = [('applicability', '=', 'taxes'), ('country_id', 'in', allowed_country_ids)]
@api.onchange('account_id', 'repartition_type')
def _on_change_account_id(self):
if not self.account_id or self.repartition_type == 'base':
@@ -1312,12 +1320,6 @@ class AccountTaxRepartitionLine(models.Model):
else:
self.use_in_tax_closing = self.account_id.internal_group not in ('income', 'expense')
@api.constrains('tax_id', 'tag_ids')
def validate_tags_country(self):
for record in self:
if record.tag_ids.country_id and record.tax_id.country_id != record.tag_ids.country_id:
raise ValidationError(_("A tax should only use tags from its country. You should use another tax and a fiscal position if you wish to uses the tags from foreign tax reports."))
@api.depends('factor_percent')
def _compute_factor(self):
for record in self:
+22
View File
@@ -172,6 +172,15 @@ class ResCompany(models.Model):
# Storno Accounting
account_storno = fields.Boolean(string="Storno accounting", readonly=False)
# Multivat
fiscal_position_ids = fields.One2many(comodel_name="account.fiscal.position", inverse_name="company_id")
multi_vat_foreign_country_ids = fields.Many2many(
string="Foreign VAT countries",
help="Countries for which the company has a VAT number",
comodel_name='res.country',
compute='_compute_multi_vat_foreign_country',
)
# Fiduciary mode
quick_edit_mode = fields.Selection(
selection=[
@@ -197,6 +206,19 @@ class ResCompany(models.Model):
if rec.fiscalyear_last_day > max_day:
raise ValidationError(_("Invalid fiscal year last day"))
@api.depends('fiscal_position_ids.foreign_vat')
def _compute_multi_vat_foreign_country(self):
company_to_foreign_vat_country = {
val['company_id'][0]: val['country_ids']
for val in self.env['account.fiscal.position'].read_group(
domain=[('company_id', 'in', self.ids), ('foreign_vat', '!=', False)],
fields=['country_ids:array_agg(country_id)'],
groupby='company_id',
)
}
for company in self:
company.multi_vat_foreign_country_ids = self.env['res.country'].browse(company_to_foreign_vat_country.get(company.id))
@api.depends('country_id')
def compute_account_tax_fiscal_country(self):
for record in self:
+13
View File
@@ -283,6 +283,19 @@ class AccountTestInvoicingCommon(TransactionCase):
'rates': rate1 + rate2,
}
@classmethod
def _instantiate_basic_test_tax_group(cls, company=None, country=None):
company = company or cls.env.company
vals = {
'name': 'Test tax group',
'company_id': company.id,
'tax_receivable_account_id': cls.company_data['default_account_receivable'].sudo().copy({'company_id': company.id}).id,
'tax_payable_account_id': cls.company_data['default_account_payable'].sudo().copy({'company_id': company.id}).id,
}
if country:
vals['country_id'] = country.id
return cls.env['account.tax.group'].sudo().create(vals)
@classmethod
def setup_armageddon_tax(cls, tax_name, company_data):
return cls.env['account.tax'].create({
@@ -28,6 +28,7 @@
<tree string="Tags">
<field name="name"/>
<field name="applicability"/>
<field name="country_id" />
</tree>
</field>
</record>
+5 -1
View File
@@ -41,11 +41,15 @@
<field name="factor_percent" attrs="{'invisible': [('repartition_type', '=', 'base')]}"/>
<field name="repartition_type"/>
<field name="account_id" attrs="{'invisible': [('repartition_type', '=', 'base')]}" options="{'no_create': True}"/>
<field name="tag_ids" widget="many2many_tags" options="{'no_create': True}" domain="[('applicability', '=', 'taxes'), '|', ('country_id', '=', parent.country_id), ('country_id', '=', False)]"/>
<field name="tag_ids"
widget="many2many_tags"
options="{'no_create': True}"
domain="tag_ids_domain"/>
<field name="use_in_tax_closing"
optional="hidden"
attrs="{'invisible': [('repartition_type', '=', 'base')]}"/>
<field name="company_id" invisible="1"/>
<field name="tag_ids_domain" invisible="1"/>
</tree>
</field>
</record>