1035 lines
52 KiB
Python
1035 lines
52 KiB
Python
|
import io
|
||
|
|
||
|
from markupsafe import Markup
|
||
|
from unittest.mock import patch
|
||
|
|
||
|
from odoo import Command
|
||
|
from odoo.exceptions import UserError
|
||
|
from odoo.tests import tagged
|
||
|
from odoo.addons.account.models.chart_template import code_translations, AccountChartTemplate, TEMPLATE_MODELS
|
||
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||
|
|
||
|
|
||
|
def _get_chart_template_mapping(self, get_all=False):
|
||
|
return {'test': {
|
||
|
'name': 'test',
|
||
|
'country_id': self.env.ref('base.be').id,
|
||
|
'country_code': None,
|
||
|
'module': 'account',
|
||
|
'parent': None,
|
||
|
}}
|
||
|
|
||
|
def test_get_data(self, template_code):
|
||
|
return {
|
||
|
'template_data': {
|
||
|
'code_digits': 6,
|
||
|
'currency_id': 'base.EUR',
|
||
|
'property_account_income_categ_id': 'test_account_income_template',
|
||
|
'property_account_expense_categ_id': 'test_account_expense_template',
|
||
|
'property_account_receivable_id': 'test_account_receivable_template',
|
||
|
'property_account_payable_id': 'test_account_payable_template',
|
||
|
},
|
||
|
'account.tax.group': {
|
||
|
'tax_group_taxes': {
|
||
|
'name': "Taxes",
|
||
|
'sequence': 0,
|
||
|
},
|
||
|
},
|
||
|
'account.journal': self._get_account_journal(template_code),
|
||
|
'res.company': {
|
||
|
self.env.company.id: {
|
||
|
'bank_account_code_prefix': '1000',
|
||
|
'cash_account_code_prefix': '2000',
|
||
|
'transfer_account_code_prefix': '3000',
|
||
|
'account_sale_tax_id': 'test_tax_1_template',
|
||
|
},
|
||
|
},
|
||
|
'account.account.tag': {
|
||
|
f'account.account_tax_tag_{i}': {
|
||
|
'name': f'tax_tag_name_{i}',
|
||
|
'applicability': 'taxes',
|
||
|
'country_id': 'base.be',
|
||
|
} for i in range(1, 9)
|
||
|
},
|
||
|
'account.tax': {
|
||
|
xmlid: _tax_vals(name, amount, 'account.account_tax_tag_1')
|
||
|
for name, xmlid, amount in [
|
||
|
('Tax 1', 'test_tax_1_template', 15),
|
||
|
('Tax 2', 'test_tax_2_template', 0),
|
||
|
]
|
||
|
},
|
||
|
'account.group': {
|
||
|
'test_account_group_1': {
|
||
|
'name': 'test_account_group_name_1',
|
||
|
'code_prefix_start': 222220,
|
||
|
'code_prefix_end': 222229,
|
||
|
}
|
||
|
},
|
||
|
'account.account': {
|
||
|
'test_account_receivable_template': {
|
||
|
'name': 'property_receivable_account',
|
||
|
'code': '411111',
|
||
|
'account_type': 'asset_receivable',
|
||
|
},
|
||
|
'test_account_payable_template': {
|
||
|
'name': 'property_payable_account',
|
||
|
'code': '421111',
|
||
|
'account_type': 'liability_payable',
|
||
|
},
|
||
|
'test_account_income_template': {
|
||
|
'name': 'property_income_account',
|
||
|
'code': '222221',
|
||
|
'account_type': 'income',
|
||
|
'group_id': 'test_account_group_1',
|
||
|
},
|
||
|
'test_account_expense_template': {
|
||
|
'name': 'property_expense_account',
|
||
|
'code': '222222',
|
||
|
'account_type': 'expense',
|
||
|
},
|
||
|
},
|
||
|
'account.fiscal.position': {
|
||
|
'test_fiscal_position_template': {
|
||
|
'name': 'Fiscal Position',
|
||
|
'country_id': 'base.be',
|
||
|
'auto_apply': True,
|
||
|
'tax_ids': [
|
||
|
Command.create({
|
||
|
'tax_src_id': 'test_tax_1_template',
|
||
|
'tax_dest_id': 'test_tax_2_template',
|
||
|
})
|
||
|
]
|
||
|
}
|
||
|
},
|
||
|
'account.reconcile.model': {
|
||
|
'test_account_reconcile_model_1': {
|
||
|
'name': 'test_reconcile_model_with_payment_tolerance',
|
||
|
'rule_type': 'invoice_matching',
|
||
|
'allow_payment_tolerance': True,
|
||
|
'payment_tolerance_type': 'percentage',
|
||
|
'payment_tolerance_param': 2.0,
|
||
|
'line_ids': [Command.create({'account_id': 'test_account_income_template'})],
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
def _tax_vals(name, amount, tax_tag_id=None, children_tax_xmlids=None, active=True, tax_scope="consu"):
|
||
|
tag_command = [Command.set([tax_tag_id])] if tax_tag_id else None
|
||
|
tax_vals = {
|
||
|
'name': name,
|
||
|
'amount': amount,
|
||
|
'amount_type': 'percent' if not children_tax_xmlids else 'group',
|
||
|
'tax_group_id': 'tax_group_taxes',
|
||
|
'active': active,
|
||
|
'tax_scope': tax_scope
|
||
|
}
|
||
|
if children_tax_xmlids:
|
||
|
tax_vals.update({'children_tax_ids': [Command.set(children_tax_xmlids)]})
|
||
|
else:
|
||
|
tax_vals.update({'repartition_line_ids': [
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'base', 'tag_ids': tag_command}),
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'base'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
]})
|
||
|
return tax_vals
|
||
|
|
||
|
CSV_DATA = {
|
||
|
'tax_1': (
|
||
|
'"id","name","type_tax_use","amount","amount_type","description","invoice_label","tax_group_id","repartition_line_ids/repartition_type",'
|
||
|
'"repartition_line_ids/factor_percent","repartition_line_ids/document_type","repartition_line_ids/tag_ids","repartition_line_ids/account_id",'
|
||
|
'"repartition_line_ids/use_in_tax_closing","description@en"\n'
|
||
|
'"tax_1","5%","sale","5.0","percent","","VAT 5%","tax_group_taxes","base","","invoice","tax_tag_name_1||tax_tag_name_2","","","Test tax"\n'
|
||
|
'"","","","","","","","","tax","50","invoice","tax_tag_name_3","test_account_income_template","False",""\n'
|
||
|
'"","","","","","","","","tax","50","invoice","tax_tag_name_4","test_account_income_template","False",""\n'
|
||
|
'"","","","","","","","","base","","refund","tax_tag_name_5||tax_tag_name_6","","",""\n'
|
||
|
'"","","","","","","","","tax","50","refund","tax_tag_name_7","test_account_income_template","False",""\n'
|
||
|
'"","","","","","","","","tax","50","refund","tax_tag_name_8","test_account_income_template","False",""\n'
|
||
|
),
|
||
|
'test_fiscal_position_template': (
|
||
|
'"id","name","country_id","auto_apply","tax_ids/tax_src_id","tax_ids/tax_dest_id"\n'
|
||
|
'"test_fiscal_position_template","Fiscal Position","base.be","1","test_tax_3_template","test_tax_4_template"\n'
|
||
|
),
|
||
|
}
|
||
|
|
||
|
@tagged('post_install', '-at_install')
|
||
|
@patch.object(AccountChartTemplate, '_get_chart_template_mapping', _get_chart_template_mapping)
|
||
|
class TestChartTemplate(AccountTestInvoicingCommon):
|
||
|
|
||
|
@classmethod
|
||
|
def _use_chart_template(cls, company, chart_template_ref=None):
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
cls.env['account.chart.template'].try_loading("test", company=company, install_demo=False)
|
||
|
|
||
|
@classmethod
|
||
|
@AccountTestInvoicingCommon.setup_country('be')
|
||
|
@patch.object(AccountChartTemplate, '_get_chart_template_mapping', _get_chart_template_mapping)
|
||
|
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
|
||
|
"""
|
||
|
# Avoid creating data from AccountTestInvoicingCommon setUpClass
|
||
|
# just use the override of the functions it provides
|
||
|
super(AccountTestInvoicingCommon, cls).setUpClass()
|
||
|
|
||
|
cls.ChartTemplate = cls.env['account.chart.template'].with_company(cls.company)
|
||
|
cls.country_be = cls.env.ref('base.be')
|
||
|
|
||
|
def test_signed_and_unsigned_tags_tax(self):
|
||
|
tax_report = self.env['account.report'].create({
|
||
|
'name': "Tax report 1",
|
||
|
'country_id': self.country_be.id,
|
||
|
'column_ids': [
|
||
|
Command.create({
|
||
|
'name': "Balance",
|
||
|
'expression_label': 'balance',
|
||
|
}),
|
||
|
],
|
||
|
})
|
||
|
self.env['account.report.line'].create({
|
||
|
'name': "[SIGNED_TAG] Signed tag line",
|
||
|
'report_id': tax_report.id,
|
||
|
'sequence': max(tax_report.mapped('line_ids.sequence') or [0]) + 1,
|
||
|
'expression_ids': [
|
||
|
Command.create({
|
||
|
'label': 'balance',
|
||
|
'engine': 'tax_tags',
|
||
|
'formula': 'SIGNED_TAG',
|
||
|
}),
|
||
|
],
|
||
|
})
|
||
|
signed_tag = self.env['account.account.tag'].search([
|
||
|
('applicability', '=', 'taxes'),
|
||
|
('name', '=', '+SIGNED_TAG'),
|
||
|
])
|
||
|
self.env['account.account.tag']._load_records([
|
||
|
{
|
||
|
'xml_id': 'account.unsigned_tax_tag',
|
||
|
'noupdate': True,
|
||
|
'values': {
|
||
|
'name': "unsigned tax tag",
|
||
|
'applicability': 'taxes',
|
||
|
'country_id': self.country_be.id,
|
||
|
},
|
||
|
},
|
||
|
])
|
||
|
tax_to_load = {
|
||
|
'name': 'Mixed Tags Tax',
|
||
|
'amount': 30,
|
||
|
'amount_type': 'percent',
|
||
|
'tax_group_id': 'tax_group_taxes',
|
||
|
'active': True,
|
||
|
'repartition_line_ids': [
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'base', 'tag_ids': 'account.unsigned_tax_tag||+SIGNED_TAG'}),
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'base'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
]
|
||
|
}
|
||
|
self.env['account.chart.template']._deref_account_tags('test', {'tax1': tax_to_load})
|
||
|
self.assertEqual(
|
||
|
tax_to_load['repartition_line_ids'][0],
|
||
|
Command.create({
|
||
|
'document_type': 'invoice',
|
||
|
'factor_percent': 100,
|
||
|
'repartition_type': 'base',
|
||
|
'tag_ids': [Command.set(['account.unsigned_tax_tag', signed_tag.id])],
|
||
|
})
|
||
|
)
|
||
|
|
||
|
def test_inactive_tag_tax(self):
|
||
|
inactive_tag = self.env['account.account.tag'].create({
|
||
|
'name': 'Inactive Tax Tag',
|
||
|
'applicability': 'taxes',
|
||
|
'active': False,
|
||
|
'country_id': self.country_be.id,
|
||
|
})
|
||
|
tax_to_load = {
|
||
|
'name': 'Inactive Tags Tax',
|
||
|
'amount': 30,
|
||
|
'amount_type': 'percent',
|
||
|
'tax_group_id': 'tax_group_taxes',
|
||
|
'active': True,
|
||
|
'repartition_line_ids': [
|
||
|
Command.create({
|
||
|
'document_type': 'invoice',
|
||
|
'factor_percent': 100,
|
||
|
'repartition_type': 'base',
|
||
|
'tag_ids': inactive_tag.name,
|
||
|
}),
|
||
|
]
|
||
|
}
|
||
|
self.env['account.chart.template']._deref_account_tags('test', {'tax1': tax_to_load})
|
||
|
self.assertEqual(
|
||
|
tax_to_load['repartition_line_ids'][0],
|
||
|
Command.create({
|
||
|
'document_type': 'invoice',
|
||
|
'factor_percent': 100,
|
||
|
'repartition_type': 'base',
|
||
|
'tag_ids': [(Command.set([inactive_tag.id]))],
|
||
|
}),
|
||
|
)
|
||
|
|
||
|
def test_update_taxes_creation(self):
|
||
|
""" Tests that adding a new tax and a fiscal position tax creates new records when updating. """
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.tax'].update({
|
||
|
xmlid: _tax_vals(name, amount)
|
||
|
for name, xmlid, amount in [
|
||
|
('Tax 3', 'test_tax_3_template', 16),
|
||
|
('Tax 4', 'test_tax_4_template', 17),
|
||
|
]
|
||
|
})
|
||
|
data['account.fiscal.position']['test_fiscal_position_template']['tax_ids'].extend([
|
||
|
Command.create({
|
||
|
'tax_src_id': 'test_tax_3_template',
|
||
|
'tax_dest_id': 'test_tax_1_template',
|
||
|
}),
|
||
|
Command.create({
|
||
|
'tax_src_id': 'test_tax_2_template',
|
||
|
'tax_dest_id': 'test_tax_4_template',
|
||
|
}),
|
||
|
])
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
taxes = self.env['account.tax'].search([('company_id', '=', self.company.id)])
|
||
|
self.assertRecordValues(taxes, [
|
||
|
{'name': 'Tax 1'},
|
||
|
{'name': 'Tax 2'},
|
||
|
{'name': 'Tax 3'},
|
||
|
{'name': 'Tax 4'},
|
||
|
])
|
||
|
|
||
|
fiscal_position = self.env['account.fiscal.position'].search([])
|
||
|
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_new_tax_rate(self):
|
||
|
"""Test the flow to replace taxes from an old to a new rate.
|
||
|
|
||
|
This test aims at defining more clearly how to update the taxes when the rate changes.
|
||
|
There are some constraints to take into account:
|
||
|
* There is a transition period where both taxes need to be usable
|
||
|
* Some reports might need to keep the references to the old tax i.e. on the fiscal positions (OSS)
|
||
|
|
||
|
The procedure for the code change is:
|
||
|
* Create the new report section if needed.
|
||
|
* Remove the taxes from the template.
|
||
|
This will not delete the taxes from the company when updating the template on the company.
|
||
|
* Add the new taxes with a xmlid different from the ones deleted.
|
||
|
The taxes will be created when updating the template on the company.
|
||
|
* Update all the references to the old taxes to the new ones.
|
||
|
- Fiscal positions: The previous mappings won't be deleted but the new ones will be created.
|
||
|
This ensures that reports still work.
|
||
|
- Company: The default sales/purchase taxes should be updated. It should only impact the creation
|
||
|
of new products so it is probably not going to be an issue.
|
||
|
* If such a tax was part of a group, a new group must be created and the old one removed from the template.
|
||
|
|
||
|
The procedure for the users, when they are ready, is:
|
||
|
* When the old taxes are not needed anymore (i.e. tax period closed), archive them
|
||
|
* Update the taxes on
|
||
|
- products
|
||
|
- accounts
|
||
|
- reconcile models
|
||
|
"""
|
||
|
def local_get_data(self, template_code):
|
||
|
# Delete the existing tax and create a new one with a different rate
|
||
|
data = test_get_data(self, template_code)
|
||
|
del data['account.tax']['test_tax_1_template']
|
||
|
data['account.tax']['test_tax_3_template'] = _tax_vals('Tax 3', 30)
|
||
|
for fpos in data['account.fiscal.position'].values():
|
||
|
for _command, _id, tax in fpos['tax_ids']:
|
||
|
if tax['tax_src_id'] == 'test_tax_1_template':
|
||
|
tax['tax_src_id'] = 'test_tax_3_template'
|
||
|
if tax['tax_dest_id'] == 'test_tax_1_template':
|
||
|
tax['tax_dest_id'] = 'test_tax_3_template'
|
||
|
data['res.company'][self.env.company.id]['account_sale_tax_id'] = 'test_tax_3_template'
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
# On an existing company all the taxes are still present.
|
||
|
# The user has to deactivate them manually when not needed anymore (if they needed to encode things in the past)
|
||
|
taxes = self.env['account.tax'].search([('company_id', '=', self.company.id)])
|
||
|
self.assertRecordValues(taxes, [
|
||
|
{'name': 'Tax 1'},
|
||
|
{'name': 'Tax 2'},
|
||
|
{'name': 'Tax 3'},
|
||
|
])
|
||
|
|
||
|
tax_1, tax_2, tax_3 = taxes
|
||
|
fiscal_position = self.env['account.fiscal.position'].search([('company_id', '=', self.company.id)])
|
||
|
self.assertRecordValues(fiscal_position.tax_ids, [
|
||
|
{'tax_src_id': tax_1.id, 'tax_dest_id': tax_2.id},
|
||
|
{'tax_src_id': tax_3.id, 'tax_dest_id': tax_2.id},
|
||
|
])
|
||
|
self.assertEqual(self.company.account_sale_tax_id, tax_3)
|
||
|
|
||
|
# On a new company you would never see the old tax.
|
||
|
# In case users need it, they can duplicate the new one and change the rate.
|
||
|
new_company = self.env['res.company'].create({'name': 'New Company'})
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=new_company, install_demo=False)
|
||
|
|
||
|
taxes = self.env['account.tax'].search([('company_id', '=', new_company.id)])
|
||
|
self.assertRecordValues(taxes, [
|
||
|
{'name': 'Tax 2'},
|
||
|
{'name': 'Tax 3'},
|
||
|
])
|
||
|
|
||
|
tax_2, tax_3 = taxes
|
||
|
fiscal_position = self.env['account.fiscal.position'].search([('company_id', '=', new_company.id)])
|
||
|
self.assertRecordValues(fiscal_position.tax_ids, [
|
||
|
{'tax_src_id': tax_3.id, 'tax_dest_id': tax_2.id},
|
||
|
])
|
||
|
self.assertEqual(new_company.account_sale_tax_id, tax_3)
|
||
|
|
||
|
|
||
|
def test_update_taxes_update(self):
|
||
|
""" When a tax is close enough from an existing tax we want to update that tax with the new values. """
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.account.tag']['account.account_tax_tag_1']['name'] += ' [DUP]'
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
updated_tax = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', 'like', '%Tax 1')])
|
||
|
# Check that tax was not recreated
|
||
|
self.assertEqual(len(updated_tax), 1)
|
||
|
# Check that tags have been updated
|
||
|
self.assertEqual(updated_tax.invoice_repartition_line_ids.tag_ids.name, 'tax_tag_name_1 [DUP]')
|
||
|
|
||
|
def test_update_taxes_update_rounding(self):
|
||
|
"""
|
||
|
When a tax is close enough to an existing tax but has a minor rounding error,
|
||
|
we still want to update that tax with the new values.
|
||
|
"""
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.account.tag']['account.account_tax_tag_1']['name'] += ' [DUP]'
|
||
|
# We compare up to the precision of the field, which is 4 decimals
|
||
|
data['account.tax']['test_tax_1_template']['amount'] += 0.00001
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
updated_tax = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', 'like', '%Tax 1')])
|
||
|
# Check that tax was not recreated
|
||
|
self.assertEqual(len(updated_tax), 1)
|
||
|
# Check that tags have been updated
|
||
|
self.assertEqual(updated_tax.invoice_repartition_line_ids.tag_ids.name, 'tax_tag_name_1 [DUP]')
|
||
|
|
||
|
def test_update_taxes_recreation(self):
|
||
|
""" When a tax is too different from an existing tax we want to recreate a new tax with new values. """
|
||
|
def local_get_data(self, template_code):
|
||
|
# We increment the amount so the template gets slightly different from the
|
||
|
# corresponding tax and triggers recreation
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.tax']['test_tax_1_template']['name'] = 'Tax 1 modified'
|
||
|
data['account.tax']['test_tax_1_template']['amount'] += 1
|
||
|
return data
|
||
|
|
||
|
tax_existing = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', 'Tax 1')])
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
# Check that old tax has not been changed beside of the name prefixed by [old]
|
||
|
self.assertRecordValues(tax_existing, [{'name': '[old] Tax 1', 'amount': 15}])
|
||
|
|
||
|
# Check that new tax has been recreated
|
||
|
new_tax = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', 'Tax 1 modified')])
|
||
|
self.assertEqual(new_tax.amount, tax_existing.amount + 1)
|
||
|
|
||
|
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'].search([])
|
||
|
fiscal_position.tax_ids.unlink()
|
||
|
self.env['account.tax'].search([('company_id', '=', self.company.id)]).unlink()
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
# 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.id)])), 2)
|
||
|
self.assertEqual(len(fiscal_position.tax_ids), 1)
|
||
|
|
||
|
fiscal_position.tax_ids.unlink()
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
# if only the fiscal position mapping has been removed, it won't be recreated
|
||
|
self.assertEqual(len(fiscal_position.tax_ids), 0)
|
||
|
|
||
|
def test_update_taxes_conflict_name(self):
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.tax']['test_tax_1_template']['amount'] = 40
|
||
|
return data
|
||
|
|
||
|
def local_get_data2(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.tax']['test_tax_1_template']['amount'] = 15
|
||
|
return data
|
||
|
|
||
|
tax_1_existing = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "Tax 1")])
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
tax_1_old = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "[old] Tax 1")])
|
||
|
tax_1_new = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "Tax 1")])
|
||
|
self.assertEqual(tax_1_old, tax_1_existing, "Old tax still exists but with a different name.")
|
||
|
self.assertEqual(len(tax_1_new), 1, "New tax have been created with the original name.")
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data2, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
tax_1_old_first = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "[old] Tax 1")])
|
||
|
tax_1_old_second = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "[old1] Tax 1")])
|
||
|
tax_1_latest = self.env['account.tax'].search([('company_id', '=', self.company.id), ('name', '=', "Tax 1")])
|
||
|
|
||
|
self.assertEqual(tax_1_old, tax_1_old_first, "Old renamed tax is still the same.")
|
||
|
self.assertEqual(tax_1_old_second, tax_1_new, "Outdated tax is renamed again.")
|
||
|
self.assertEqual(len(tax_1_latest), 1, "New tax have been created with the original name.")
|
||
|
|
||
|
def test_update_taxes_multi_company(self):
|
||
|
""" In a multi-company environment all companies should be correctly updated."""
|
||
|
def local_get_data(self, template_code):
|
||
|
# triggers recreation of tax 1
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.tax']['test_tax_1_template']['amount'] += 1
|
||
|
return data
|
||
|
|
||
|
company_2 = self.env['res.company'].create({
|
||
|
'name': 'TestCompany2',
|
||
|
'country_id': self.env.ref('base.be').id,
|
||
|
})
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=company_2, install_demo=False)
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
self.env['account.chart.template'].try_loading('test', company=company_2, install_demo=False)
|
||
|
|
||
|
taxes_1_companies = self.env['account.tax'].search([
|
||
|
('name', '=like', '%Tax 1'),
|
||
|
('company_id', 'in', [self.company.id, company_2.id]),
|
||
|
])
|
||
|
# we should have 4 records: 2 companies * (1 original tax + 1 recreated tax)
|
||
|
self.assertEqual(len(taxes_1_companies), 4)
|
||
|
|
||
|
def test_update_account_codes_conflict(self):
|
||
|
# Change code of an existing account to something else
|
||
|
standard_account = self.env['account.chart.template'].ref('test_account_income_template')
|
||
|
standard_account.code = '111111'
|
||
|
|
||
|
# create a new account with the same code as an existing one
|
||
|
problematic_account = self.env['account.account'].create({
|
||
|
'code': '222221',
|
||
|
'name': 'problematic_account',
|
||
|
})
|
||
|
|
||
|
# remove an xmlid to see if it gets relinked and not duplicated
|
||
|
self.env['ir.model.data'].search([
|
||
|
('name', '=', f'{self.company.id}_test_account_expense_template'),
|
||
|
('module', '=', 'account'),
|
||
|
]).unlink()
|
||
|
|
||
|
# reload chart template
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
# check that xmlid is now pointing to problematic_account
|
||
|
xmlid_account = self.env.ref(f'account.{self.company.id}_test_account_income_template')
|
||
|
self.assertEqual(problematic_account, xmlid_account, "xmlid is not pointing to the right account")
|
||
|
|
||
|
def test_update_taxes_children_tax_ids(self):
|
||
|
""" Ensures children_tax_ids are correctly generated when updating taxes with
|
||
|
amount_type='group'.
|
||
|
"""
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
normal_tax_xmlids = ['test_tax_3_template', 'test_tax_4_template']
|
||
|
data['account.tax'].update({
|
||
|
xmlid: _tax_vals(name, amount, children_tax_xmlids=children_tax_xmlids)
|
||
|
for name, xmlid, amount, children_tax_xmlids in [
|
||
|
('Tax 3', normal_tax_xmlids[0], 16, None),
|
||
|
('Tax 4', normal_tax_xmlids[1], 17, None),
|
||
|
('Tax with children', 'test_tax_5_group_template', 0, normal_tax_xmlids)
|
||
|
]
|
||
|
})
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
parent_tax = self.env['account.tax'].search([
|
||
|
('company_id', '=', self.company.id),
|
||
|
('name', '=', 'Tax with children'),
|
||
|
])
|
||
|
children_taxes = self.env['account.tax'].search([
|
||
|
('company_id', '=', self.company.id),
|
||
|
('name', 'in', ['Tax 3', 'Tax 4']),
|
||
|
])
|
||
|
self.assertEqual(len(parent_tax), 1, "The parent tax should have been created.")
|
||
|
self.assertEqual(len(children_taxes), 2, "Two children should have been created.")
|
||
|
self.assertEqual(parent_tax.children_tax_ids.ids, children_taxes.ids, "The parent and its children taxes should be linked together.")
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
# We don't change anything
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
self.assertEqual(parent_tax.name, 'Tax with children', "The parent tax created before should not have changed")
|
||
|
|
||
|
def test_update_taxes_children_tax_ids_inactive(self):
|
||
|
""" Ensure tax templates are correctly generated when updating taxes with children taxes,
|
||
|
even if templates are inactive.
|
||
|
"""
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
normal_tax_xmlids = ['test_tax_3_template', 'test_tax_4_template']
|
||
|
data['account.tax'].update({
|
||
|
xmlid: _tax_vals(name, amount, children_tax_xmlids=children_tax_xmlids, active=active)
|
||
|
for name, xmlid, amount, children_tax_xmlids, active in [
|
||
|
('Inactive Tax 3', normal_tax_xmlids[0], 16, None, False),
|
||
|
('Inactive Tax 4', normal_tax_xmlids[1], 17, None, False),
|
||
|
('Inactive Tax with children', 'test_tax_5_group_template', 0, normal_tax_xmlids, False)
|
||
|
]
|
||
|
})
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
parent_tax = self.env['account.tax'].with_context(active_test=False).search([
|
||
|
('company_id', '=', self.company.id),
|
||
|
('name', '=', 'Inactive Tax with children'),
|
||
|
])
|
||
|
children_taxes = self.env['account.tax'].with_context(active_test=False).search([
|
||
|
('company_id', '=', self.company.id),
|
||
|
('name', 'in', ['Inactive Tax 3', 'Inactive Tax 4']),
|
||
|
])
|
||
|
self.assertEqual(len(parent_tax), 1, "The parent tax should have been created, even if it is inactive.")
|
||
|
self.assertFalse(parent_tax.active, "The parent tax should be inactive.")
|
||
|
self.assertEqual(len(children_taxes), 2, "Two children should have been created, even if they are inactive.")
|
||
|
self.assertEqual(children_taxes.mapped('active'), [False] * 2, "Children taxes should be inactive.")
|
||
|
|
||
|
def test_update_reload_no_new_data(self):
|
||
|
""" Tests that the reload does nothing when data are left unchanged.
|
||
|
Tested models: account.group, account.account, account.tax.group, account.tax, account.journal,
|
||
|
account.reconcile.model, account.fiscal.position, account.fiscal.position.tax, account.tax.repartition.line,
|
||
|
account.account.tag.
|
||
|
"""
|
||
|
def get_domain(model):
|
||
|
if model == 'account.account.tag':
|
||
|
return [('country_id', '=', self.company.country_id.id)]
|
||
|
elif model == 'account.account':
|
||
|
return [('company_ids', '=', self.company.id)]
|
||
|
else:
|
||
|
return [('company_id', '=', self.company.id)]
|
||
|
|
||
|
sub_models = ('account.fiscal.position.tax', 'account.tax.repartition.line', 'account.account.tag')
|
||
|
data_before = {}
|
||
|
for model in TEMPLATE_MODELS + sub_models:
|
||
|
data_before[model] = self.env[model].search(get_domain(model))
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
for model in TEMPLATE_MODELS + sub_models:
|
||
|
data_after = self.env[model].search(get_domain(model))
|
||
|
self.assertEqual(data_before[model], data_after)
|
||
|
|
||
|
def test_unknown_company_fields(self):
|
||
|
""" Tests that if a key is not known in the company template data when the
|
||
|
context value 'l10n_check_fields_complete' is set, an error is raised. If a
|
||
|
key is not known in the company template data but the context value is not
|
||
|
set, that key is skipped and no error is raised."""
|
||
|
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['res.company'][company.id]['unknown_company_key'] = 'unknown_company_value'
|
||
|
return data
|
||
|
|
||
|
company = self.company
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
# hard fail the loading if the context key is set to ensure `test_all_l10n` works as expected
|
||
|
with (
|
||
|
self.assertRaisesRegex(ValueError, 'unknown_company_key'),
|
||
|
self.env.cr.savepoint(),
|
||
|
):
|
||
|
self.env['account.chart.template'].with_context(l10n_check_fields_complete=True).try_loading('test', company=company, install_demo=False)
|
||
|
|
||
|
# silently ignore if the field doesn't exist (yet)
|
||
|
self.env['account.chart.template'].try_loading('test', company=company, install_demo=False)
|
||
|
|
||
|
def test_branch(self):
|
||
|
# Test the auto-installation of a chart template (including demo data) on a branch
|
||
|
# Create a new main company, because install_demo doesn't do anything when reloading data
|
||
|
company = self.env['res.company'].create([{'name': 'Test Company'}])
|
||
|
branch = self.env['res.company'].create([{
|
||
|
'name': 'Test Branch',
|
||
|
'parent_id': company.id,
|
||
|
}])
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=company, install_demo=True)
|
||
|
self.assertEqual(company.chart_template, 'test')
|
||
|
self.assertEqual(branch.chart_template, 'test')
|
||
|
|
||
|
def test_change_coa(self):
|
||
|
def _get_chart_template_mapping(self, get_all=False):
|
||
|
return {'other_test': {
|
||
|
'name': 'test',
|
||
|
'country_id': None,
|
||
|
'country_code': None,
|
||
|
'module': 'account',
|
||
|
'parent': None,
|
||
|
}}
|
||
|
|
||
|
# Check that company fields that should depend on CoA are reset when changing CoA
|
||
|
# (afaik there is only `anglo_saxon_accounting`)
|
||
|
self.company.anglo_saxon_accounting = True
|
||
|
|
||
|
with (
|
||
|
patch.object(AccountChartTemplate, '_get_chart_template_mapping', _get_chart_template_mapping),
|
||
|
patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True)
|
||
|
):
|
||
|
self.env['account.chart.template'].try_loading('other_test', company=self.company, install_demo=True)
|
||
|
|
||
|
# Create a branch and an unrelated company
|
||
|
branch, other_company = self.env['res.company'].create([
|
||
|
{
|
||
|
'name': 'Test Branch',
|
||
|
'parent_id': self.company.id,
|
||
|
},
|
||
|
{
|
||
|
'name': 'Other Test Company',
|
||
|
},
|
||
|
])
|
||
|
# Run precommit hook to load the template on the branch
|
||
|
self.env.cr.precommit.run()
|
||
|
|
||
|
self.assertEqual(self.company.chart_template, 'other_test')
|
||
|
self.assertEqual(branch.chart_template, 'other_test')
|
||
|
self.assertFalse(self.company.anglo_saxon_accounting)
|
||
|
|
||
|
# Setup a shared account, belonging to the company, the branch, and the unrelated company
|
||
|
shared_account = self.env['account.account'].create([{
|
||
|
'name': 'Shared Account',
|
||
|
'company_ids': [Command.set((self.company | branch | other_company).ids)],
|
||
|
'code_mapping_ids': [
|
||
|
Command.create({'company_id': self.company.id, 'code': '180001'}),
|
||
|
Command.create({'company_id': branch.id, 'code': '180001'}),
|
||
|
Command.create({'company_id': other_company.id, 'code': '180001'}),
|
||
|
],
|
||
|
}])
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=True)
|
||
|
self.assertEqual(self.company.chart_template, 'test')
|
||
|
self.assertEqual(branch.chart_template, 'test')
|
||
|
|
||
|
# Check that the shared account was not deleted, but just unlinked from the company and the branch.
|
||
|
self.assertEqual(shared_account.company_ids, other_company)
|
||
|
|
||
|
def test_update_tax_with_non_existent_tag(self):
|
||
|
""" Tests that when we update the CoA with a tax that has a tag that does not exist yet we raise an error.
|
||
|
Typical use case is when the code got updated but the module haven't been updated (-u).
|
||
|
"""
|
||
|
tax_to_load = {
|
||
|
'name': 'Mixed Tags Tax',
|
||
|
'amount': 30,
|
||
|
'amount_type': 'percent',
|
||
|
'tax_group_id': 'tax_group_taxes',
|
||
|
'active': True,
|
||
|
'repartition_line_ids': [
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'base', 'tag_ids': '+SIGNED_TAG'}),
|
||
|
Command.create({'document_type': 'invoice', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'base'}),
|
||
|
Command.create({'document_type': 'refund', 'factor_percent': 100, 'repartition_type': 'tax'}),
|
||
|
]
|
||
|
}
|
||
|
with self.assertRaisesRegex(UserError, 'update your localization'):
|
||
|
self.env['account.chart.template']._deref_account_tags('test', {'tax1': tax_to_load})
|
||
|
|
||
|
def test_install_with_translations(self):
|
||
|
""" Ensure that the translations are loaded correctly when installing chart data; i.e. test '_load_translations' and that the untranslatable fields are translated correctly.
|
||
|
Note: The '_load_translations' function depends on the '_get_chart_template_data' function for some information.
|
||
|
The result of '_get_chart_template_data' is mocked (correctly) in this test (and not tested).
|
||
|
"""
|
||
|
|
||
|
# Local mock for '_get_chart_template_mapping'
|
||
|
# We will use / install a dedicated new chart 'translation' (not just reload 'test')
|
||
|
# To have control over the original / en_US values.
|
||
|
def local_get_mapping(self, get_all=False):
|
||
|
return {'translation': {
|
||
|
'name': 'translation',
|
||
|
'country_id': None,
|
||
|
'country_code': None,
|
||
|
'modules': ['account'],
|
||
|
'parent': None,
|
||
|
}}
|
||
|
|
||
|
company = self.company
|
||
|
|
||
|
# Create records that are not part of the chart template
|
||
|
# They will be translated via code translations.
|
||
|
# The module used to source the translation is the module from the xml_id or 'account' (as fallback)
|
||
|
|
||
|
non_chart_data = {
|
||
|
'account.group': {
|
||
|
# try module 'no_translation'; fallback to 'account'
|
||
|
'no_translation.test_chart_template_company_test_free_account_group': {
|
||
|
'name': 'Free Account Group',
|
||
|
'code_prefix_start': 333330,
|
||
|
'code_prefix_end': 333339,
|
||
|
'company_id': company.id,
|
||
|
},
|
||
|
},
|
||
|
'account.account': {
|
||
|
# translate via 'translation' module
|
||
|
'translation.test_chart_template_company_test_free_account': {
|
||
|
'name': 'Free Account',
|
||
|
'code': '333331',
|
||
|
'account_type': 'asset_current',
|
||
|
'company_ids': [Command.link(company.id)],
|
||
|
},
|
||
|
},
|
||
|
'account.tax': {
|
||
|
# translate via 'translation' module;
|
||
|
# 2 translatable fields ('name' and 'description')
|
||
|
'translation.test_chart_template_company_test_free_tax': {
|
||
|
"name": "Free Tax",
|
||
|
"description": "Free Tax Description",
|
||
|
"amount": "0.00",
|
||
|
"company_id": company.id,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
# Local function to "extend" '_post_load_data' to ensure the creation of the records from 'non_chart_data'
|
||
|
def test_post_load_data(template_code, company, template_data):
|
||
|
for model, data in non_chart_data.items():
|
||
|
for xml_id, values in data.items():
|
||
|
self.env[model]._load_records([{
|
||
|
'xml_id': xml_id,
|
||
|
'values': values,
|
||
|
}])
|
||
|
|
||
|
# Create a local mock of '_get_chart_template_data'; "extend" 'test_get_data' with the translation info
|
||
|
|
||
|
translation_update_for_test_get_data = {
|
||
|
# Use code translations from module 'translation'
|
||
|
'account.journal': {
|
||
|
'cash': {
|
||
|
'name': "Cash",
|
||
|
'code': "C", # untranslatable field; shortened due to length restriction (for _translation)
|
||
|
'__translation_module__': {
|
||
|
'name': 'translation',
|
||
|
'code': 'translation',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
# Different modules for code translations of 'name' and 'description'
|
||
|
'account.tax': {
|
||
|
'test_tax_1_template': {
|
||
|
'name': "Tax 1",
|
||
|
'description': "Tax 1 Description",
|
||
|
'__translation_module__': {
|
||
|
'name': 'translation',
|
||
|
'description': 'translation2',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
# Use 'name@' and not code translation
|
||
|
'account.tax.group': {
|
||
|
'tax_group_taxes': {
|
||
|
'name': "Taxes",
|
||
|
'name@fr': "Taxes FR",
|
||
|
'__translation_module__': {
|
||
|
'name': 'translation',
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
for model, record_info in translation_update_for_test_get_data.items():
|
||
|
for xmlid, data_update in record_info.items():
|
||
|
data[model][xmlid].update(data_update)
|
||
|
return data
|
||
|
|
||
|
# Tranlations should fall back to more generic locale 'fr'
|
||
|
|
||
|
# Target lang for untranslatable fields
|
||
|
company.partner_id.lang = self.env['res.lang']._activate_lang('fr_BE').code
|
||
|
|
||
|
# Init empty mock translations to make sure we do not use unintended translation
|
||
|
mock_python_translations = {}
|
||
|
|
||
|
for module, lang, value, translation in [
|
||
|
# wrong translations
|
||
|
('translation', 'fr', "Taxes", "WRONG"), # there is a value in the chart data
|
||
|
('translation', 'fr', "Free Account", "Free Account FR"), # there is a value for fr_BE
|
||
|
# correct translations
|
||
|
('translation', 'fr', "Cash", "Cash FR"),
|
||
|
('translation', 'fr', "C", "C FR"),
|
||
|
('translation', 'fr', "Tax 1", "Tax 1 FR"),
|
||
|
('translation', 'fr_BE', "Free Account", "Free Account FR_BE"),
|
||
|
('translation', 'fr', "Free Tax", "Free Tax FR"),
|
||
|
('translation', 'fr', "Free Tax Description", "Free Tax Description FR"),
|
||
|
('translation2', 'fr', "Tax 1 Description", "Tax 1 Description translation2/FR"),
|
||
|
('account', 'fr', "Free Account Group", "Free Account Group account/FR"),
|
||
|
]:
|
||
|
mock_python_translations.setdefault((module, lang), {})[value] = translation
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_mapping', side_effect=local_get_mapping, autospec=True):
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
with patch.object(AccountChartTemplate, '_post_load_data', wraps=test_post_load_data):
|
||
|
with patch.object(code_translations, 'python_translations', mock_python_translations):
|
||
|
self.env['account.chart.template'].try_loading('translation', company=company, install_demo=False)
|
||
|
|
||
|
# Check translations
|
||
|
translatable_model_fields = self.env['account.chart.template']._get_translatable_template_model_fields()
|
||
|
untranslatable_model_fields = self.env['account.chart.template']._get_untranslatable_fields_to_translate()
|
||
|
fields_to_translate = {
|
||
|
model: set(translatable_model_fields.get(model, []) + untranslatable_model_fields.get(model, []))
|
||
|
for model in TEMPLATE_MODELS
|
||
|
}
|
||
|
|
||
|
self.assertEqual({
|
||
|
f'{xmlid}.{field}@{lang}': self.env['account.chart.template'].ref(xmlid).with_context(lang=lang)[field]
|
||
|
for chart_like_data in [non_chart_data, translation_update_for_test_get_data]
|
||
|
for model, data in chart_like_data.items()
|
||
|
for xmlid, record_data in data.items()
|
||
|
for field in record_data if field in fields_to_translate.get(model, set())
|
||
|
for lang in ['en_US', 'fr_BE']
|
||
|
}, {
|
||
|
'cash.code@en_US': 'C FR', # untranslatable field loaded in lang fr_BE
|
||
|
'cash.code@fr_BE': 'C FR',
|
||
|
'cash.name@en_US': 'Cash',
|
||
|
'cash.name@fr_BE': 'Cash FR',
|
||
|
'no_translation.test_chart_template_company_test_free_account_group.name@en_US': 'Free Account Group',
|
||
|
'no_translation.test_chart_template_company_test_free_account_group.name@fr_BE': 'Free Account Group account/FR', # fallback to account
|
||
|
'tax_group_taxes.name@en_US': 'Taxes',
|
||
|
'tax_group_taxes.name@fr_BE': 'Taxes FR',
|
||
|
'test_tax_1_template.description@en_US': Markup('<p>Tax 1 Description</p>'),
|
||
|
'test_tax_1_template.description@fr_BE': Markup('Tax 1 Description translation2/FR'),
|
||
|
'test_tax_1_template.name@en_US': 'Tax 1',
|
||
|
'test_tax_1_template.name@fr_BE': 'Tax 1 FR',
|
||
|
'translation.test_chart_template_company_test_free_account.name@en_US': 'Free Account',
|
||
|
'translation.test_chart_template_company_test_free_account.name@fr_BE': 'Free Account FR_BE', # do not use generic lang
|
||
|
'translation.test_chart_template_company_test_free_tax.description@en_US': Markup('<p>Free Tax Description</p>'),
|
||
|
'translation.test_chart_template_company_test_free_tax.description@fr_BE': Markup('<p>Free Tax Description</p>'),
|
||
|
'translation.test_chart_template_company_test_free_tax.name@en_US': 'Free Tax',
|
||
|
'translation.test_chart_template_company_test_free_tax.name@fr_BE': 'Free Tax FR',
|
||
|
})
|
||
|
|
||
|
def test_parsed_csv_submodel_being_loaded(self):
|
||
|
def get_rep_line_data(x):
|
||
|
return (x.document_type, x.repartition_type, x.factor_percent, x.use_in_tax_closing)
|
||
|
|
||
|
with patch('odoo.addons.account.models.chart_template.file_open',
|
||
|
side_effect=lambda *args: io.StringIO(CSV_DATA['tax_1'])):
|
||
|
data = {'account.tax': self.ChartTemplate._get_account_tax('test')}
|
||
|
self.ChartTemplate._load_data(data)
|
||
|
|
||
|
tax_1 = self.env.ref(f'account.{self.company.id}_tax_1', raise_if_not_found=False)
|
||
|
tax_rep_lines = tax_1.repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
|
||
|
self.assertEqual([
|
||
|
('invoice', 'tax', 50.0, False),
|
||
|
('invoice', 'tax', 50.0, False),
|
||
|
('refund', 'tax', 50.0, False),
|
||
|
('refund', 'tax', 50.0, False),
|
||
|
], tax_rep_lines.mapped(get_rep_line_data))
|
||
|
|
||
|
def test_parsed_csv_submodel_being_updated(self):
|
||
|
def local_get_data(self, template_code):
|
||
|
return {
|
||
|
**test_get_data(self, template_code),
|
||
|
'account.tax': {
|
||
|
xmlid: _tax_vals(name, amount)
|
||
|
for name, xmlid, amount in [
|
||
|
('Tax 1', 'test_tax_1_template', 15),
|
||
|
('Tax 2', 'test_tax_2_template', 0),
|
||
|
('Tax 3', 'test_tax_3_template', 16),
|
||
|
('Tax 4', 'test_tax_4_template', 17),
|
||
|
]
|
||
|
},
|
||
|
}
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
with patch('odoo.addons.account.models.chart_template.file_open',
|
||
|
side_effect=lambda *args: io.StringIO(CSV_DATA['test_fiscal_position_template'])):
|
||
|
data = {'account.fiscal.position': self.ChartTemplate._get_account_fiscal_position('test')}
|
||
|
self.ChartTemplate._pre_reload_data(self.company, {}, data)
|
||
|
self.ChartTemplate._load_data(data)
|
||
|
|
||
|
def test_command_int_values(self):
|
||
|
""" Command int values should just work in place of their Enum alternatives. """
|
||
|
def local_get_data(self, template_code):
|
||
|
data = test_get_data(self, template_code)
|
||
|
data['account.account'].update({
|
||
|
"test_account": {
|
||
|
'name': "Test account A",
|
||
|
'code': '777777',
|
||
|
'account_type': 'income_other',
|
||
|
'tag_ids': [(6, 0, self.ref('account.account_tag_investing').ids)],
|
||
|
},
|
||
|
"test_account_2": {
|
||
|
'name': "Test account B",
|
||
|
'code': '777778',
|
||
|
'account_type': 'income_other',
|
||
|
'tag_ids': [
|
||
|
(5, 0, 0),
|
||
|
(0, 0, {'name': 'Test account tag', 'applicability': 'accounts'}),
|
||
|
(0, 0, {'name': 'Test account tag 2', 'applicability': 'accounts'}),
|
||
|
]}
|
||
|
})
|
||
|
return data
|
||
|
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=local_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=self.company, install_demo=False)
|
||
|
|
||
|
accounts = self.env['account.account'].search([
|
||
|
('company_ids', '=', self.company.id),
|
||
|
('code', 'in', ('777777', '777778'))
|
||
|
], order='code asc')
|
||
|
self.assertEqual(2, len(accounts))
|
||
|
self.assertEqual(self.env.ref('account.account_tag_investing'), accounts[0].tag_ids)
|
||
|
self.assertEqual({'Test account tag', 'Test account tag 2'}, set(accounts[1].tag_ids.mapped("name")))
|
||
|
|
||
|
def test_chart_template_company_without_country(self):
|
||
|
"""
|
||
|
In this test we will try to install a chart template to a company without a country. The expected behavior
|
||
|
is that the country of the chart template will be set on the company
|
||
|
"""
|
||
|
company = self.env['res.company'].create({'name': 'Test Company Without country'})
|
||
|
self.assertFalse(company.country_id)
|
||
|
with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
|
||
|
self.env['account.chart.template'].try_loading('test', company=company, install_demo=False)
|
||
|
self.assertEqual(company.country_id.code, "BE")
|