Skip to content

Commit

Permalink
[FIX] account: update fiscal position when updating taxes
Browse files Browse the repository at this point in the history
The fiscal positions for the new taxes should also be
provided, when we create the taxes, so the clients
don't have to manually input them.
Only fiscal positions that exist in the db and with new
taxes are created.

Related: odoo#108571
task-3116246

closes odoo#109491

X-original-commit: b6947f2
Signed-off-by: William André (wan) <[email protected]>
Signed-off-by: Wala Gauthier (gawa) <[email protected]>
  • Loading branch information
gawa-odoo committed Jan 10, 2023
1 parent f92a2c9 commit cf86276
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 9 deletions.
42 changes: 33 additions & 9 deletions addons/account/models/chart_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def _update_tax_from_template(template, tax):
tax_line.write({"tag_ids": [(6, 0, tags_to_add.ids)]})
_cleanup_tags(tags_to_unlink)

def _get_template_to_tax_xmlid_mapping(company):
def _get_template_to_real_xmlid_mapping(company, model):
"""
This function uses ir_model_data to return a mapping between the tax templates and the taxes, using their xmlid
This function uses ir_model_data to return a mapping between the templates and the data, using their xmlid
:returns: {
account.tax.template.id: account.tax.id
}
Expand All @@ -72,15 +72,15 @@ def _get_template_to_tax_xmlid_mapping(company):
env.cr.execute(
"""
SELECT template.res_id AS template_res_id,
tax.res_id AS tax_res_id
FROM ir_model_data tax
data.res_id AS data_res_id
FROM ir_model_data data
JOIN ir_model_data template
ON template.name = substr(tax.name, strpos(tax.name, '_') + 1)
WHERE tax.model = 'account.tax'
AND tax.name LIKE %s
ON template.name = substr(data.name, strpos(data.name, '_') + 1)
WHERE data.model = %s
AND data.name LIKE %s
-- tax.name is of the form: {company_id}_{account.tax.template.name}
""",
[r"%s\_%%" % company.id],
[model, r"%s\_%%" % company.id],
)
tuples = env.cr.fetchall()
return dict(tuples)
Expand Down Expand Up @@ -116,6 +116,26 @@ def _cleanup_tags(tags):
if not (aml_using_tag or tax_using_tag or report_line_using_tag):
tag.unlink()

def _update_fiscal_positions_from_templates(company, chart_template_id, new_taxes_template):
chart_template = env["account.chart.template"].browse(chart_template_id)
positions = env['account.fiscal.position.template'].search([('chart_template_id', '=', chart_template_id)])
tax_template_ref = _get_template_to_real_xmlid_mapping(company, 'account.tax')
fp_template_ref = _get_template_to_real_xmlid_mapping(company, 'account.fiscal.position')

tax_template_vals = []
for position_template in positions:
fp = env["account.fiscal.position"].browse(fp_template_ref.get(position_template.id))
if not fp:
continue
for position_tax in position_template.tax_ids:
if position_tax.tax_src_id in new_taxes_template or position_tax.tax_dest_id in new_taxes_template:
tax_template_vals.append((position_tax, {
'tax_src_id': tax_template_ref[position_tax.tax_src_id.id],
'tax_dest_id': position_tax.tax_dest_id and tax_template_ref[position_tax.tax_dest_id.id] or False,
'position_id': fp.id,
}))
chart_template._create_records_with_xmlid('account.fiscal.position.tax', tax_template_vals, company)

def _notify_accountant_managers(taxes_to_check):
accountant_manager_group = env.ref("account.group_account_manager")
partner_managers_ids = accountant_manager_group.users.mapped('partner_id')
Expand All @@ -142,17 +162,21 @@ def _notify_accountant_managers(taxes_to_check):
chart_template_id = env['ir.model.data']._xmlid_to_res_id(chart_template_xmlid)
companies = env['res.company'].search([('chart_template_id', '=', chart_template_id)])
outdated_taxes = []
new_taxes_template = []
for company in companies:
template_to_tax = _get_template_to_tax_xmlid_mapping(company)
template_to_tax = _get_template_to_real_xmlid_mapping(company, 'account.tax')
templates = env['account.tax.template'].with_context(active_test=False).search([("chart_template_id", "=", chart_template_id)])
for template in templates:
tax = env["account.tax"].browse(template_to_tax.get(template.id))
if not tax or not _is_tax_and_template_same(template, tax):
_create_tax_from_template(company, template, old_tax=tax)
if tax:
outdated_taxes.append(tax)
else:
new_taxes_template.append(template)
else:
_update_tax_from_template(template, tax)
_update_fiscal_positions_from_templates(company, chart_template_id, new_taxes_template)
if outdated_taxes:
_notify_accountant_managers(outdated_taxes)

Expand Down
1 change: 1 addition & 0 deletions addons/account/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from . import test_account_invoice_report
from . import test_account_move_line_tax_details
from . import test_account_journal_dashboard
from . import test_chart_template
from . import test_fiscal_position
from . import test_reconciliation
from . import test_sequence_mixin
Expand Down
215 changes: 215 additions & 0 deletions addons/account/tests/test_chart_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
from odoo import Command
from odoo.addons.account.models.chart_template import update_taxes_from_templates
from odoo.tests import tagged
from odoo.tests.common import TransactionCase



@tagged('post_install', '-at_install')
class TestChartTemplate(TransactionCase):

@classmethod
def create_tax_template(cls, name, template_name, amount):
return cls.env['account.tax.template']._load_records([{
'xml_id': template_name,
'values': {
'name': name,
'amount': amount,
'chart_template_id': cls.chart_template.id,
'invoice_repartition_line_ids': [
Command.create({
'factor_percent': 100,
'repartition_type': 'base',
}),
Command.create({
'factor_percent': 100,
'repartition_type': 'tax',
}),
],
'refund_repartition_line_ids': [
Command.create({
'factor_percent': 100,
'repartition_type': 'base',
}),
Command.create({
'factor_percent': 100,
'repartition_type': 'tax',
}),
],
},
}])

@classmethod
def setUpClass(cls):
"""
Setups a company with a custom chart template, containing a tax and a fiscal position.
We need to add xml_ids to the templates because they are loaded from their xml_ids
"""
super().setUpClass()

# Create user.
user = cls.env['res.users'].create({
'name': 'Because I am accountman!',
'login': 'accountman',
'password': 'accountman',
'groups_id': [Command.set(cls.env.user.groups_id.ids), Command.link(cls.env.ref('account.group_account_user').id)],
})
user.partner_id.email = '[email protected]'

cls.company_1 = cls.env['res.company'].create({
'name': 'TestCompany1',
'country_id': cls.env.ref('base.be').id,
})

cls.env = cls.env(user=user)
cls.cr = cls.env.cr

user.write({
'company_ids': [Command.set(cls.company_1.ids)],
'company_id': cls.company_1.id,
})

cls.chart_template = cls.env['account.chart.template']._load_records([{
'xml_id': 'account.test_chart_template',
'values': {
'name': 'Test Chart Template',
'code_digits': 6,
'currency_id': cls.env.ref('base.EUR').id,
'bank_account_code_prefix': 1000,
'cash_account_code_prefix': 2000,
'transfer_account_code_prefix': 3000,
},
}])

account_templates = cls.env['account.account.template']._load_records([
{
'xml_id': 'account.test_account_income_template',
'values':
{
'name': 'property_income_account',
'code': '222221',
'user_type_id': cls.env.ref('account.data_account_type_revenue').id,
'chart_template_id': cls.chart_template.id,
}
},
{
'xml_id': 'account.test_account_expense_template',
'values':
{
'name': 'property_expense_account',
'code': '222222',
'user_type_id': cls.env.ref('account.data_account_type_expenses').id,
'chart_template_id': cls.chart_template.id,
}
},
])

cls.chart_template.property_account_income_categ_id = account_templates[0].id
cls.chart_template.property_account_expense_categ_id = account_templates[1].id

cls.tax_1_template = cls.create_tax_template('Tax 1', 'account.test_tax_1_template', 15)
cls.tax_2_template = cls.create_tax_template('Tax 2', 'account.test_tax_2_template', 0)

cls.fiscal_position_template = cls.env['account.fiscal.position.template']._load_records([{
'xml_id': 'account.test_fiscal_position_template',
'values': {
'name': 'Fiscal Position',
'chart_template_id': cls.chart_template.id,
'country_id': cls.env.ref('base.be').id,
'auto_apply': True,
},
}])

cls.env['account.fiscal.position.tax.template']._load_records([{
'xml_id': 'account.test_fiscal_position_tax_template_1',
'values': {
'tax_src_id': cls.tax_1_template.id,
'tax_dest_id': cls.tax_2_template.id,
'position_id': cls.fiscal_position_template.id,
},
}])

cls.chart_template.try_loading(company=cls.company_1, install_demo=False)

cls.partner_1 = cls.env['res.partner'].create({
'name': 'Partner 1',
'country_id': cls.env.ref('base.be').id,
})

def test_update_taxes_from_templates(self):
"""
Tests that adding a new tax template and a fiscal position tax template with this new tax template
creates this new tax and fiscal position line when updating
"""
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_1.id)
tax_3_template = self.create_tax_template('Tax 3', 'account.test_tax_3_template', 16)
tax_4_template = self.create_tax_template('Tax 4', 'account.test_tax_4_template', 17)

self.env['account.fiscal.position.tax.template']._load_records([
{
'xml_id': 'account.test_fiscal_position_new_tax_src_template',
'values': {
'tax_src_id': tax_3_template.id,
'tax_dest_id': self.tax_1_template.id,
'position_id': self.fiscal_position_template.id,
},
},
{
'xml_id': 'account.test_fiscal_position_new_tax_dest_template',
'values': {
'tax_src_id': self.tax_2_template.id,
'tax_dest_id': tax_4_template.id,
'position_id': self.fiscal_position_template.id,
},
},
])

taxes = self.env['account.tax'].search([('company_id', '=', self.company_1.id)])

# Only the template have been created, so it should not be reflected yet on the company's chart template
self.assertEqual(len(fiscal_position.tax_ids), 1)
self.assertEqual(len(taxes), 2)

chart_template_xml_id = self.chart_template.get_external_id()[self.chart_template.id]
update_taxes_from_templates(self.env.cr, chart_template_xml_id)

taxes = self.env['account.tax'].search([('company_id', '=', self.company_1.id)])
self.assertRecordValues(taxes, [
{'name': 'Tax 1'},
{'name': 'Tax 2'},
{'name': 'Tax 3'},
{'name': 'Tax 4'},
])

self.assertRecordValues(fiscal_position.tax_ids.tax_src_id, [
{'name': 'Tax 1'},
{'name': 'Tax 3'},
{'name': 'Tax 2'},
])
self.assertRecordValues(fiscal_position.tax_ids.tax_dest_id, [
{'name': 'Tax 2'},
{'name': 'Tax 1'},
{'name': 'Tax 4'},
])

def test_update_taxes_removed_from_templates(self):
"""
Tests updating after the removal of taxes and fiscal position mapping from the company
"""
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_1.id)
fiscal_position.tax_ids.unlink()
self.env['account.tax'].search([('company_id', '=', self.company_1.id)]).unlink()

chart_template_xml_id = self.chart_template.get_external_id()[self.chart_template.id]
update_taxes_from_templates(self.env.cr, chart_template_xml_id)

# if taxes have been deleted, they will be recreated, and the fiscal position mapping for it too
self.assertEqual(len(self.env['account.tax'].search([('company_id', '=', self.company_1.id)])), 2)
self.assertEqual(len(fiscal_position.tax_ids), 1)

fiscal_position.tax_ids.unlink()
update_taxes_from_templates(self.env.cr, chart_template_xml_id)

# if only the fiscal position mapping has been removed, it won't be recreated
self.assertEqual(len(fiscal_position.tax_ids), 0)

0 comments on commit cf86276

Please sign in to comment.