From b96dc99f03c65dc74051ed4e2248c32b49449673 Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Tue, 25 Apr 2023 08:23:06 +0000 Subject: [PATCH] [FIX] account: tax computation: when forcing the tags on caba taxes, also force the account Cash basis taxes use a distinct transition account on their tax lines; the tax account set on the repartition lines is only used when the payment is received, on the cash basis move. In enterprise, the bank reconciliation widget allows setting a tax on the manual write-off done for a statement line. When doing so, we expect the tax to behave just like on an invoice. For cash basis taxes, this means the "final" tax account has to be used instead of the transition one ; it wasn't the case. OPW 3255511 X-original-commit: 991fa46081296c3560bd7ca195996693a81a46b8 Part-of: odoo/odoo#120924 --- addons/account/models/account_tax.py | 12 ++-- .../account/tests/test_account_move_entry.py | 59 +++++++++++++++++++ addons/l10n_in/models/account.py | 4 +- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index ca4e317be12..ff863ec2fd4 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -741,7 +741,7 @@ class AccountTax(models.Model): 'amount': sign * line_amount, 'base': float_round(sign * tax_base_amount, precision_rounding=prec), 'sequence': tax.sequence, - 'account_id': repartition_line._get_aml_target_tax_account().id, + 'account_id': repartition_line._get_aml_target_tax_account(force_caba_exigibility=include_caba_tags).id, 'analytic': tax.analytic, 'use_in_tax_closing': repartition_line.use_in_tax_closing, 'price_include': price_include, @@ -825,7 +825,7 @@ class AccountTax(models.Model): } @api.model - def _get_generation_dict_from_base_line(self, line_vals, tax_vals): + def _get_generation_dict_from_base_line(self, line_vals, tax_vals, force_caba_exigibility=False): """ Take a tax results returned by the taxes computation method and return a dictionary representing the way the tax amounts will be grouped together. To do so, the dictionary will be converted into a string key. Then, the existing tax lines sharing the same key will be updated and the missing ones will be created. @@ -835,7 +835,7 @@ class AccountTax(models.Model): :return: A python dict. """ tax_repartition_line = tax_vals['tax_repartition_line'] - tax_account = tax_repartition_line._get_aml_target_tax_account() or line_vals['account'] + tax_account = tax_repartition_line._get_aml_target_tax_account(force_caba_exigibility=force_caba_exigibility) or line_vals['account'] return { 'account_id': tax_account.id, 'currency_id': line_vals['currency'].id, @@ -1129,7 +1129,7 @@ class AccountTax(models.Model): existing_tax_line_map[grouping_key] = line_vals def grouping_key_generator(base_line, tax_values): - return self._get_generation_dict_from_base_line(base_line, tax_values) + return self._get_generation_dict_from_base_line(base_line, tax_values, force_caba_exigibility=include_caba_tags) # Update/create the tax lines. global_tax_details = self._aggregate_taxes(to_process, grouping_key_generator=grouping_key_generator) @@ -1387,13 +1387,13 @@ class AccountTaxRepartitionLine(models.Model): if self.repartition_type == 'base': self.account_id = None - def _get_aml_target_tax_account(self): + def _get_aml_target_tax_account(self, force_caba_exigibility=False): """ Get the default tax account to set on a business line. :return: An account.account record or an empty recordset. """ self.ensure_one() - if self.tax_id.tax_exigibility == 'on_payment' and not self._context.get('caba_no_transition_account'): + if not force_caba_exigibility and self.tax_id.tax_exigibility == 'on_payment' and not self._context.get('caba_no_transition_account'): return self.tax_id.cash_basis_transition_account_id else: return self.account_id diff --git a/addons/account/tests/test_account_move_entry.py b/addons/account/tests/test_account_move_entry.py index a3d5bcedb01..8936f78e3d6 100644 --- a/addons/account/tests/test_account_move_entry.py +++ b/addons/account/tests/test_account_move_entry.py @@ -906,3 +906,62 @@ class TestAccountMove(AccountTestInvoicingCommon): self.assertTrue(exchange_diff) with self.assertRaises(UserError), self.cr.savepoint(): exchange_diff.button_draft() + + def test_always_exigible_caba_account(self): + """ Always exigible misc operations (so, the ones without payable/receivable line) with cash basis + taxes should see their tax lines use the final tax account, not the transition account. + """ + tax_account = self.company_data['default_account_tax_sale'] + + caba_tax = self.env['account.tax'].create({ + 'name': "CABA", + 'amount_type': 'percent', + 'amount': 20.0, + 'tax_exigibility': 'on_payment', + 'cash_basis_transition_account_id': self.safe_copy(tax_account).id, + 'invoice_repartition_line_ids': [ + (0, 0, { + 'repartition_type': 'base', + }), + (0, 0, { + 'repartition_type': 'tax', + 'account_id': tax_account.id, + }), + ], + 'refund_repartition_line_ids': [ + (0, 0, { + 'repartition_type': 'base', + }), + (0, 0, { + 'repartition_type': 'tax', + 'account_id': tax_account.id, + }), + ], + }) + + move_form = Form(self.env['account.move'].with_context(default_move_type='entry')) + + # Create a new account.move.line with debit amount. + income_account = self.company_data['default_account_revenue'] + with move_form.line_ids.new() as debit_line: + debit_line.name = 'debit' + debit_line.account_id = income_account + debit_line.debit = 120 + + with move_form.line_ids.new() as credit_line: + credit_line.name = 'credit' + credit_line.account_id = income_account + credit_line.credit = 100 + credit_line.tax_ids.clear() + credit_line.tax_ids.add(caba_tax) + + move = move_form.save() + + self.assertTrue(move.always_tax_exigible) + + self.assertRecordValues(move.line_ids.sorted(lambda x: -x.balance), [ + # pylint: disable=C0326 + {'name': 'debit', 'debit': 120.0, 'credit': 0.0, 'account_id': income_account.id, 'tax_ids': [], 'tax_line_id': False}, + {'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}, + ]) diff --git a/addons/l10n_in/models/account.py b/addons/l10n_in/models/account.py index 43904b8bcd2..225582237f9 100644 --- a/addons/l10n_in/models/account.py +++ b/addons/l10n_in/models/account.py @@ -29,10 +29,10 @@ class AccountTax(models.Model): l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting") @api.model - def _get_generation_dict_from_base_line(self, line_vals, tax_vals): + def _get_generation_dict_from_base_line(self, line_vals, tax_vals, force_caba_exigibility=False): # EXTENDS account # Group taxes also by product. - res = super()._get_generation_dict_from_base_line(line_vals, tax_vals) + res = super()._get_generation_dict_from_base_line(line_vals, tax_vals, force_caba_exigibility=force_caba_exigibility) record = line_vals['record'] if isinstance(record, models.Model)\ and record._name == 'account.move.line'\