From b6616b09459cf9df1ebc13d23e79a0b7e104c406 Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Tue, 12 Jan 2021 13:30:20 +0000 Subject: [PATCH] [FIX] account: reconciliation models: allow disabling bypass of matching amount check for more advanced cases Reconciliation models can now create partial payment on invoices. Because of that, the default behavior is not anymore to fully pay the invoice and create an open balance write off. In some cases, though, this is what we want (exchange difference, ...). The solution for that is to create a reconciliation model with write off lines (telling it it must only apply if there is a 90% percent match on the amount, for example), and then create another one not checking the amount to just match the things to reconcile partially by using the partner. However, as it is now, reconciliation models always bypass the check made on the amount in case the statement line's label matches the invoice's payment reference exactly. This can lead to cases where we are matching elements we shouldn't with the rule doing the write off. Example: - use only two 'invoice matching' reconciliation models, in the following application sequence: - model 1, matching partner if with amount matching percentage is 90%, and doing a write-off for the unmatched amount - model 2, matching partner with no amount matching percentage, and no write off - create an invoice with payment refrence INV1 for partner A, amount: 100 - create an invoice with payment refernce INV2 for partner A, amount: 200 - create a statement with two lines: - line 1, with 99 for partner A, label= INV1. - line 2, with 1 for partner A, label= INV2. - Open the reconciliation widget ==> Before the fix: model 1 is applied on both statement lines, as their label match payment references. This is what we want for line 1, but probably not for line 2, which will create a write off of 199, while we'd have wished for a partial payment, leaving the invoice with 199 open. ==> After the fix The behavior is the same unless the config_parameter is set. If it is: - line 1 will be matched by model 1 (as 99 - 9,9 < 100 < 99 + 9,9). INV1 will be paid with a write off of 1. - line 2 will not be matched by model 1 (as 200 is not between 1 - 0,1 and 1 + 0,1), but it will be by model 2 (since line 2 and INV 2 share the same partner). So, INV2 will be partially reconciled with line 2. OPW 2419030 closes odoo/odoo#64504 X-original-commit: ed023e208736e58d14649aa2fd91fb00510f20c1 Signed-off-by: Laurent Smet Signed-off-by: oco-odoo --- .../account/models/account_reconcile_model.py | 3 +- .../test_reconciliation_matching_rules.py | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 4b1582535b8..b22aa22a9e9 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -803,7 +803,8 @@ class AccountReconcileModel(models.Model): # We check the amount criteria of the reconciliation model, and select the # candidates if they pass the verification. Candidates from the first priority # level (even already selected) bypass this check, and are selected anyway. - if priorities & {1,2} or self._check_rule_propositions(st_line, candidates): + disable_bypass = self.env['ir.config_parameter'].sudo().get_param('account.disable_rec_models_bypass') + if (not disable_bypass and priorities & {1,2}) or self._check_rule_propositions(st_line, candidates): rslt = { 'model': self, 'aml_ids': [candidate['aml_id'] for candidate in candidates], diff --git a/addons/account/tests/test_reconciliation_matching_rules.py b/addons/account/tests/test_reconciliation_matching_rules.py index 98cbcb51c33..b31e40eab5c 100644 --- a/addons/account/tests/test_reconciliation_matching_rules.py +++ b/addons/account/tests/test_reconciliation_matching_rules.py @@ -824,3 +824,35 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): ]) self.assertEqual(self.invoice_line_1.amount_residual, 0.0, "The invoice should have been fully reconciled") + + def test_avoid_amount_matching_bypass(self): + """ By the default, if the label of statement lines exactly matches a payment reference, it bypasses any kind of amount verification. + This is annoying in some setups, so a config parameter was introduced to handle that. + """ + self.env['ir.config_parameter'].set_param('account.disable_rec_models_bypass', '1') + self.rule_1.match_total_amount_param = 90 + second_inv_matching_rule = self.env['account.reconcile.model'].create({ + 'name': 'Invoices Matching Rule', + 'sequence': 2, + 'rule_type': 'invoice_matching', + 'auto_reconcile': False, + 'match_nature': 'both', + 'match_same_currency': False, + 'match_total_amount': False, + 'match_partner': True, + 'company_id': self.company.id, + }) + + self.bank_line_1.write({ + 'payment_ref': self.invoice_line_1.move_id.payment_reference, + 'amount': 99, + }) + self.bank_line_2.write({ + 'payment_ref': self.invoice_line_2.move_id.payment_reference, + 'amount': 1, + }) + + self._check_statement_matching(self.rule_1 + second_inv_matching_rule, { + self.bank_line_1.id: {'aml_ids': [self.invoice_line_1.id], 'model': self.rule_1, 'status': 'write_off', 'partner': self.bank_line_1.partner_id}, + self.bank_line_2.id: {'aml_ids': [self.invoice_line_2.id], 'model': second_inv_matching_rule, 'partner': self.bank_line_2.partner_id} + }, statements=self.bank_st)