From addd40a09fcd22bb662e1cc7d2e858bcc39a2c05 Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Wed, 9 Dec 2020 15:50:54 +0000 Subject: [PATCH] [FIX] account: reconciliation models: fix auto-reconciliation result when the reconciliation doesn't cover the whole invoice Before that, using an auto-validate reconciliation model to match an invoice with a statement line with a lesser amount resulted in the creation of an payment entry with which the invoice got fully reconciled, and the difference amount was fowarded to the suspsense account in an additional line. This wasn't correct, as we expect the invoice to be only partially paid in that case (with an amount equal to the statement line's). closes odoo/odoo#63303 X-original-commit: 7393aa3ebcea7b80762b729315f1c890bc677efb Signed-off-by: Laurent Smet --- .../account/models/account_reconcile_model.py | 39 ++++++++++++++----- .../test_reconciliation_matching_rules.py | 21 ++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 51b5088ad71..be3edf6acc1 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -385,26 +385,45 @@ class AccountReconcileModel(models.Model): liquidity_lines, suspense_lines, other_lines = st_line._seek_for_lines() if st_line.to_check: - residual_balance = -liquidity_lines.balance + st_line_residual = -liquidity_lines.balance elif suspense_lines.account_id.reconcile: - residual_balance = sum(suspense_lines.mapped('amount_residual')) + st_line_residual = sum(suspense_lines.mapped('amount_residual')) else: - residual_balance = sum(suspense_lines.mapped('balance')) + st_line_residual = sum(suspense_lines.mapped('balance')) partner = partner or st_line.partner_id - lines_vals_list = [{'id': aml_id} for aml_id in aml_ids] + + lines_vals_list = [] + amls = self.env['account.move.line'].browse(aml_ids) + st_line_residual_before = st_line_residual + aml_total_residual = 0 + for aml in amls: + aml_total_residual += aml.amount_residual + + if aml.balance * st_line_residual > 0: + # Meaning they have the same signs, so they can't be reconciled together + assigned_balance = -aml.amount_residual + else: + assigned_balance = min(-aml.amount_residual, st_line_residual, key=abs) + st_line_residual -= assigned_balance + + lines_vals_list.append({ + 'id': aml.id, + 'balance': assigned_balance, + 'currency_id': st_line.move_id.company_id.currency_id.id, + }) + + write_off_amount = max(aml_total_residual, -st_line_residual_before, key=abs) + st_line_residual_before + st_line_residual + reconciliation_overview, open_balance_vals = st_line._prepare_reconciliation(lines_vals_list) - for reconciliation_vals in reconciliation_overview: - residual_balance -= reconciliation_vals['line_vals']['debit'] - reconciliation_vals['line_vals']['credit'] - - writeoff_vals_list = self._get_write_off_move_lines_dict(st_line, residual_balance) + writeoff_vals_list = self._get_write_off_move_lines_dict(st_line, write_off_amount) for line_vals in writeoff_vals_list: - residual_balance -= st_line.company_currency_id.round(line_vals['balance']) + st_line_residual -= st_line.company_currency_id.round(line_vals['balance']) # Check we have enough information to create an open balance. - if not st_line.company_currency_id.is_zero(residual_balance): + if not st_line.company_currency_id.is_zero(st_line_residual): if st_line.amount > 0: open_balance_account = partner.property_account_receivable_id else: diff --git a/addons/account/tests/test_reconciliation_matching_rules.py b/addons/account/tests/test_reconciliation_matching_rules.py index 5ec74054b85..ef471961a03 100644 --- a/addons/account/tests/test_reconciliation_matching_rules.py +++ b/addons/account/tests/test_reconciliation_matching_rules.py @@ -447,6 +447,27 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): {'partner_id': self.partner_2.id, 'debit': 1000.0, 'credit': 0.0}, ]) + def test_larger_invoice_auto_reconcile(self): + ''' Test auto reconciliation with an invoice with larger amount than the statement line's.''' + self.bank_line_1.amount = 40 + self.invoice_line_1.move_id.payment_reference = self.bank_line_1.payment_ref + + self.rule_1.sequence = 2 + self.rule_1.auto_reconcile = True + + self._check_statement_matching(self.rule_1, { + self.bank_line_1.id: {'aml_ids': [self.invoice_line_1.id], 'model': self.rule_1, 'status': 'reconciled', 'partner': self.bank_line_1.partner_id}, + self.bank_line_2.id: {'aml_ids': []}, + }, statements=self.bank_st) + + # Check first line has been well reconciled. + self.assertRecordValues(self.bank_line_1.line_ids, [ + {'partner_id': self.partner_1.id, 'debit': 40.0, 'credit': 0.0}, + {'partner_id': self.partner_1.id, 'debit': 0.0, 'credit': 40.0}, + ]) + + self.assertEqual(self.invoice_line_1.amount_residual, 60.0, "The invoice should have been partially reconciled") + def test_auto_reconcile_with_tax(self): ''' Test auto reconciliation with a tax amount included in the bank statement line''' self.rule_1.write({