[FIX] account: computation of tax_tag_invert

Two issues solved in 1 commit; as their solutions depend on one another.

** ISSUE 1 **

To reproduce the issue, on a l10n with a tax report:
- Create a misc operation with a tax on it in the same form as a sales invoice ; post it.
- Reverse that move, and post the reverse.

=> Open the tax report: the amounts of the original move (for both tax lines and base lines) are doubled.
That's not what we want ; instead, the reverse should have entirely canceled the original move.

This is due to the fact the is_refund field of account.move.line is badly computed on the reverse: both tax and base lines are considered refund. Because of that, tax_tag_invert gets inverted, and ends up doubling the amount in the report instead of cancelling it. While made on a reverse move, those lines should not be considered as refunds, since they must both use the original repartition of the reversed move , unlike invoices, which would make use of the refund repartition in this case.

** ISSUE 2 **

To reproduce, on a l10n with a tax report:
- Create a cash basis tax, and set tags on its repartition lines so that it should be taken into account by the report. Make sure the refund repartition cancels the invoice repartition.
- Create an invoice using that tax, post it
- Click the "Add Credit Note" button, select "partial refund", and post the generated refund (which will cover the full amount of the original invoice)
- Reconcile the refund with the invoice

=> Because it's a partial refund, cash basis entries will still be generated. Open the tax report to ensure they sum up to 0... And the tax lines don't !

This happens because the computation of tax_tag_invert field inverts the sign of tag on cash basis entry tax lines when they have a negative tax_base_amount. In our case, when going through the "reverse" button and making a partial refund, we actually do get a negative amount in the credit note's tax_base_amount. This is inconsistent with what a refund generated from scratch does (then, you'll get a positive tax_base_amount in our case), and should only be legit when the document contains a negative line explicitly (so, a line with a negative quantity, hence inverting the meaning of debit/credit).

