summaryrefslogtreecommitdiff
path: root/addons/account/tests/test_invoice_taxes.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/account/tests/test_invoice_taxes.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/account/tests/test_invoice_taxes.py')
-rw-r--r--addons/account/tests/test_invoice_taxes.py669
1 files changed, 669 insertions, 0 deletions
diff --git a/addons/account/tests/test_invoice_taxes.py b/addons/account/tests/test_invoice_taxes.py
new file mode 100644
index 00000000..83b9faa2
--- /dev/null
+++ b/addons/account/tests/test_invoice_taxes.py
@@ -0,0 +1,669 @@
+# -*- coding: utf-8 -*-
+from odoo.addons.account.tests.common import AccountTestInvoicingCommon
+from odoo.tests import tagged, Form
+
+
+@tagged('post_install', '-at_install')
+class TestInvoiceTaxes(AccountTestInvoicingCommon):
+
+ @classmethod
+ def setUpClass(cls, chart_template_ref=None):
+ super().setUpClass(chart_template_ref=chart_template_ref)
+
+ cls.company_data['company'].country_id = cls.env.ref('base.us')
+
+ cls.percent_tax_1 = cls.env['account.tax'].create({
+ 'name': '21%',
+ 'amount_type': 'percent',
+ 'amount': 21,
+ 'sequence': 10,
+ })
+ cls.percent_tax_1_incl = cls.env['account.tax'].create({
+ 'name': '21% incl',
+ 'amount_type': 'percent',
+ 'amount': 21,
+ 'price_include': True,
+ 'include_base_amount': True,
+ 'sequence': 20,
+ })
+ cls.percent_tax_2 = cls.env['account.tax'].create({
+ 'name': '12%',
+ 'amount_type': 'percent',
+ 'amount': 12,
+ 'sequence': 30,
+ })
+ cls.percent_tax_3_incl = cls.env['account.tax'].create({
+ 'name': '5% incl',
+ 'amount_type': 'percent',
+ 'amount': 5,
+ 'price_include': True,
+ 'include_base_amount': True,
+ 'sequence': 40,
+ })
+ cls.group_tax = cls.env['account.tax'].create({
+ 'name': 'group 12% + 21%',
+ 'amount_type': 'group',
+ 'amount': 21,
+ 'children_tax_ids': [
+ (4, cls.percent_tax_1_incl.id),
+ (4, cls.percent_tax_2.id)
+ ],
+ 'sequence': 40,
+ })
+
+ cls.tax_report = cls.env['account.tax.report'].create({
+ 'name': "Tax report",
+ 'country_id': cls.company_data['company'].country_id.id,
+ })
+
+ cls.tax_report_line = cls.env['account.tax.report.line'].create({
+ 'name': 'test_tax_report_line',
+ 'tag_name': 'test_tax_report_line',
+ 'report_id': cls.tax_report.id,
+ 'sequence': 10,
+ })
+ cls.tax_tag_pos = cls.tax_report_line.tag_ids.filtered(lambda x: not x.tax_negate)
+ cls.tax_tag_neg = cls.tax_report_line.tag_ids.filtered(lambda x: x.tax_negate)
+ cls.base_tax_report_line = cls.env['account.tax.report.line'].create({
+ 'name': 'base_test_tax_report_line',
+ 'tag_name': 'base_test_tax_report_line',
+ 'report_id': cls.tax_report.id,
+ 'sequence': 10,
+ })
+ cls.base_tag_pos = cls.base_tax_report_line.tag_ids.filtered(lambda x: not x.tax_negate)
+ cls.base_tag_neg = cls.base_tax_report_line.tag_ids.filtered(lambda x: x.tax_negate)
+
+ def _create_invoice(self, taxes_per_line, inv_type='out_invoice', currency_id=False, invoice_payment_term_id=False):
+ ''' Create an invoice on the fly.
+
+ :param taxes_per_line: A list of tuple (price_unit, account.tax recordset)
+ '''
+ vals = {
+ 'move_type': inv_type,
+ 'partner_id': self.partner_a.id,
+ 'invoice_line_ids': [(0, 0, {
+ 'name': 'xxxx',
+ 'quantity': 1,
+ 'price_unit': amount,
+ 'tax_ids': [(6, 0, taxes.ids)],
+ }) for amount, taxes in taxes_per_line],
+ }
+ if currency_id:
+ vals['currency_id'] = currency_id.id
+ if invoice_payment_term_id:
+ vals['invoice_payment_term_id'] = invoice_payment_term_id.id
+ return self.env['account.move'].create(vals)
+
+ def test_one_tax_per_line(self):
+ ''' Test:
+ price_unit | Taxes
+ ------------------
+ 100 | 21%
+ 121 | 21% incl
+ 100 | 12%
+
+ Expected:
+ Tax | Taxes | Base | Amount
+ --------------------------------------------
+ 21% | / | 100 | 21
+ 21% incl | / | 100 | 21
+ 12% | / | 100 | 12
+ '''
+ invoice = self._create_invoice([
+ (100, self.percent_tax_1),
+ (121, self.percent_tax_1_incl),
+ (100, self.percent_tax_2),
+ ])
+ invoice.action_post()
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id'), [
+ {'name': self.percent_tax_1.name, 'tax_base_amount': 100, 'price_unit': 21, 'tax_ids': []},
+ {'name': self.percent_tax_1_incl.name, 'tax_base_amount': 100, 'price_unit': 21, 'tax_ids': []},
+ {'name': self.percent_tax_2.name, 'tax_base_amount': 100, 'price_unit': 12, 'tax_ids': []},
+ ])
+
+ def test_affecting_base_amount(self):
+ ''' Test:
+ price_unit | Taxes
+ ------------------
+ 121 | 21% incl, 12%
+ 100 | 12%
+
+ Expected:
+ Tax | Taxes | Base | Amount
+ --------------------------------------------
+ 21% incl | 12% | 100 | 21
+ 12% | / | 121 | 14.52
+ 12% | / | 100 | 12
+ '''
+ invoice = self._create_invoice([
+ (121, self.percent_tax_1_incl + self.percent_tax_2),
+ (100, self.percent_tax_2),
+ ])
+ invoice.action_post()
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id').sorted(lambda x: x.price_unit), [
+ {'name': self.percent_tax_1_incl.name, 'tax_base_amount': 100, 'price_unit': 21, 'tax_ids': [self.percent_tax_2.id]},
+ {'name': self.percent_tax_2.name, 'tax_base_amount': 221, 'price_unit': 26.52, 'tax_ids': []},
+ ])
+
+ def test_group_of_taxes(self):
+ ''' Test:
+ price_unit | Taxes
+ ------------------
+ 121 | 21% incl + 12%
+ 100 | 12%
+
+ Expected:
+ Tax | Taxes | Base | Amount
+ --------------------------------------------
+ 21% incl | / | 100 | 21
+ 12% | 21% incl | 121 | 14.52
+ 12% | / | 100 | 12
+ '''
+ invoice = self._create_invoice([
+ (121, self.group_tax),
+ (100, self.percent_tax_2),
+ ])
+ invoice.action_post()
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id').sorted(lambda x: x.price_unit), [
+ {'name': self.percent_tax_1_incl.name, 'tax_base_amount': 100, 'price_unit': 21, 'tax_ids': [self.percent_tax_2.id]},
+ {'name': self.percent_tax_2.name, 'tax_base_amount': 221, 'price_unit': 26.52, 'tax_ids': []},
+ ])
+
+ def _create_tax_tag(self, tag_name):
+ return self.env['account.account.tag'].create({
+ 'name': tag_name,
+ 'applicability': 'taxes',
+ 'country_id': self.env.company.country_id.id,
+ })
+
+ def test_tax_repartition(self):
+ inv_base_tag = self._create_tax_tag('invoice_base')
+ inv_tax_tag_10 = self._create_tax_tag('invoice_tax_10')
+ inv_tax_tag_90 = self._create_tax_tag('invoice_tax_90')
+ ref_base_tag = self._create_tax_tag('refund_base')
+ ref_tax_tag = self._create_tax_tag('refund_tax')
+
+ user_type = self.env.ref('account.data_account_type_current_assets')
+ account_1 = self.env['account.account'].create({'name': 'test1', 'code': 'test1', 'user_type_id': user_type.id})
+ account_2 = self.env['account.account'].create({'name': 'test2', 'code': 'test2', 'user_type_id': user_type.id})
+
+ tax = self.env['account.tax'].create({
+ 'name': "Tax with account",
+ 'amount_type': 'fixed',
+ 'type_tax_use': 'sale',
+ 'amount': 42,
+ 'invoice_repartition_line_ids': [
+ (0,0, {
+ 'factor_percent': 100,
+ 'repartition_type': 'base',
+ 'tag_ids': [(4, inv_base_tag.id, 0)],
+ }),
+
+ (0,0, {
+ 'factor_percent': 10,
+ 'repartition_type': 'tax',
+ 'account_id': account_1.id,
+ 'tag_ids': [(4, inv_tax_tag_10.id, 0)],
+ }),
+
+ (0,0, {
+ 'factor_percent': 90,
+ 'repartition_type': 'tax',
+ 'account_id': account_2.id,
+ 'tag_ids': [(4, inv_tax_tag_90.id, 0)],
+ }),
+ ],
+ 'refund_repartition_line_ids': [
+ (0,0, {
+ 'factor_percent': 100,
+ 'repartition_type': 'base',
+ 'tag_ids': [(4, ref_base_tag.id, 0)],
+ }),
+
+ (0,0, {
+ 'factor_percent': 10,
+ 'repartition_type': 'tax',
+ 'tag_ids': [(4, ref_tax_tag.id, 0)],
+ }),
+
+ (0,0, {
+ 'factor_percent': 90,
+ 'repartition_type': 'tax',
+ 'account_id': account_1.id,
+ 'tag_ids': [(4, ref_tax_tag.id, 0)],
+ }),
+ ],
+ })
+
+ # Test invoice repartition
+ invoice = self._create_invoice([(100, tax)], inv_type='out_invoice')
+ invoice.action_post()
+
+ self.assertEqual(len(invoice.line_ids), 4, "There should be 4 account move lines created for the invoice: payable, base and 2 tax lines")
+ inv_base_line = invoice.line_ids.filtered(lambda x: not x.tax_repartition_line_id and x.account_id.user_type_id.type != 'receivable')
+ self.assertEqual(len(inv_base_line), 1, "There should be only one base line generated")
+ self.assertEqual(abs(inv_base_line.balance), 100, "Base amount should be 100")
+ self.assertEqual(inv_base_line.tax_tag_ids, inv_base_tag, "Base line should have received base tag")
+ inv_tax_lines = invoice.line_ids.filtered(lambda x: x.tax_repartition_line_id.repartition_type == 'tax')
+ self.assertEqual(len(inv_tax_lines), 2, "There should be two tax lines, one for each repartition line.")
+ self.assertEqual(abs(inv_tax_lines.filtered(lambda x: x.account_id == account_1).balance), 4.2, "Tax line on account 1 should amount to 4.2 (10% of 42)")
+ self.assertEqual(inv_tax_lines.filtered(lambda x: x.account_id == account_1).tax_tag_ids, inv_tax_tag_10, "Tax line on account 1 should have 10% tag")
+ self.assertAlmostEqual(abs(inv_tax_lines.filtered(lambda x: x.account_id == account_2).balance), 37.8, 2, "Tax line on account 2 should amount to 37.8 (90% of 42)")
+ self.assertEqual(inv_tax_lines.filtered(lambda x: x.account_id == account_2).tax_tag_ids, inv_tax_tag_90, "Tax line on account 2 should have 90% tag")
+
+ # Test refund repartition
+ refund = self._create_invoice([(100, tax)], inv_type='out_refund')
+ refund.action_post()
+
+ self.assertEqual(len(refund.line_ids), 4, "There should be 4 account move lines created for the refund: payable, base and 2 tax lines")
+ ref_base_line = refund.line_ids.filtered(lambda x: not x.tax_repartition_line_id and x.account_id.user_type_id.type != 'receivable')
+ self.assertEqual(len(ref_base_line), 1, "There should be only one base line generated")
+ self.assertEqual(abs(ref_base_line.balance), 100, "Base amount should be 100")
+ self.assertEqual(ref_base_line.tax_tag_ids, ref_base_tag, "Base line should have received base tag")
+ ref_tax_lines = refund.line_ids.filtered(lambda x: x.tax_repartition_line_id.repartition_type == 'tax')
+ self.assertEqual(len(ref_tax_lines), 2, "There should be two refund tax lines")
+ self.assertEqual(abs(ref_tax_lines.filtered(lambda x: x.account_id == ref_base_line.account_id).balance), 4.2, "Refund tax line on base account should amount to 4.2 (10% of 42)")
+ self.assertAlmostEqual(abs(ref_tax_lines.filtered(lambda x: x.account_id == account_1).balance), 37.8, 2, "Refund tax line on account 1 should amount to 37.8 (90% of 42)")
+ self.assertEqual(ref_tax_lines.mapped('tax_tag_ids'), ref_tax_tag, "Refund tax lines should have the right tag")
+
+ def test_division_tax(self):
+ '''
+ Test that when using division tax, with percentage amount
+ 100% any change on price unit is correctly reflected on
+ the whole move.
+
+ Complete scenario:
+ - Create a division tax, 100% amount, included in price.
+ - Create an invoice, with only the mentioned tax
+ - Change price_unit of the aml
+ - Total price of the move should change as well
+ '''
+
+ sale_tax = self.env['account.tax'].create({
+ 'name': 'tax',
+ 'type_tax_use': 'sale',
+ 'amount_type': 'division',
+ 'amount': 100,
+ 'price_include': True,
+ 'include_base_amount': True,
+ })
+ invoice = self._create_invoice([(100, sale_tax)])
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id'), [{
+ 'name': sale_tax.name,
+ 'tax_base_amount': 0.0,
+ 'balance': -100,
+ }])
+ # change price unit, everything should change as well
+ with Form(invoice) as invoice_form:
+ with invoice_form.line_ids.edit(0) as line_edit:
+ line_edit.price_unit = 200
+
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id'), [{
+ 'name': sale_tax.name,
+ 'tax_base_amount': 0.0,
+ 'balance': -200,
+ }])
+
+ def test_misc_journal_entry_tax_tags_sale(self):
+ sale_tax = self.env['account.tax'].create({
+ 'name': 'tax',
+ 'type_tax_use': 'sale',
+ 'amount_type': 'percent',
+ 'amount': 10,
+ 'invoice_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_pos.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_pos.ids)],
+ }),
+ ],
+ 'refund_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_neg.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_neg.ids)],
+ }),
+ ],
+ })
+
+ inv_tax_rep_ln = sale_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
+ ref_tax_rep_ln = sale_tax.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
+
+ # === Tax in debit ===
+
+ move_form = Form(self.env['account.move'], view='account.view_move_form')
+ move_form.ref = 'azerty'
+
+ # Debit base tax line.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'debit_line_1'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.debit = 1000.0
+ credit_line.tax_ids.clear()
+ credit_line.tax_ids.add(sale_tax)
+
+ self.assertTrue(credit_line.recompute_tax_line)
+
+ # Balance the journal entry.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'balance'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.credit = 1100.0
+
+ move = move_form.save()
+
+ self.assertRecordValues(move.line_ids.sorted('balance'), [
+ {'balance': -1100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ {'balance': 100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_neg.ids, 'tax_base_amount': 1000, 'tax_repartition_line_id': ref_tax_rep_ln.id},
+ {'balance': 1000.0, 'tax_ids': sale_tax.ids, 'tax_tag_ids': self.base_tag_neg.ids, 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ ])
+
+ # === Tax in credit ===
+
+ move_form = Form(self.env['account.move'], view='account.view_move_form')
+ move_form.ref = 'azerty'
+
+ # Debit base tax line.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'debit_line_1'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.credit = 1000.0
+ credit_line.tax_ids.clear()
+ credit_line.tax_ids.add(sale_tax)
+
+ self.assertTrue(credit_line.recompute_tax_line)
+
+ # Balance the journal entry.
+ with move_form.line_ids.new() as debit_line:
+ debit_line.name = 'balance'
+ debit_line.account_id = self.company_data['default_account_revenue']
+ debit_line.debit = 1100.0
+
+ move = move_form.save()
+
+ self.assertRecordValues(move.line_ids.sorted('balance'), [
+ {'balance': -1000.0, 'tax_ids': sale_tax.ids, 'tax_tag_ids': self.base_tag_neg.ids, 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ {'balance': -100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_neg.ids, 'tax_base_amount': 1000, 'tax_repartition_line_id': inv_tax_rep_ln.id},
+ {'balance': 1100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ ])
+
+ def test_misc_journal_entry_tax_tags_purchase(self):
+ purch_tax = self.env['account.tax'].create({
+ 'name': 'tax',
+ 'type_tax_use': 'purchase',
+ 'amount_type': 'percent',
+ 'amount': 10,
+ 'invoice_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_pos.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_pos.ids)],
+ }),
+ ],
+ 'refund_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_neg.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_neg.ids)],
+ }),
+ ],
+ })
+
+ inv_tax_rep_ln = purch_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
+ ref_tax_rep_ln = purch_tax.refund_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
+
+ # === Tax in debit ===
+
+ move_form = Form(self.env['account.move'])
+ move_form.ref = 'azerty'
+
+ # Debit base tax line.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'debit_line_1'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.debit = 1000.0
+ credit_line.tax_ids.clear()
+ credit_line.tax_ids.add(purch_tax)
+
+ self.assertTrue(credit_line.recompute_tax_line)
+
+ # Balance the journal entry.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'balance'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.credit = 1100.0
+
+ move = move_form.save()
+
+ self.assertRecordValues(move.line_ids.sorted('balance'), [
+ {'balance': -1100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ {'balance': 100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_pos.ids, 'tax_base_amount': 1000, 'tax_repartition_line_id': inv_tax_rep_ln.id},
+ {'balance': 1000.0, 'tax_ids': purch_tax.ids, 'tax_tag_ids': self.base_tag_pos.ids, 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ ])
+
+ # === Tax in credit ===
+
+ move_form = Form(self.env['account.move'])
+ move_form.ref = 'azerty'
+
+ # Debit base tax line.
+ with move_form.line_ids.new() as credit_line:
+ credit_line.name = 'debit_line_1'
+ credit_line.account_id = self.company_data['default_account_revenue']
+ credit_line.credit = 1000.0
+ credit_line.tax_ids.clear()
+ credit_line.tax_ids.add(purch_tax)
+
+ self.assertTrue(credit_line.recompute_tax_line)
+
+ # Balance the journal entry.
+ with move_form.line_ids.new() as debit_line:
+ debit_line.name = 'balance'
+ debit_line.account_id = self.company_data['default_account_revenue']
+ debit_line.debit = 1100.0
+
+ move = move_form.save()
+
+ self.assertRecordValues(move.line_ids.sorted('balance'), [
+ {'balance': -1000.0, 'tax_ids': purch_tax.ids, 'tax_tag_ids': self.base_tag_pos.ids, 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ {'balance': -100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_pos.ids, 'tax_base_amount': 1000, 'tax_repartition_line_id': ref_tax_rep_ln.id},
+ {'balance': 1100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0, 'tax_repartition_line_id': False},
+ ])
+
+ def test_misc_entry_tax_group_signs(self):
+ """ Tests sign inversion of the tags on misc operations made with tax
+ groups.
+ """
+ def _create_group_of_taxes(tax_type):
+ # We use asymmetric tags between the child taxes to avoid shadowing errors
+ child1_sale_tax = self.env['account.tax'].create({
+ 'sequence': 1,
+ 'name': 'child1_%s' % tax_type,
+ 'type_tax_use': 'none',
+ 'amount_type': 'percent',
+ 'amount': 5,
+ 'invoice_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_pos.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_pos.ids)],
+ }),
+ ],
+ 'refund_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ }),
+ ],
+ })
+ child2_sale_tax = self.env['account.tax'].create({
+ 'sequence': 2,
+ 'name': 'child2_%s' % tax_type,
+ 'type_tax_use': 'none',
+ 'amount_type': 'percent',
+ 'amount': 10,
+ 'invoice_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ }),
+ ],
+ 'refund_repartition_line_ids': [
+ (0, 0, {
+ 'repartition_type': 'base',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.base_tag_neg.ids)],
+ }),
+ (0, 0, {
+ 'repartition_type': 'tax',
+ 'factor_percent': 100.0,
+ 'tag_ids': [(6, 0, self.tax_tag_neg.ids)],
+ }),
+ ],
+ })
+ return self.env['account.tax'].create({
+ 'name': 'group_%s' % tax_type,
+ 'type_tax_use': tax_type,
+ 'amount_type': 'group',
+ 'amount': 10,
+ 'children_tax_ids':[(6,0,[child1_sale_tax.id, child2_sale_tax.id])]
+ })
+
+ def _create_misc_operation(tax, tax_field):
+ with Form(self.env['account.move'], view='account.view_move_form') as move_form:
+ for line_field in ('debit', 'credit'):
+ line_amount = tax_field == line_field and 1000 or 1150
+ with move_form.line_ids.new() as line_form:
+ line_form.name = '%s_line' % line_field
+ line_form.account_id = self.company_data['default_account_revenue']
+ line_form.debit = line_field == 'debit' and line_amount or 0
+ line_form.credit = line_field == 'credit' and line_amount or 0
+
+ if tax_field == line_field:
+ line_form.tax_ids.clear()
+ line_form.tax_ids.add(tax)
+
+ return move_form.save()
+
+ sale_group = _create_group_of_taxes('sale')
+ purchase_group = _create_group_of_taxes('purchase')
+
+ # Sale tax on debit: use refund repartition
+ debit_sale_move = _create_misc_operation(sale_group, 'debit')
+ self.assertRecordValues(debit_sale_move.line_ids.sorted('balance'), [
+ {'balance': -1150.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0},
+ {'balance': 50.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 1000},
+ {'balance': 100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_neg.ids, 'tax_base_amount': 1000},
+ {'balance': 1000.0, 'tax_ids': sale_group.ids, 'tax_tag_ids': self.base_tag_neg.ids, 'tax_base_amount': 0},
+ ])
+
+ # Sale tax on credit: use invoice repartition and invert tags
+ credit_sale_move = _create_misc_operation(sale_group, 'credit')
+ self.assertRecordValues(credit_sale_move.line_ids.sorted('balance'), [
+ {'balance': -1000.0, 'tax_ids': sale_group.ids, 'tax_tag_ids': self.base_tag_neg.ids, 'tax_base_amount': 0},
+ {'balance': -100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 1000},
+ {'balance': -50.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_neg.ids, 'tax_base_amount': 1000},
+ {'balance': 1150.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0},
+ ])
+
+ # Purchase tax on debit: use invoice repartition
+ debit_purchase_move = _create_misc_operation(purchase_group, 'debit')
+ self.assertRecordValues(debit_purchase_move.line_ids.sorted('balance'), [
+ {'balance': -1150.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0},
+ {'balance': 50.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_pos.ids, 'tax_base_amount': 1000},
+ {'balance': 100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 1000},
+ {'balance': 1000.0, 'tax_ids': purchase_group.ids, 'tax_tag_ids': self.base_tag_pos.ids, 'tax_base_amount': 0},
+ ])
+
+ # Purchase tax on credit: use refund repartition and invert tags
+ credit_purchase_move = _create_misc_operation(purchase_group, 'credit')
+ self.assertRecordValues(credit_purchase_move.line_ids.sorted('balance'), [
+ {'balance': -1000.0, 'tax_ids': purchase_group.ids, 'tax_tag_ids': self.base_tag_pos.ids, 'tax_base_amount': 0},
+ {'balance': -100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_pos.ids, 'tax_base_amount': 1000},
+ {'balance': -50.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 1000},
+ {'balance': 1150.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0},
+ ])
+
+ def test_tax_calculation_foreign_currency_large_quantity(self):
+ ''' Test:
+ Foreign currency with rate of 1.1726 and tax of 21%
+ price_unit | Quantity | Taxes
+ ------------------
+ 2.82 | 20000 | 21% not incl
+ '''
+ self.env['res.currency.rate'].create({
+ 'name': '2018-01-01',
+ 'rate': 1.1726,
+ 'currency_id': self.currency_data['currency'].id,
+ 'company_id': self.env.company.id,
+ })
+ self.currency_data['currency'].rounding = 0.05
+
+ invoice = self.env['account.move'].create({
+ 'move_type': 'out_invoice',
+ 'partner_id': self.partner_a.id,
+ 'currency_id': self.currency_data['currency'].id,
+ 'invoice_date': '2018-01-01',
+ 'date': '2018-01-01',
+ 'invoice_line_ids': [(0, 0, {
+ 'name': 'xxxx',
+ 'quantity': 20000,
+ 'price_unit': 2.82,
+ 'tax_ids': [(6, 0, self.percent_tax_1.ids)],
+ })]
+ })
+
+ self.assertRecordValues(invoice.line_ids.filtered('tax_line_id'), [{
+ 'tax_base_amount': 48098.24, # 20000 * 2.82 / 1.1726
+ 'credit': 10100.63, # tax_base_amount * 0.21
+ }])
+
+ def test_ensure_no_unbalanced_entry(self):
+ ''' Ensure to not create an unbalanced journal entry when saving. '''
+ self.env['res.currency.rate'].create({
+ 'name': '2018-01-01',
+ 'rate': 0.654065014,
+ 'currency_id': self.currency_data['currency'].id,
+ 'company_id': self.env.company.id,
+ })
+ self.currency_data['currency'].rounding = 0.05
+
+ invoice = self._create_invoice([
+ (5, self.percent_tax_3_incl),
+ (10, self.percent_tax_3_incl),
+ (50, self.percent_tax_3_incl),
+ ], currency_id=self.currency_data['currency'], invoice_payment_term_id=self.pay_terms_a)
+ invoice.action_post()