943 lines
33 KiB
Python
943 lines
33 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
|
from odoo.fields import Command
|
|
from odoo.tests import tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestTaxTotals(AccountTestInvoicingCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls, chart_template_ref=None):
|
|
super().setUpClass(chart_template_ref=chart_template_ref)
|
|
|
|
cls.tax_group1 = cls.env['account.tax.group'].create({
|
|
'name': '1',
|
|
'sequence': 1
|
|
})
|
|
cls.tax_group2 = cls.env['account.tax.group'].create({
|
|
'name': '2',
|
|
'sequence': 2
|
|
})
|
|
cls.tax_group_sub1 = cls.env['account.tax.group'].create({
|
|
'name': 'subtotals 1',
|
|
'preceding_subtotal': "PRE GROUP 1",
|
|
'sequence': 3
|
|
})
|
|
cls.tax_group_sub2 = cls.env['account.tax.group'].create({
|
|
'name': 'subtotals 2',
|
|
'preceding_subtotal': "PRE GROUP 2",
|
|
'sequence': 4
|
|
})
|
|
cls.tax_group_sub3 = cls.env['account.tax.group'].create({
|
|
'name': 'subtotals 3',
|
|
'preceding_subtotal': "PRE GROUP 1", # same as sub1, on purpose
|
|
'sequence': 5
|
|
})
|
|
|
|
cls.tax_16 = cls.env['account.tax'].create({
|
|
'name': "tax_16",
|
|
'amount_type': 'percent',
|
|
'amount': 16.0,
|
|
})
|
|
cls.tax_53 = cls.env['account.tax'].create({
|
|
'name': "tax_53",
|
|
'amount_type': 'percent',
|
|
'amount': 53.0,
|
|
})
|
|
cls.tax_17a = cls.env['account.tax'].create({
|
|
'name': "tax_17a",
|
|
'amount_type': 'percent',
|
|
'amount': 17.0,
|
|
})
|
|
cls.tax_17b = cls.tax_17a.copy({'name': "tax_17b"})
|
|
|
|
def assertTaxTotals(self, document, expected_values):
|
|
main_keys_to_ignore = {'formatted_amount_total', 'formatted_amount_untaxed'}
|
|
group_keys_to_ignore = {'group_key', 'formatted_tax_group_amount', 'formatted_tax_group_base_amount'}
|
|
subtotals_keys_to_ignore = {'formatted_amount'}
|
|
|
|
to_compare = document.tax_totals
|
|
|
|
for key in main_keys_to_ignore:
|
|
del to_compare[key]
|
|
|
|
for key in group_keys_to_ignore:
|
|
for groups in to_compare['groups_by_subtotal'].values():
|
|
for group in groups:
|
|
del group[key]
|
|
|
|
for key in subtotals_keys_to_ignore:
|
|
for subtotal in to_compare['subtotals']:
|
|
del subtotal[key]
|
|
|
|
self.assertEqual(to_compare, expected_values)
|
|
|
|
def _create_document_for_tax_totals_test(self, lines_data):
|
|
""" Creates and returns a new record of a model defining a tax_totals
|
|
field and using the related widget.
|
|
|
|
By default, this function creates an invoice, but it is overridden in sale
|
|
and purchase to create respectively a sale.order or a purchase.order. This way,
|
|
we can test the invoice_tax_totals from both these models in the same way as
|
|
account.move's.
|
|
|
|
:param lines_data: a list of tuple (amount, taxes), where amount is a base amount,
|
|
and taxes a recordset of account.tax objects corresponding
|
|
to the taxes to apply on this amount. Each element of the list
|
|
corresponds to a line of the document (invoice line, PO line, SO line).
|
|
"""
|
|
invoice_lines_vals = [
|
|
(0, 0, {
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'price_unit': amount,
|
|
'tax_ids': [(6, 0, taxes.ids)],
|
|
})
|
|
for amount, taxes in lines_data]
|
|
|
|
return self.env['account.move'].create({
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2019-01-01',
|
|
'invoice_line_ids': invoice_lines_vals,
|
|
})
|
|
|
|
def test_multiple_tax_lines(self):
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
})
|
|
|
|
tax_20 = self.env['account.tax'].create({
|
|
'name': "tax_20",
|
|
'amount_type': 'percent',
|
|
'amount': 20.0,
|
|
'tax_group_id': self.tax_group2.id,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1000, tax_10 + tax_20),
|
|
(1000, tax_10),
|
|
(1000, tax_20),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 3600,
|
|
'amount_untaxed': 3000,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 200,
|
|
'tax_group_base_amount': 2000,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
|
|
{
|
|
'tax_group_name': self.tax_group2.name,
|
|
'tax_group_amount': 400,
|
|
'tax_group_base_amount': 2000,
|
|
'tax_group_id': self.tax_group2.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 3000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
# Same but both are sharing the same tax group.
|
|
|
|
tax_20.tax_group_id = self.tax_group1
|
|
document.invalidate_model(['tax_totals'])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 3600,
|
|
'amount_untaxed': 3000,
|
|
'display_tax_base': False,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 600,
|
|
'tax_group_base_amount': 3000,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 3000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
def test_zero_tax_lines(self):
|
|
tax_0 = self.env['account.tax'].create({
|
|
'name': "tax_0",
|
|
'amount_type': 'percent',
|
|
'amount': 0.0,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1000, tax_0),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 1000,
|
|
'amount_untaxed': 1000,
|
|
'display_tax_base': False,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': tax_0.tax_group_id.name,
|
|
'tax_group_amount': 0,
|
|
'tax_group_base_amount': 1000,
|
|
'tax_group_id': tax_0.tax_group_id.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 1000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
def test_tax_affect_base_1(self):
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
'price_include': True,
|
|
'include_base_amount': True,
|
|
})
|
|
|
|
tax_20 = self.env['account.tax'].create({
|
|
'name': "tax_20",
|
|
'amount_type': 'percent',
|
|
'amount': 20.0,
|
|
'tax_group_id': self.tax_group2.id,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1100, tax_10 + tax_20),
|
|
(1100, tax_10),
|
|
(1000, tax_20),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 3620,
|
|
'amount_untaxed': 3000,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 200,
|
|
'tax_group_base_amount': 2000,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
|
|
{
|
|
'tax_group_name': self.tax_group2.name,
|
|
'tax_group_amount': 420,
|
|
'tax_group_base_amount': 2100,
|
|
'tax_group_id': self.tax_group2.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 3000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
# Same but both are sharing the same tax group.
|
|
|
|
tax_20.tax_group_id = self.tax_group1
|
|
document.invalidate_model(['tax_totals'])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 3620,
|
|
'amount_untaxed': 3000,
|
|
'display_tax_base': False,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 620,
|
|
'tax_group_base_amount': 3000,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 3000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
def test_tax_affect_base_2(self):
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
'include_base_amount': True,
|
|
})
|
|
|
|
tax_20 = self.env['account.tax'].create({
|
|
'name': "tax_20",
|
|
'amount_type': 'percent',
|
|
'amount': 20.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
})
|
|
|
|
tax_30 = self.env['account.tax'].create({
|
|
'name': "tax_30",
|
|
'amount_type': 'percent',
|
|
'amount': 30.0,
|
|
'tax_group_id': self.tax_group2.id,
|
|
'include_base_amount': True,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1000, tax_10 + tax_20),
|
|
(1000, tax_30 + tax_10),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 2750,
|
|
'amount_untaxed': 2000,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 450,
|
|
'tax_group_base_amount': 2300,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
|
|
{
|
|
'tax_group_name': self.tax_group2.name,
|
|
'tax_group_amount': 300,
|
|
'tax_group_base_amount': 1000,
|
|
'tax_group_id': self.tax_group2.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 2000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
# Same but both are sharing the same tax group.
|
|
|
|
tax_30.tax_group_id = self.tax_group1
|
|
document.invalidate_model(['tax_totals'])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 2750,
|
|
'amount_untaxed': 2000,
|
|
'display_tax_base': False,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 750,
|
|
'tax_group_base_amount': 2000,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 2000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
def test_subtotals_basic(self):
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
})
|
|
|
|
tax_25 = self.env['account.tax'].create({
|
|
'name': "tax_25",
|
|
'amount_type': 'percent',
|
|
'amount': 25.0,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
})
|
|
|
|
tax_42 = self.env['account.tax'].create({
|
|
'name': "tax_42",
|
|
'amount_type': 'percent',
|
|
'amount': 42.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1000, tax_10),
|
|
(1000, tax_25),
|
|
(100, tax_42),
|
|
(200, tax_42 + tax_10 + tax_25),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 2846,
|
|
'amount_untaxed': 2300,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 126,
|
|
'tax_group_base_amount': 300,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
],
|
|
'PRE GROUP 1': [
|
|
{
|
|
'tax_group_name': self.tax_group_sub1.name,
|
|
'tax_group_amount': 120,
|
|
'tax_group_base_amount': 1200,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
},
|
|
],
|
|
'PRE GROUP 2': [
|
|
{
|
|
'tax_group_name': self.tax_group_sub2.name,
|
|
'tax_group_amount': 300,
|
|
'tax_group_base_amount': 1200,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
},
|
|
]
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 2300,
|
|
},
|
|
|
|
{
|
|
'name': "PRE GROUP 1",
|
|
'amount': 2426,
|
|
},
|
|
|
|
{
|
|
'name': "PRE GROUP 2",
|
|
'amount': 2546,
|
|
},
|
|
],
|
|
'subtotals_order': ["Untaxed Amount", "PRE GROUP 1", "PRE GROUP 2"],
|
|
})
|
|
|
|
def test_after_total_mix(self):
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group_sub3.id,
|
|
})
|
|
|
|
tax_25 = self.env['account.tax'].create({
|
|
'name': "tax_25",
|
|
'amount_type': 'percent',
|
|
'amount': -25.0,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
})
|
|
|
|
tax_42 = self.env['account.tax'].create({
|
|
'name': "tax_42",
|
|
'amount_type': 'percent',
|
|
'amount': 42.0,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
})
|
|
|
|
tax_30 = self.env['account.tax'].create({
|
|
'name': "tax_30",
|
|
'amount_type': 'percent',
|
|
'amount': 30.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
})
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(100, tax_10),
|
|
(100, tax_25 + tax_42 + tax_30),
|
|
(200, tax_10 + tax_25),
|
|
(1000, tax_30),
|
|
(100, tax_30 + tax_10)
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 1867,
|
|
'amount_untaxed': 1500,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 360,
|
|
'tax_group_base_amount': 1200,
|
|
'tax_group_id': self.tax_group1.id,
|
|
},
|
|
],
|
|
|
|
'PRE GROUP 1': [
|
|
{
|
|
'tax_group_name': self.tax_group_sub1.name,
|
|
'tax_group_amount': 42,
|
|
'tax_group_base_amount': 100,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
},
|
|
|
|
{
|
|
'tax_group_name': self.tax_group_sub3.name,
|
|
'tax_group_amount': 40,
|
|
'tax_group_base_amount': 400,
|
|
'tax_group_id': self.tax_group_sub3.id,
|
|
},
|
|
],
|
|
|
|
'PRE GROUP 2': [
|
|
{
|
|
'tax_group_name': self.tax_group_sub2.name,
|
|
'tax_group_amount': -75,
|
|
'tax_group_base_amount': 300,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
},
|
|
],
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 1500,
|
|
},
|
|
|
|
{
|
|
'name': "PRE GROUP 1",
|
|
'amount': 1860,
|
|
},
|
|
|
|
{
|
|
'name': "PRE GROUP 2",
|
|
'amount': 1942,
|
|
},
|
|
],
|
|
'subtotals_order': ["Untaxed Amount", "PRE GROUP 1", "PRE GROUP 2"],
|
|
})
|
|
|
|
def test_discounted_tax(self):
|
|
tax_21_exempted = self.env['account.tax'].create({
|
|
'name': "tax_21_exempted",
|
|
'amount_type': 'group',
|
|
'amount': 2.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
'children_tax_ids': [
|
|
Command.create({
|
|
'name': "tax_exempt",
|
|
'amount_type': 'percent',
|
|
'amount': -2.0,
|
|
'include_base_amount': True,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
'sequence': 1,
|
|
}),
|
|
Command.create({
|
|
'name': "tax_21",
|
|
'amount_type': 'percent',
|
|
'amount': 21.0,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
'sequence': 2,
|
|
}),
|
|
Command.create({
|
|
'name': "tax_reapply",
|
|
'amount_type': 'percent',
|
|
'amount': 2.0,
|
|
'is_base_affected': False,
|
|
'tax_group_id': self.tax_group_sub3.id,
|
|
'sequence': 3,
|
|
}),
|
|
]
|
|
})
|
|
self.tax_group_sub1.preceding_subtotal = "Tax exemption"
|
|
self.tax_group_sub2.preceding_subtotal = "Tax application"
|
|
self.tax_group_sub3.preceding_subtotal = "Reapply amount"
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(1000 / 0.98, tax_21_exempted),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 1230.41,
|
|
'amount_untaxed': 1020.41,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
"Reapply amount": [{
|
|
'tax_group_name': self.tax_group_sub3.name,
|
|
'tax_group_amount': 20.41,
|
|
'tax_group_base_amount': 1020.41,
|
|
'tax_group_id': self.tax_group_sub3.id,
|
|
}],
|
|
"Tax application": [{
|
|
'tax_group_name': self.tax_group_sub2.name,
|
|
'tax_group_amount': 210.0,
|
|
'tax_group_base_amount': 1000.0,
|
|
'tax_group_id': self.tax_group_sub2.id,
|
|
}],
|
|
"Tax exemption": [{
|
|
'tax_group_name': self.tax_group_sub1.name,
|
|
'tax_group_amount': -20.41,
|
|
'tax_group_base_amount': 1020.41,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
}],
|
|
},
|
|
'subtotals': [{
|
|
'name': "Tax exemption",
|
|
'amount': 1020.41,
|
|
}, {
|
|
'name': "Tax application",
|
|
'amount': 1000.00,
|
|
}, {
|
|
'name': "Reapply amount",
|
|
'amount': 1210.00,
|
|
}],
|
|
'subtotals_order': ["Tax exemption", "Tax application", "Reapply amount"],
|
|
})
|
|
|
|
def test_invoice_grouped_taxes_with_tax_group(self):
|
|
""" A tax of type group with a tax_group_id being the same as one of the children tax shouldn't affect the
|
|
result of the _prepare_tax_totals.
|
|
"""
|
|
tax_10_withheld = self.env['account.tax'].create({
|
|
'name': "tax_10_withheld",
|
|
'amount_type': 'group',
|
|
'tax_group_id': self.tax_group1.id,
|
|
'children_tax_ids': [
|
|
Command.create({
|
|
'name': "tax_withheld",
|
|
'amount_type': 'percent',
|
|
'amount': -47,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
'sequence': 1,
|
|
}),
|
|
Command.create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10,
|
|
'tax_group_id': self.tax_group1.id,
|
|
'sequence': 2,
|
|
}),
|
|
]
|
|
})
|
|
self.tax_group_sub1.preceding_subtotal = "Tax withholding"
|
|
|
|
document = self._create_document_for_tax_totals_test([
|
|
(100, tax_10_withheld),
|
|
])
|
|
|
|
self.assertTaxTotals(document, {
|
|
'amount_total': 63,
|
|
'amount_untaxed': 100,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 10,
|
|
'tax_group_base_amount': 100,
|
|
'tax_group_id': self.tax_group1.id,
|
|
}],
|
|
"Tax withholding": [{
|
|
'tax_group_name': self.tax_group_sub1.name,
|
|
'tax_group_amount': -47,
|
|
'tax_group_base_amount': 100,
|
|
'tax_group_id': self.tax_group_sub1.id,
|
|
}],
|
|
},
|
|
'subtotals': [{
|
|
'name': "Untaxed Amount",
|
|
'amount': 100,
|
|
}, {
|
|
'name': "Tax withholding",
|
|
'amount': 110,
|
|
}],
|
|
'subtotals_order': ["Untaxed Amount", "Tax withholding"],
|
|
})
|
|
|
|
def test_taxtotals_with_different_tax_rounding_methods(self):
|
|
|
|
def run_case(rounding_line, lines, expected_tax_group_amounts):
|
|
self.env.company.tax_calculation_rounding_method = rounding_line
|
|
|
|
document = self._create_document_for_tax_totals_test(lines)
|
|
tax_amounts = document.tax_totals['groups_by_subtotal']['Untaxed Amount']
|
|
|
|
if len(expected_tax_group_amounts) != len(tax_amounts):
|
|
self.fail("Wrong number of values to compare.")
|
|
|
|
for tax_amount, expected in zip(tax_amounts, expected_tax_group_amounts):
|
|
actual = tax_amount['tax_group_amount']
|
|
if document.currency_id.compare_amounts(actual, expected) != 0:
|
|
self.fail(f'{document.currency_id.round(actual)} != {expected}')
|
|
|
|
# one line, two taxes
|
|
lines = [
|
|
(100.41, self.tax_16 + self.tax_53),
|
|
]
|
|
run_case('round_per_line', lines, [69.29])
|
|
run_case('round_globally', lines, [69.29])
|
|
|
|
# two lines, different taxes
|
|
lines = [
|
|
(50.4, self.tax_17a),
|
|
(47.21, self.tax_17b),
|
|
]
|
|
run_case('round_per_line', lines, [16.60])
|
|
run_case('round_globally', lines, [16.60])
|
|
|
|
# two lines, same tax
|
|
lines = [
|
|
(50.4, self.tax_17a),
|
|
(47.21, self.tax_17a),
|
|
]
|
|
run_case('round_per_line', lines, [16.60])
|
|
run_case('round_globally', lines, [16.59])
|
|
|
|
def test_invoice_foreign_currency_tax_totals(self):
|
|
self.env['res.currency.rate'].create({
|
|
'name': '2018-01-01',
|
|
'rate': 0.2,
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'company_id': self.env.company.id,
|
|
})
|
|
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'amount': 10.0,
|
|
'tax_group_id': self.tax_group1.id,
|
|
})
|
|
|
|
tax_20 = self.env['account.tax'].create({
|
|
'name': "tax_20",
|
|
'amount_type': 'percent',
|
|
'amount': 20.0,
|
|
'tax_group_id': self.tax_group2.id,
|
|
})
|
|
|
|
invoice = self.env['account.move'].create({
|
|
'move_type': 'out_invoice',
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2019-01-01',
|
|
'currency_id': self.currency_data['currency'].id,
|
|
})
|
|
|
|
lines_data = [(100, tax_10), (300, tax_20)]
|
|
invoice_lines_vals = [
|
|
Command.create({
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'account_id': self.company_data['default_account_revenue'].id,
|
|
'price_unit': amount,
|
|
'tax_ids': [Command.set(taxes.ids)],
|
|
})
|
|
for amount, taxes in lines_data
|
|
]
|
|
|
|
invoice['invoice_line_ids'] = invoice_lines_vals
|
|
|
|
self.assertTaxTotals(invoice, {
|
|
'amount_total': 470,
|
|
'amount_untaxed': 400,
|
|
'display_tax_base': True,
|
|
'groups_by_subtotal': {
|
|
'Untaxed Amount': [
|
|
{
|
|
'tax_group_name': self.tax_group1.name,
|
|
'tax_group_amount': 10,
|
|
'tax_group_base_amount': 100,
|
|
'tax_group_id': self.tax_group1.id,
|
|
'tax_group_amount_company_currency': 50,
|
|
'tax_group_base_amount_company_currency': 500,
|
|
},
|
|
{
|
|
'tax_group_name': self.tax_group2.name,
|
|
'tax_group_amount': 60,
|
|
'tax_group_base_amount': 300,
|
|
'tax_group_id': self.tax_group2.id,
|
|
'tax_group_amount_company_currency': 300,
|
|
'tax_group_base_amount_company_currency': 1500,
|
|
}
|
|
]
|
|
},
|
|
'subtotals': [
|
|
{
|
|
'name': "Untaxed Amount",
|
|
'amount': 400,
|
|
'amount_company_currency': 2000,
|
|
}
|
|
],
|
|
'subtotals_order': ["Untaxed Amount"],
|
|
})
|
|
|
|
def test_cash_rounding_amount_total_rounded(self):
|
|
tax_15 = self.env['account.tax'].create({
|
|
'name': "tax_15",
|
|
'amount_type': 'percent',
|
|
'tax_group_id': self.tax_group1.id,
|
|
'amount': 15.0,
|
|
})
|
|
tax_10 = self.env['account.tax'].create({
|
|
'name': "tax_10",
|
|
'amount_type': 'percent',
|
|
'tax_group_id': self.tax_group2.id,
|
|
'amount': 10.0,
|
|
})
|
|
cash_rounding_biggest_tax = self.env['account.cash.rounding'].create({
|
|
'name': 'biggest tax Rounding HALF-UP',
|
|
'rounding': 1,
|
|
'strategy': 'biggest_tax',
|
|
'rounding_method': 'HALF-UP',
|
|
})
|
|
cash_rounding_add_invoice_line = self.env['account.cash.rounding'].create({
|
|
'name': 'add invoice line Rounding HALF-UP',
|
|
'rounding': 1,
|
|
'strategy': 'add_invoice_line',
|
|
'profit_account_id': self.company_data['default_account_revenue'].id,
|
|
'loss_account_id': self.company_data['default_account_expense'].id,
|
|
'rounding_method': 'HALF-UP',
|
|
})
|
|
|
|
for move_type in ['out_invoice', 'in_invoice']:
|
|
move = self.env['account.move'].create({
|
|
'move_type': move_type,
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2019-01-01',
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'price_unit': 378,
|
|
'tax_ids': [Command.set(tax_15.ids)],
|
|
}),
|
|
Command.create({
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'price_unit': 100,
|
|
'tax_ids': [Command.set(tax_10.ids)],
|
|
})
|
|
],
|
|
})
|
|
|
|
move.invoice_cash_rounding_id = cash_rounding_biggest_tax
|
|
self.assertEqual(move.tax_totals['groups_by_subtotal']['Untaxed Amount'][0]['tax_group_amount'], 57)
|
|
self.assertEqual(move.tax_totals['groups_by_subtotal']['Untaxed Amount'][1]['tax_group_amount'], 10)
|
|
self.assertEqual(move.tax_totals['amount_total'], 545)
|
|
|
|
move.invoice_cash_rounding_id = cash_rounding_add_invoice_line
|
|
self.assertEqual(move.tax_totals['groups_by_subtotal']['Untaxed Amount'][0]['tax_group_amount'], 56.7)
|
|
self.assertEqual(move.tax_totals['groups_by_subtotal']['Untaxed Amount'][1]['tax_group_amount'], 10)
|
|
self.assertEqual(move.tax_totals['rounding_amount'], 0.3)
|
|
self.assertEqual(move.tax_totals['amount_total'], 545)
|
|
|
|
def test_recompute_cash_rounding_lines(self):
|
|
# if rounding_method is changed then rounding shouldn't be recomputed in posted invoices
|
|
cash_rounding_add_invoice_line = self.env['account.cash.rounding'].create({
|
|
'name': 'Add invoice line Rounding UP',
|
|
'rounding': 1,
|
|
'strategy': 'add_invoice_line',
|
|
'profit_account_id': self.company_data['default_account_revenue'].id,
|
|
'loss_account_id': self.company_data['default_account_expense'].id,
|
|
'rounding_method': 'UP',
|
|
})
|
|
moves_rounding = {}
|
|
moves = self.env['account.move']
|
|
for move_type in ['out_invoice', 'in_invoice']:
|
|
move = self.env['account.move'].create({
|
|
'move_type': move_type,
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2019-01-01',
|
|
'invoice_cash_rounding_id': cash_rounding_add_invoice_line.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'price_unit': 99.5,
|
|
})
|
|
],
|
|
})
|
|
moves_rounding[move] = sum(move.line_ids.filtered(lambda line: line.display_type == 'rounding').mapped('balance'))
|
|
moves += move
|
|
moves.action_post()
|
|
cash_rounding_add_invoice_line.rounding_method = 'DOWN'
|
|
# check if rounding is recomputed
|
|
moves.to_check = True
|
|
for move in moves_rounding:
|
|
self.assertEqual(sum(move.line_ids.filtered(lambda line: line.display_type == 'rounding').mapped('balance')), moves_rounding[move])
|
|
|
|
def test_cash_rounding_amount_total_rounded_foreign_currency(self):
|
|
tax_15 = self.env['account.tax'].create({
|
|
'name': "tax_15",
|
|
'amount_type': 'percent',
|
|
'amount': 15.0,
|
|
})
|
|
cash_rounding = self.env['account.cash.rounding'].create({
|
|
'name': 'Rounding HALF-UP',
|
|
'rounding': 10,
|
|
'strategy': 'biggest_tax',
|
|
'rounding_method': 'HALF-UP',
|
|
})
|
|
self.env['res.currency.rate'].create({
|
|
'name': '2023-01-01',
|
|
'rate': 0.2,
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'company_id': self.env.company.id,
|
|
})
|
|
for move_type in ['out_invoice', 'in_invoice']:
|
|
move = self.env['account.move'].create({
|
|
'move_type': move_type,
|
|
'partner_id': self.partner_a.id,
|
|
'invoice_date': '2023-01-01',
|
|
'currency_id': self.currency_data['currency'].id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': 'line',
|
|
'display_type': 'product',
|
|
'price_unit': 100,
|
|
'tax_ids': [tax_15.id],
|
|
})
|
|
]
|
|
})
|
|
move.invoice_cash_rounding_id = cash_rounding
|
|
self.assertEqual(move.tax_totals['amount_total'], 120)
|