The root of the issue is that the tax_base_amount of the tax line uses the tax_tag_invert of the base line to define its sign. And tax_tag_invert was omitting to set copy=False. When reversing a move, the first step is to copy it. Because of the missing copy=False, tax_tag_invert was copied, and never recomputed (since it's a computed editable field) on the base line, giving it an inconsistent value, and ending up inverting the sign of tax_base_amount on the tax line. In turn, the tax line got a wrong tax_tag_invert because of that, and would propagate that to the cash basis entry when generating it.

This commit adds the missing copy=False to tax_tag_invert, but also fixes the computation of tax_tag_invert so that in does not rely on tax_base_amount anymore. This way, existing data will also benefit from the fix without having to recompute the field. Additionnally, there are currently plans to remove tax_base_amount in master, so this would have had to be done eventually anyway.

======

Forward-port note:

In 16.2, https://github.com/odoo/odoo/commit/84d7aab94e2984a81923f0546e24caf54cd53033 actually wrongly inverted the sign of the Mexican withholding tag in repartition lines. Those taxes are negative, so a tag needing to get the retention amount in positive must be negative itself. The bugs fixed in this commit shadowed the problem, and this fix reveals it. A fix of the tags's signs was added into this forward-port.

OPW 3255511

closes odoo/odoo#123187

X-original-commit: 688ab4d6688ccd2377ea9702c00b1ec16f99527a
Related: odoo/enterprise#41748
Signed-off-by: Quentin De Paoli <qdp@odoo.com>
Signed-off-by: Olivier Colson (oco) <oco@odoo.com>
This commit is contained in:
oco-odoo
2023-06-01 11:44:05 +02:00
parent 9da144788f
commit 0aeeb5374e
6 changed files with 122 additions and 24 deletions
+1 -1
View File
@@ -3378,7 +3378,7 @@ class AccountMove(models.Model):
reverse_moves += move.with_context(
move_reverse_cancel=cancel,
include_business_fields=True,
skip_invoice_sync=bool(move.tax_cash_basis_origin_move_id),
skip_invoice_sync=move.move_type == 'entry',
).copy(default_values)
reverse_moves.with_context(skip_invoice_sync=cancel).write({'line_ids': [
+13 -13
View File
@@ -213,7 +213,7 @@ class AccountMoveLine(models.Model):
# inverted when computing its total for each tag (for sales invoices, for # example)
tax_tag_invert = fields.Boolean(
string="Invert Tags",
compute='_compute_tax_tag_invert', store=True, readonly=False,
compute='_compute_tax_tag_invert', store=True, readonly=False, copy=False,
)
# === Reconciliation fields === #
@@ -767,12 +767,9 @@ class AccountMoveLine(models.Model):
is_refund = record.is_refund
tax_type = tax.type_tax_use
record.tax_tag_invert = (tax_type == 'purchase' and is_refund) or (tax_type == 'sale' and not is_refund)
else:
# For invoices with taxes
record.tax_tag_invert = record.move_id.is_inbound()
if record.move_id.tax_cash_basis_origin_move_id and (record.move_id.reversed_entry_id or record.tax_base_amount < 0):
record.tax_tag_invert = not record.tax_tag_invert
@api.depends('product_id')
def _compute_product_uom_id(self):
@@ -1016,21 +1013,24 @@ class AccountMoveLine(models.Model):
epd_needed_vals['price_subtotal'] += line.price_subtotal * percentage
line.epd_needed = {k: frozendict(v) for k, v in epd_needed.items()}
@api.depends('move_id.move_type', 'balance', 'tax_ids')
@api.depends('move_id.move_type', 'balance', 'tax_repartition_line_id', 'tax_ids')
def _compute_is_refund(self):
for line in self:
is_refund = False
if line.move_id.move_type in ('out_refund', 'in_refund'):
is_refund = True
elif line.move_id.move_type == 'entry':
taxes = line.tax_repartition_line_id.tax_id or line.tax_ids[:1]
tax_type = set(taxes.mapped('type_tax_use'))
if tax_type == {'sale'} and line.credit == 0:
is_refund = True
elif tax_type == {'purchase'} and line.debit == 0:
is_refund = True
if line.tax_repartition_line_id.factor_percent < 0:
is_refund = not is_refund
if line.tax_repartition_line_id:
is_refund = line.tax_repartition_line_id.document_type == 'refund'
else:
tax_type = line.tax_ids[:1].type_tax_use
if tax_type == 'sale' and line.credit == 0:
is_refund = True
elif tax_type == 'purchase' and line.debit == 0:
is_refund = True
if line.tax_ids and line.move_id.reversed_entry_id:
is_refund = not is_refund
line.is_refund = is_refund
@api.depends('date_maturity')
@@ -964,3 +964,61 @@ class TestAccountMove(AccountTestInvoicingCommon):
{'name': 'CABA', 'debit': 0.0, 'credit': 20.0, 'account_id': tax_account.id, 'tax_ids': [], 'tax_line_id': caba_tax.id},
{'name': 'credit', 'debit': 0.0, 'credit': 100.0, 'account_id': income_account.id, 'tax_ids': caba_tax.ids, 'tax_line_id': False},
])
def test_misc_with_taxes_reverse(self):
test_account = self.company_data['default_account_revenue']
# With a sale tax
sale_tax = self.company_data['default_tax_sale']
move_form = Form(self.env['account.move'])
with move_form.line_ids.new() as debit_line_form:
debit_line_form.name = 'debit'
debit_line_form.account_id = test_account
debit_line_form.debit = 115
with move_form.line_ids.new() as credit_line_form:
credit_line_form.name = 'credit'
credit_line_form.account_id = test_account
credit_line_form.credit = 100
credit_line_form.tax_ids.clear()
credit_line_form.tax_ids.add(sale_tax)
sale_move = move_form.save()
sale_invoice_rep_line = sale_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
self.assertRecordValues(sale_move.line_ids.sorted(lambda x: -x.balance), [
# pylint: disable=C0326
{'name': 'debit', 'debit': 115.0, 'credit': 0.0, 'account_id': test_account.id, 'tax_ids': [], 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
{'name': '15%', 'debit': 0.0, 'credit': 15.0, 'account_id': self.company_data['default_account_tax_sale'].id, 'tax_ids': [], 'tax_base_amount': 100, 'tax_tag_invert': True, 'tax_repartition_line_id': sale_invoice_rep_line.id},
{'name': 'credit', 'debit': 0.0, 'credit': 100.0, 'account_id': test_account.id, 'tax_ids': sale_tax.ids, 'tax_base_amount': 0, 'tax_tag_invert': True, 'tax_repartition_line_id': False},
])
# Same with a purchase tax
purchase_tax = self.company_data['default_tax_purchase']
move_form = Form(self.env['account.move'])
with move_form.line_ids.new() as credit_line_form:
credit_line_form.name = 'credit'
credit_line_form.account_id = test_account
credit_line_form.credit = 115
with move_form.line_ids.new() as debit_line_form:
debit_line_form.name = 'debit'
debit_line_form.account_id = test_account
debit_line_form.debit = 100
debit_line_form.tax_ids.clear()
debit_line_form.tax_ids.add(purchase_tax)
purchase_move = move_form.save()
purchase_invoice_rep_line = purchase_tax.invoice_repartition_line_ids.filtered(lambda x: x.repartition_type == 'tax')
self.assertRecordValues(purchase_move.line_ids.sorted(lambda x: x.balance), [
# pylint: disable=C0326
{'name': 'credit', 'credit': 115.0, 'debit': 0.0, 'account_id': test_account.id, 'tax_ids': [], 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
{'name': '15%', 'credit': 0.0, 'debit': 15.0, 'account_id': self.company_data['default_account_tax_purchase'].id, 'tax_ids': [], 'tax_base_amount': 100, 'tax_tag_invert': False, 'tax_repartition_line_id': purchase_invoice_rep_line.id},
{'name': 'debit', 'credit': 0.0, 'debit': 100.0, 'account_id': test_account.id, 'tax_ids': purchase_tax.ids, 'tax_base_amount': 0, 'tax_tag_invert': False, 'tax_repartition_line_id': False},
])
@@ -1128,24 +1128,32 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_currency': -800.0,
'debit': 0.0,
'credit': 800.0,
'tax_tag_invert': True,
'tax_base_amount': 0.0,
},
{
**self.product_line_vals_2,
'amount_currency': -160.0,
'debit': 0.0,
'credit': 160.0,
'tax_tag_invert': True,
'tax_base_amount': 0.0,
},
{
**self.tax_line_vals_1,
'amount_currency': -144.0,
'debit': 0.0,
'credit': 144.0,
'tax_tag_invert': True,
'tax_base_amount': 960.0,
},
{
**self.tax_line_vals_2,
'amount_currency': -24.0,
'debit': 0.0,
'credit': 24.0,
'tax_tag_invert': True,
'tax_base_amount': 160.0,
},
{
**self.term_line_vals_1,
@@ -1154,6 +1162,8 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 1128.0,
'credit': 0.0,
'date_maturity': move_reversal.date,
'tax_tag_invert': False,
'tax_base_amount': 0.0,
},
], {
**self.move_vals,
@@ -1179,24 +1189,32 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_currency': 800.0,
'debit': 800.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 0,
},
{
**self.product_line_vals_2,
'amount_currency': 160.0,
'debit': 160.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 0,
},
{
**self.tax_line_vals_1,
'amount_currency': 144.0,
'debit': 144.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 960.0,
},
{
**self.tax_line_vals_2,
'amount_currency': 24.0,
'debit': 24.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 160.0,
},
{
**self.term_line_vals_1,
@@ -1205,6 +1223,8 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 1128.0,
'date_maturity': move_reversal.date,
'tax_tag_invert': False,
'tax_base_amount': 0,
},
], {
**self.move_vals,
@@ -1744,24 +1744,32 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_currency': 1000.0,
'debit': 1000.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 0.0,
},
{
**self.product_line_vals_2,
'amount_currency': 200.0,
'debit': 200.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 0.0,
},
{
**self.tax_line_vals_1,
'amount_currency': 180.0,
'debit': 180.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 1200.0,
},
{
**self.tax_line_vals_2,
'amount_currency': 30.0,
'debit': 30.0,
'credit': 0.0,
'tax_tag_invert': False,
'tax_base_amount': 200.0,
},
{
**self.term_line_vals_1,
@@ -1770,6 +1778,8 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 0.0,
'credit': 1410.0,
'date_maturity': move_reversal.date,
'tax_tag_invert': False,
'tax_base_amount': 0.0,
},
], {
**self.move_vals,
@@ -1796,24 +1806,32 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_currency': -1000.0,
'debit': 0.0,
'credit': 1000.0,
'tax_tag_invert': True,
'tax_base_amount': 0.0,
},
{
**self.product_line_vals_2,
'amount_currency': -200.0,
'debit': 0.0,
'credit': 200.0,
'tax_tag_invert': True,
'tax_base_amount': 0.0,
},
{
**self.tax_line_vals_1,
'amount_currency': -180.0,
'debit': 0.0,
'credit': 180.0,
'tax_tag_invert': True,
'tax_base_amount': 1200.0,
},
{
**self.tax_line_vals_2,
'amount_currency': -30.0,
'debit': 0.0,
'credit': 30.0,
'tax_tag_invert': True,
'tax_base_amount': 200.0,
},
{
**self.term_line_vals_1,
@@ -1822,6 +1840,8 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'debit': 1410.0,
'credit': 0.0,
'date_maturity': move_reversal.date,
'tax_tag_invert': False,
'tax_base_amount': 0.0,
},
], {
**self.move_vals,
+10 -10
View File
@@ -8,17 +8,17 @@
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta208_01","l10n_mx.tag_iva",""
"tax1","10","4% WH","VAT withholding","VAT withholding(-4%)","-4.0","percent","purchase","tax_group_iva_ret_4","on_payment","cuenta216_10","Tasa","","base","invoice","","","Retención del IVA"
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","+DIOT: Retención",""
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","+DIOT: Retención",""
"tax18","20","1.25% WH","VAT witholding RESICO 1.25%","Retención IVA(-1.25%)","-1.25","percent","purchase","tax_group_iva_ret_1_25","on_payment","cuenta216_10","Tasa","0","base","invoice","","","Retención de IVA RESICO 1.25%"
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","+DIOT: Retención",""
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","+DIOT: Retención",""
"tax2","10","10% WH L","VAT withholding lease 10%","VAT withholding(-10%)","-10.0","percent","purchase","tax_group_iva_ret_10","on_payment","cuenta216_10","Tasa","","base","invoice","","","Retención IVA arrendamiento 10%"
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","+DIOT: Retención",""
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","+DIOT: Retención",""
"tax3","10","10% WH L I","Withholding of lease income tax 10%","Withholding income(-10%)","-10.0","percent","purchase","tax_group_isr_ret_10","","","Tasa","","base","invoice","","","Retención del impuesto de arrendamiento 10%"
"","","","","","","","","","","","","","tax","invoice","cuenta216_03","",""
"","","","","","","","","","","","","","base","refund","","",""
@@ -28,13 +28,13 @@
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_04","",""
"tax7","10","10.67% WH L","Vat withholding tax on leasing 10.67%","Vat withholding(-10.67%)","-10.67","percent","purchase","tax_group_iva_ret_1067","on_payment","cuenta216_10","Tasa","","base","invoice","","","Retención a cuenta del IVA sobre el arrendamiento financiero 10.67%"
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","+DIOT: Retención",""
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","+DIOT: Retención",""
"tax8","10","10.67% WH","Vat withholding fees 10.67%","Vat withholding(-10.67%)","-10.67","percent","purchase","tax_group_iva_ret_1067","on_payment","cuenta216_10","Tasa","","base","invoice","","","Retención del IVA 10.67%"
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","+DIOT: Retención",""
"","","","","","","","","","","","","","tax","invoice","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","base","refund","","",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","-DIOT: Retención",""
"","","","","","","","","","","","","","tax","refund","cuenta216_10_20","+DIOT: Retención",""
"tax13","10","0%","","VAT(0%)","0.0","percent","purchase","tax_group_iva_0","on_payment","cuenta119_01","Tasa","","base","invoice","","+DIOT: 0%",""
"","","","","","","","","","","","","","tax","invoice","cuenta118_01","",""
"","","","","","","","","","","","","","base","refund","","",""
1 id sequence name description invoice_label amount amount_type type_tax_use tax_group_id tax_exigibility cash_basis_transition_account_id l10n_mx_tax_type active repartition_line_ids/repartition_type repartition_line_ids/document_type repartition_line_ids/account_id repartition_line_ids/tag_ids description@es
8 base refund
9 tax refund cuenta208_01 l10n_mx.tag_iva
10 tax1 10 4% WH VAT withholding VAT withholding(-4%) -4.0 percent purchase tax_group_iva_ret_4 on_payment cuenta216_10 Tasa base invoice Retención del IVA
11 tax invoice cuenta216_10_20 +DIOT: Retención -DIOT: Retención
12 base refund
13 tax refund cuenta216_10_20 -DIOT: Retención +DIOT: Retención
14 tax18 20 1.25% WH VAT witholding RESICO 1.25% Retención IVA(-1.25%) -1.25 percent purchase tax_group_iva_ret_1_25 on_payment cuenta216_10 Tasa 0 base invoice Retención de IVA RESICO 1.25%
15 tax invoice cuenta216_10_20 +DIOT: Retención -DIOT: Retención
16 base refund
17 tax refund cuenta216_10_20 -DIOT: Retención +DIOT: Retención
18 tax2 10 10% WH L VAT withholding lease 10% VAT withholding(-10%) -10.0 percent purchase tax_group_iva_ret_10 on_payment cuenta216_10 Tasa base invoice Retención IVA arrendamiento 10%
19 tax invoice cuenta216_10_20 +DIOT: Retención -DIOT: Retención
20 base refund
21 tax refund cuenta216_10_20 -DIOT: Retención +DIOT: Retención
22 tax3 10 10% WH L I Withholding of lease income tax 10% Withholding income(-10%) -10.0 percent purchase tax_group_isr_ret_10 Tasa base invoice Retención del impuesto de arrendamiento 10%
23 tax invoice cuenta216_03
24 base refund
28 base refund
29 tax refund cuenta216_04
30 tax7 10 10.67% WH L Vat withholding tax on leasing 10.67% Vat withholding(-10.67%) -10.67 percent purchase tax_group_iva_ret_1067 on_payment cuenta216_10 Tasa base invoice Retención a cuenta del IVA sobre el arrendamiento financiero 10.67%
31 tax invoice cuenta216_10_20 +DIOT: Retención -DIOT: Retención
32 base refund
33 tax refund cuenta216_10_20 -DIOT: Retención +DIOT: Retención
34 tax8 10 10.67% WH Vat withholding fees 10.67% Vat withholding(-10.67%) -10.67 percent purchase tax_group_iva_ret_1067 on_payment cuenta216_10 Tasa base invoice Retención del IVA 10.67%
35 tax invoice cuenta216_10_20 +DIOT: Retención -DIOT: Retención
36 base refund
37 tax refund cuenta216_10_20 -DIOT: Retención +DIOT: Retención
38 tax13 10 0% VAT(0%) 0.0 percent purchase tax_group_iva_0 on_payment cuenta119_01 Tasa base invoice +DIOT: 0%
39 tax invoice cuenta118_01
40 base refund