[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:
@@ -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': [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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","","",""
|
||||
|
||||
|
Reference in New Issue
Block a user