From b66c4fd0f71864c17dc6036d2285efe534a67955 Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Mon, 2 Nov 2020 12:21:58 +0000 Subject: [PATCH] [FIX] account: reconciliation widget: make invoice matching reconciliation models work with write-off lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use case: 1) Create an invoice matching reconciliation model, matching 90% percent of the amount, with a write-off line applying a 100% write-off of the remaining amount. 2) Create a 100€ invoice 3) Create a bank statement with a 90€ statement line whose payment reference corresponds to the one of the invoice 4) Click 'reconcile' on the bank statement On enterprise, this crashes when preparing the data to give to the reconciliation widget, as the write_off_vals key is not set properly by the call to _apply_rules. OPW 2367754 closes odoo/odoo#61265 X-original-commit: 1dd6199519b4ad5b5b17dfa17965ededcecb6da4 Related: odoo/enterprise#14563 Signed-off-by: Laurent Smet Signed-off-by: oco-odoo --- .../account/models/account_reconcile_model.py | 6 ++-- .../test_reconciliation_matching_rules.py | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 25e8733c725..82ec44563a6 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -317,7 +317,7 @@ class AccountReconcileModel(models.Model): lines_vals_list = [] for line in self.line_ids: - currency_id = st_line.currency_id or st_line.journal_id.currency_id or self.company_id.currency_id + currency_id = st_line.currency_id or st_line.journal_id.currency_id or self.company_id.currency_id if not line.account_id or currency_id.is_zero(residual_balance): return [] @@ -795,8 +795,10 @@ class AccountReconcileModel(models.Model): lines_vals_list = self._prepare_reconciliation(st_line, aml_ids=rslt['aml_ids'], partner=partner) # A write-off must be applied if there are some 'new' lines to propose. - if not lines_vals_list or any(not line_vals.get('id') for line_vals in lines_vals_list): + write_off_lines_vals = list(filter(lambda x: 'id' not in x, lines_vals_list)) + if not lines_vals_list or write_off_lines_vals: rslt['status'] = 'write_off' + rslt['write_off_vals'] = write_off_lines_vals # Process auto-reconciliation. We only do that for the first two priorities, if they are not matched elsewhere. if lines_vals_list and priorities & {1, 3} and self.auto_reconcile: diff --git a/addons/account/tests/test_reconciliation_matching_rules.py b/addons/account/tests/test_reconciliation_matching_rules.py index 1b868f0e132..5ec74054b85 100644 --- a/addons/account/tests/test_reconciliation_matching_rules.py +++ b/addons/account/tests/test_reconciliation_matching_rules.py @@ -712,3 +712,37 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon): self._check_statement_matching(matching_rule, { statement_line.id: {'aml_ids': (move_line_1 + move_line_2).ids, 'model': matching_rule, 'partner': statement_line.partner_id} }, statements=statement) + + def test_inv_matching_with_write_off(self): + self.rule_1.match_total_amount_param = 90 + self.bank_st.line_ids[1].unlink() # We don't need this one here + statement_line = self.bank_st.line_ids[0] + statement_line.write({ + 'payment_ref': self.invoice_line_1.move_id.payment_reference, + 'amount': 90, + }) + + # Test the invoice-matching part + self._check_statement_matching(self.rule_1, { + statement_line.id: {'aml_ids': self.invoice_line_1.ids, 'model': self.rule_1, 'partner': self.invoice_line_1.partner_id, 'status': 'write_off'}, + }, self.bank_st) + + # Test the write-off part + expected_write_off = { + 'balance': 10, + 'currency_id': False, + 'reconcile_model_id': self.rule_1.id, + 'account_id': self.current_assets_account.id, + } + + matching_result = self.rule_1._apply_rules(statement_line) + + self.assertEqual(len(matching_result[statement_line.id].get('write_off_vals', [])), 1, "Exactly one write-off line should be proposed.") + + full_write_off_dict = matching_result[statement_line.id]['write_off_vals'][0] + to_compare = { + key: full_write_off_dict[key] + for key in expected_write_off.keys() + } + + self.assertDictEqual(expected_write_off, to_compare)