From 85c8a478446b597be8f21fc83ead58d6aa20741b Mon Sep 17 00:00:00 2001 From: "Andrea Grazioso (agr-odoo)" Date: Mon, 17 May 2021 13:44:46 +0000 Subject: [PATCH] [FIX] account: process duplicate tax lines when creating CABA entries - Enable cash basis (Accounting>Settings>Taxes) - Enable analytic accounting (Accounting>Settings>Analytics) - Have a sale tax: - Based on payment (Account 10100 Current Assets) - Included in Analytic Cost - Create a [DEMO] product with such tax - Create an invoice with 2 lines: - One line with product DEMO and whatever analytic account - One line with product DEMO but no analytic account -Confirm and receive payment CABA entry will consider only 1 tax line, because the 2 entries are equal beside the analytic account which is not considered in the grouping keys opw-2507417 closes odoo/odoo#71556 X-original-commit: 2a8f777c04bdbdce77dc44d92e099612f158cbef Signed-off-by: oco-odoo --- addons/account/models/account_move.py | 19 ++++--- .../models/account_partial_reconcile.py | 42 ++++++++------- .../tests/test_account_move_reconcile.py | 52 +++++++++++++++++++ 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 1edd595800c..358b1f917c7 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -4540,12 +4540,19 @@ class AccountMoveLine(models.Model): if line.tax_repartition_line_id: # Tax line. grouping_key = self.env['account.partial.reconcile']._get_cash_basis_tax_line_grouping_key_from_record(line) - account_vals_to_fix[grouping_key] = { - **vals, - 'account_id': line.account_id.id, - 'tax_base_amount': line.tax_base_amount, - 'tax_repartition_line_id': line.tax_repartition_line_id.id, - } + if grouping_key in account_vals_to_fix: + account_vals_to_fix[grouping_key].update({ + 'debit': account_vals_to_fix[grouping_key]['debit'] + vals['debit'], + 'credit': account_vals_to_fix[grouping_key]['credit'] + vals['credit'], + 'tax_base_amount': account_vals_to_fix[grouping_key]['tax_base_amount'] + line.tax_base_amount, + }) + else: + account_vals_to_fix[grouping_key] = { + **vals, + 'account_id': line.account_id.id, + 'tax_base_amount': line.tax_base_amount, + 'tax_repartition_line_id': line.tax_repartition_line_id.id, + } elif line.tax_ids: # Base line. account_to_fix = line.company_id.account_cash_basis_base_account_id diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 5ed93aec365..669dedc9e9b 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -427,33 +427,35 @@ class AccountPartialReconcile(models.Model): if line.tax_repartition_line_id: # Tax line. - cb_tax_line_vals = self._prepare_cash_basis_tax_line_vals(line, balance, amount_currency) - grouping_key = self._get_cash_basis_tax_line_grouping_key_from_vals(cb_tax_line_vals) - partial_lines_to_create[grouping_key] = { - 'tax_line': line, - 'vals': cb_tax_line_vals, - } - + cb_line_vals = self._prepare_cash_basis_tax_line_vals(line, balance, amount_currency) + grouping_key = self._get_cash_basis_tax_line_grouping_key_from_vals(cb_line_vals) elif line.tax_ids: # Base line. - cb_base_line_vals = self._prepare_cash_basis_base_line_vals(line, balance, amount_currency) - grouping_key = self._get_cash_basis_base_line_grouping_key_from_vals(cb_base_line_vals) + cb_line_vals = self._prepare_cash_basis_base_line_vals(line, balance, amount_currency) + grouping_key = self._get_cash_basis_base_line_grouping_key_from_vals(cb_line_vals) - if grouping_key in partial_lines_to_create: - aggregated_vals = partial_lines_to_create[grouping_key]['vals'] - balance = aggregated_vals['debit'] - aggregated_vals['credit'] - balance += cb_base_line_vals['debit'] - cb_base_line_vals['credit'] + if grouping_key in partial_lines_to_create: + aggregated_vals = partial_lines_to_create[grouping_key]['vals'] + aggregated_vals.update({ + 'debit': aggregated_vals['debit'] + cb_line_vals['debit'], + 'credit': aggregated_vals['credit'] + cb_line_vals['credit'], + 'amount_currency': aggregated_vals['amount_currency'] + cb_line_vals['amount_currency'], + }) + if line.tax_repartition_line_id: aggregated_vals.update({ - 'debit': balance if balance > 0.0 else 0.0, - 'credit': -balance if balance < 0.0 else 0.0, + 'tax_base_amount': aggregated_vals['tax_base_amount'] + cb_line_vals['tax_base_amount'], + }) + partial_lines_to_create[grouping_key]['tax_line'] += line + else: + partial_lines_to_create[grouping_key] = { + 'vals': cb_line_vals, + } + if line.tax_repartition_line_id: + partial_lines_to_create[grouping_key].update({ + 'tax_line': line, }) - aggregated_vals['amount_currency'] += cb_base_line_vals['amount_currency'] - else: - partial_lines_to_create[grouping_key] = { - 'vals': cb_base_line_vals, - } # ========================================================================== # Create the counterpart journal items. diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index 22d8940fb3b..3ec8d6ea62a 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.tests import tagged +from odoo.tests.common import Form +from odoo import fields @tagged('post_install', '-at_install') @@ -2076,6 +2078,56 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): # Check full reconciliation self.assertTrue(all(line.full_reconcile_id for line in lines_to_reconcile), "All tax lines should be fully reconciled") + def test_caba_double_tax(self): + """ Test the CABA entries generated from an invoice with almost + equal lines, different only on analytic accounting + """ + # Make the tax account reconcilable + self.tax_account_1.reconcile = True + + # Create an invoice with a CABA tax using 'Include in analytic cost' + move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice', account_predictive_bills_disable_prediction=True)) + move_form.invoice_date = fields.Date.from_string('2019-01-01') + move_form.partner_id = self.partner_a + self.cash_basis_tax_a_third_amount.analytic = True + test_analytic_account = self.env['account.analytic.account'].create({'name': 'test_analytic_account'}) + + tax = self.cash_basis_tax_a_third_amount + + # line with analytic account, will generate 2 lines in CABA move + with move_form.invoice_line_ids.new() as line_form: + line_form.name = "test line with analytic account" + line_form.product_id = self.product_a + line_form.tax_ids.clear() + line_form.tax_ids.add(tax) + line_form.analytic_account_id = test_analytic_account + line_form.price_unit = 100 + + # line with analytic account, will generate other 2 lines in CABA move + # even if the tax is the same + with move_form.invoice_line_ids.new() as line_form: + line_form.name = "test line" + line_form.product_id = self.product_a + line_form.tax_ids.clear() + line_form.tax_ids.add(tax) + line_form.price_unit = 100 + + rslt = move_form.save() + rslt.action_post() + + pmt_wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=rslt.ids).create({ + 'amount': rslt.amount_total, + 'payment_date': rslt.date, + 'journal_id': self.company_data['default_journal_bank'].id, + 'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id, + }) + pmt_wizard._create_payments() + + partial_rec = rslt.mapped('line_ids.matched_debit_ids') + caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', '=', partial_rec.id)]) + self.assertEqual(len(caba_move.line_ids), 4, "All lines should be there") + self.assertEqual(caba_move.line_ids.filtered(lambda x: x.tax_line_id).balance, 66.66, "Tax amount should take into account both lines") + def test_caba_dest_acc_reconciliation_partial_pmt(self): """ Test the reconciliation of tax lines (when using a reconcilable tax account) for partially paid invoices with cash basis taxes.