From aa856399dbf1a014308cf2c35d862c9da36b71c1 Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Wed, 23 Feb 2022 17:00:48 +0000 Subject: [PATCH] [FIX] account: Fix tax details with negative invoice lines Before this commit: Suppose invl1 of 1000.0 with 10.0% and invl2 of -100.0 with the same tax. A tax line of 90.0 is created. If invl1 is created before invl2, the tax details is computing the tax amounts as: invl1 tax amount = 1000/900 * 90 = 100 (OK) invl2 tax amount = 90 - 100 = -10 (OK) total tax amount = 100 - 10 = 90 (OK) If invl2 is created before invl2: invl1 tax amount = 100/900 * 90 = 10 (NOT OK) invl2 tax amount = 90 - 10 = 80 (NOT OK) total tax amount = 10 + 80 = 90 (OK) closes odoo/odoo#85406 X-original-commit: bada85c7621b45970c88548c4d09b88b77c08e8e Signed-off-by: Olivier Colson Signed-off-by: Laurent Smet --- .../models/account_move_line_tax_details.py | 16 +++---- .../test_account_move_line_tax_details.py | 48 ++++++++++++++++++- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/addons/account/models/account_move_line_tax_details.py b/addons/account/models/account_move_line_tax_details.py index 1a3ba920b3a..b38fddb080b 100644 --- a/addons/account/models/account_move_line_tax_details.py +++ b/addons/account/models/account_move_line_tax_details.py @@ -319,11 +319,11 @@ class AccountMoveLine(models.Model): sub.src_line_id, ROUND( - COALESCE(sub.total_tax_amount * ABS(sub.cumulated_base_amount) / ABS(NULLIF(sub.total_base_amount, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount) * sub.total_tax_amount * ABS(sub.cumulated_base_amount) / NULLIF(sub.total_base_amount, 0.0), 0.0), sub.comp_curr_prec ) - LAG(ROUND( - COALESCE(sub.total_tax_amount * ABS(sub.cumulated_base_amount) / ABS(NULLIF(sub.total_base_amount, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount) * sub.total_tax_amount * ABS(sub.cumulated_base_amount) / NULLIF(sub.total_base_amount, 0.0), 0.0), sub.comp_curr_prec ), 1, 0.0) OVER ( @@ -331,11 +331,11 @@ class AccountMoveLine(models.Model): ) AS base_amount, ROUND( - COALESCE(sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / ABS(NULLIF(sub.total_base_amount_currency, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount_currency) * sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / NULLIF(sub.total_base_amount_currency, 0.0), 0.0), sub.curr_prec ) - LAG(ROUND( - COALESCE(sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / ABS(NULLIF(sub.total_base_amount_currency, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount_currency) * sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / NULLIF(sub.total_base_amount_currency, 0.0), 0.0), sub.curr_prec ), 1, 0.0) OVER ( @@ -459,11 +459,11 @@ class AccountMoveLine(models.Model): sub.base_amount, ROUND( - COALESCE(sub.total_tax_amount * ABS(sub.cumulated_base_amount) / ABS(NULLIF(sub.total_base_amount, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount) * sub.total_tax_amount * ABS(sub.cumulated_base_amount) / NULLIF(sub.total_base_amount, 0.0), 0.0), sub.comp_curr_prec ) - LAG(ROUND( - COALESCE(sub.total_tax_amount * ABS(sub.cumulated_base_amount) / ABS(NULLIF(sub.total_base_amount, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount) * sub.total_tax_amount * ABS(sub.cumulated_base_amount) / NULLIF(sub.total_base_amount, 0.0), 0.0), sub.comp_curr_prec ), 1, 0.0) OVER ( @@ -472,11 +472,11 @@ class AccountMoveLine(models.Model): sub.base_amount_currency, ROUND( - COALESCE(sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / ABS(NULLIF(sub.total_base_amount_currency, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount_currency) * sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / NULLIF(sub.total_base_amount_currency, 0.0), 0.0), sub.curr_prec ) - LAG(ROUND( - COALESCE(sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / ABS(NULLIF(sub.total_base_amount_currency, 0.0)), 0.0), + COALESCE(SIGN(sub.cumulated_base_amount_currency) * sub.total_tax_amount_currency * ABS(sub.cumulated_base_amount_currency) / NULLIF(sub.total_base_amount_currency, 0.0), 0.0), sub.curr_prec ), 1, 0.0) OVER ( diff --git a/addons/account/tests/test_account_move_line_tax_details.py b/addons/account/tests/test_account_move_line_tax_details.py index 4e8d5669e11..55c94463314 100644 --- a/addons/account/tests/test_account_move_line_tax_details.py +++ b/addons/account/tests/test_account_move_line_tax_details.py @@ -17,8 +17,8 @@ class TestAccountTaxDetailsReport(AccountTestInvoicingCommon): .sorted(lambda x: (x.move_id, x.tax_line_id, x.tax_ids, x.tax_repartition_line_id)) return base_lines, tax_lines - def _get_tax_details(self, fallback=False): - domain = [('company_id', '=', self.env.company.id)] + def _get_tax_details(self, fallback=False, extra_domain=None): + domain = [('company_id', '=', self.env.company.id)] + (extra_domain or []) tax_details_query, tax_details_params = self.env['account.move.line']._get_query_tax_details_from_domain(domain, fallback=fallback) self.cr.execute(tax_details_query, tax_details_params) tax_details_res = self.cr.dictfetchall() @@ -1252,3 +1252,47 @@ class TestAccountTaxDetailsReport(AccountTestInvoicingCommon): ], ) self.assertTotalAmounts(invoice, tax_details) + + def test_amounts_sign(self): + for tax_sign in (1, -1): + tax = self.env['account.tax'].create({ + 'name': "tax", + 'amount_type': 'percent', + 'amount': tax_sign * 10.0, + }) + + amounts_list = [ + (-1000.0, 7000.0, -2000.0), + (1000.0, -7000.0, 2000.0), + (-1000.0, -7000.0, 2000.0), + (1000.0, 7000.0, -2000.0), + ] + for amounts in amounts_list: + with self.subTest(tax_sign=tax_sign, amounts=amounts): + invoice = self.env['account.move'].create({ + 'move_type': 'in_invoice', + 'partner_id': self.partner_a.id, + 'invoice_date': '2019-01-01', + 'invoice_line_ids': [ + Command.create({ + 'name': 'line2', + 'account_id': self.company_data['default_account_revenue'].id, + 'price_unit': amount, + 'tax_ids': [Command.set(tax.ids)], + }) + for amount in amounts], + }) + _base_lines, tax_lines = self._dispatch_move_lines(invoice) + + tax_details = self._get_tax_details(extra_domain=[('move_id', '=', invoice.id)]) + self.assertTaxDetailsValues( + tax_details, + [ + { + 'tax_line_id': tax_lines[0].id, + 'base_amount': amount, + 'tax_amount': tax_sign * amount * 0.1, + } + for amount in amounts], + ) + self.assertTotalAmounts(invoice, tax_details)