[FIX] account: correct management of groups of taxes on misc operations
Using group of taxes with type_tax_use='none' children messed up the sign of the tax_base_amount and line tags. closes odoo/odoo#63479 X-original-commit: 3642c429899c4d5def27c9ba10ae76c763a8a566 Signed-off-by: oco-odoo <oco-odoo@users.noreply.github.com>
This commit is contained in:
@@ -593,7 +593,7 @@ class AccountMove(models.Model):
|
||||
|
||||
if move.move_type == 'entry':
|
||||
repartition_field = is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids'
|
||||
repartition_tags = base_line.tax_ids.mapped(repartition_field).filtered(lambda x: x.repartition_type == 'base').tag_ids
|
||||
repartition_tags = base_line.tax_ids.flatten_taxes_hierarchy().mapped(repartition_field).filtered(lambda x: x.repartition_type == 'base').tag_ids
|
||||
|
||||
return balance_taxes_res
|
||||
|
||||
@@ -647,7 +647,7 @@ class AccountMove(models.Model):
|
||||
'grouping_dict': False,
|
||||
})
|
||||
taxes_map_entry['amount'] += tax_vals['amount']
|
||||
taxes_map_entry['tax_base_amount'] += self._get_base_amount_to_display(tax_vals['base'], tax_repartition_line)
|
||||
taxes_map_entry['tax_base_amount'] += self._get_base_amount_to_display(tax_vals['base'], tax_repartition_line, tax_vals['group'])
|
||||
taxes_map_entry['grouping_dict'] = grouping_dict
|
||||
line.tax_exigible = tax_exigible
|
||||
|
||||
@@ -713,12 +713,14 @@ class AccountMove(models.Model):
|
||||
taxes_map_entry['tax_line'].update(taxes_map_entry['tax_line']._get_fields_onchange_balance(force_computation=True))
|
||||
|
||||
@api.model
|
||||
def _get_base_amount_to_display(self, base_amount, tax_rep_ln):
|
||||
def _get_base_amount_to_display(self, base_amount, tax_rep_ln, parent_tax_group=None):
|
||||
""" The base amount returned for taxes by compute_all has is the balance
|
||||
of the base line. For inbound operations, positive sign is on credit, so
|
||||
we need to invert the sign of this amount before displaying it.
|
||||
"""
|
||||
if tax_rep_ln.invoice_tax_id.type_tax_use == 'sale' or tax_rep_ln.refund_tax_id.type_tax_use == 'purchase':
|
||||
source_tax = parent_tax_group or tax_rep_ln.invoice_tax_id or tax_rep_ln.refund_tax_id
|
||||
if (tax_rep_ln.invoice_tax_id and source_tax.type_tax_use == 'sale') \
|
||||
or (tax_rep_ln.refund_tax_id and source_tax.type_tax_use == 'purchase'):
|
||||
return -base_amount
|
||||
return base_amount
|
||||
|
||||
|
||||
@@ -299,17 +299,27 @@ class AccountTax(models.Model):
|
||||
|
||||
return rslt
|
||||
|
||||
def flatten_taxes_hierarchy(self):
|
||||
def flatten_taxes_hierarchy(self, create_map=False):
|
||||
# Flattens the taxes contained in this recordset, returning all the
|
||||
# children at the bottom of the hierarchy, in a recordset, ordered by sequence.
|
||||
# Eg. considering letters as taxes and alphabetic order as sequence :
|
||||
# [G, B([A, D, F]), E, C] will be computed as [A, D, F, C, E, G]
|
||||
# If create_map is True, an additional value is returned, a dictionary
|
||||
# mapping each child tax to its parent group
|
||||
all_taxes = self.env['account.tax']
|
||||
groups_map = {}
|
||||
for tax in self.sorted(key=lambda r: r.sequence):
|
||||
if tax.amount_type == 'group':
|
||||
all_taxes += tax.children_tax_ids.flatten_taxes_hierarchy()
|
||||
flattened_children = tax.children_tax_ids.flatten_taxes_hierarchy()
|
||||
all_taxes += flattened_children
|
||||
for flat_child in flattened_children:
|
||||
groups_map[flat_child] = tax
|
||||
else:
|
||||
all_taxes += tax
|
||||
|
||||
if create_map:
|
||||
return all_taxes, groups_map
|
||||
|
||||
return all_taxes
|
||||
|
||||
def get_tax_tags(self, is_refund, repartition_type):
|
||||
@@ -345,7 +355,7 @@ class AccountTax(models.Model):
|
||||
company = self[0].company_id
|
||||
|
||||
# 1) Flatten the taxes.
|
||||
taxes = self.flatten_taxes_hierarchy()
|
||||
taxes, groups_map = self.flatten_taxes_hierarchy(create_map=True)
|
||||
|
||||
# 2) Avoid mixing taxes having price_include=False && include_base_amount=True
|
||||
# with taxes having price_include=True. This use case is not supported as the
|
||||
@@ -560,6 +570,7 @@ class AccountTax(models.Model):
|
||||
'price_include': price_include,
|
||||
'tax_exigibility': tax.tax_exigibility,
|
||||
'tax_repartition_line_id': repartition_line.id,
|
||||
'group': groups_map.get(tax),
|
||||
'tag_ids': (repartition_line.tag_ids + subsequent_tags).ids,
|
||||
'tax_ids': subsequent_taxes.ids,
|
||||
})
|
||||
|
||||
@@ -491,6 +491,133 @@ class TestInvoiceTaxes(AccountTestInvoicingCommon):
|
||||
{'balance': 1100.0, 'tax_ids': [], 'tax_tag_ids': [], 'tax_base_amount': 0, 'tax_repartition_line_id': False, 'tax_tag_invert': 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
|
||||
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_pos.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_pos.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
|
||||
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_neg.ids, 'tax_base_amount': 0},
|
||||
{'balance': -100.0, 'tax_ids': [], 'tax_tag_ids': self.tax_tag_neg.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%
|
||||
|
||||
Reference in New Issue
Block a user