From 30a37c10ec8115b8c7b84f908a5fb4871d2f6cfb Mon Sep 17 00:00:00 2001 From: william-andre Date: Sat, 21 Oct 2023 19:04:24 +0200 Subject: [PATCH] [IMP] account: journal items match improvements This commit contains some misc improvements based on the 16.0 overall improvements regarding navigation and Journal Items in account.full.reconcile, 'name' field has been removed, and the replacement for matching_number will be the SQL id instead (no more sequences) For full reconciliations, the matching number now don't have an 'A' prefix For partial reconciliations, the matching number will have a format of 'P{id}' Community-pr: https://github.com/odoo/odoo/pull/138140 Enterprise-pr: https://github.com/odoo/enterprise/pull/48980 Task-id: 3530458 Part-of: odoo/odoo#138140 --- addons/account/data/account_data.xml | 12 --- .../account/models/account_full_reconcile.py | 8 +- addons/account/models/account_move_line.py | 35 ++++--- .../models/account_partial_reconcile.py | 51 +++++++++- .../tests/test_account_move_reconcile.py | 98 +++++++++++++++++++ .../views/account_full_reconcile_views.xml | 2 +- addons/account/views/account_move_views.xml | 2 +- addons/l10n_fr_fec/wizard/account_fr_fec.py | 2 +- 8 files changed, 175 insertions(+), 35 deletions(-) diff --git a/addons/account/data/account_data.xml b/addons/account/data/account_data.xml index 628649f58e8..4ca719fa04c 100644 --- a/addons/account/data/account_data.xml +++ b/addons/account/data/account_data.xml @@ -97,18 +97,6 @@ Command.create({'value': 'percent', 'value_amount': 100.0, 'nb_days': 30})]"/> - - - Account reconcile sequence - account.reconcile - A - - - - - Validated diff --git a/addons/account/models/account_full_reconcile.py b/addons/account/models/account_full_reconcile.py index d701816f756..a27c46839ec 100644 --- a/addons/account/models/account_full_reconcile.py +++ b/addons/account/models/account_full_reconcile.py @@ -6,7 +6,6 @@ class AccountFullReconcile(models.Model): _name = "account.full.reconcile" _description = "Full Reconcile" - name = fields.Char(string='Number', required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('account.reconcile')) partial_reconcile_ids = fields.One2many('account.partial.reconcile', 'full_reconcile_id', string='Reconciliation Parts') reconciled_line_ids = fields.One2many('account.move.line', 'full_reconcile_id', string='Matched Journal Items') exchange_move_id = fields.Many2one('account.move', index="btree_not_null") @@ -35,3 +34,10 @@ class AccountFullReconcile(models.Model): moves_to_reverse._reverse_moves(default_values_list, cancel=True) return res + + @api.model_create_multi + def create(self, vals_list): + fulls = super().create(vals_list) + for full in fulls: + full.reconciled_line_ids.matching_number = str(full.id) + return fulls diff --git a/addons/account/models/account_move_line.py b/addons/account/models/account_move_line.py index 268d15efd63..7d3640789d2 100644 --- a/addons/account/models/account_move_line.py +++ b/addons/account/models/account_move_line.py @@ -256,7 +256,7 @@ class AccountMoveLine(models.Model): ) matching_number = fields.Char( string="Matching #", - compute='_compute_matching_number', store=True, + readonly=True, help="Matching number for this line, 'P' if it is only partially reconcile, or the name of " "the full reconcile if it exists.", ) @@ -666,16 +666,6 @@ class AccountMoveLine(models.Model): if line.currency_id == line.company_id.currency_id: line.amount_currency = line.balance - @api.depends('full_reconcile_id.name', 'matched_debit_ids', 'matched_credit_ids') - def _compute_matching_number(self): - for record in self: - if record.full_reconcile_id: - record.matching_number = record.full_reconcile_id.name - elif record.matched_debit_ids or record.matched_credit_ids: - record.matching_number = 'P' - else: - record.matching_number = None - @api.depends_context('order_cumulated_balance', 'domain_cumulated_balance') def _compute_cumulated_balance(self): if not self.env.context.get('order_cumulated_balance'): @@ -2948,13 +2938,22 @@ class AccountMoveLine(models.Model): def _all_reconciled_lines(self): reconciliation_lines = self.filtered(lambda x: x.account_id.reconcile or x.account_id.account_type in ('asset_cash', 'liability_credit_card')) - current_lines = reconciliation_lines - current_partials = self.env['account.partial.reconcile'] - while current_lines: - current_partials = (current_lines.matched_debit_ids + current_lines.matched_credit_ids) - current_partials - current_lines = (current_partials.debit_move_id + current_partials.credit_move_id) - current_lines - reconciliation_lines += current_lines - return reconciliation_lines + self.env['account.partial.reconcile'].flush_model() + self.env.cr.execute(""" + WITH RECURSIVE partials (current_id) AS ( + SELECT line.id + FROM account_move_line line + WHERE id = ANY(%s) + + UNION + + SELECT CASE WHEN partial.debit_move_id = p.current_id THEN partial.credit_move_id ELSE partial.debit_move_id END + FROM partials p + JOIN account_partial_reconcile partial ON partial.debit_move_id = p.current_id OR partial.credit_move_id = p.current_id + ) + SELECT current_id FROM partials; + """, [reconciliation_lines.ids]) + return self.browse(r[0] for r in self.env.cr.fetchall()) def _get_attachment_domains(self): self.ensure_one() diff --git a/addons/account/models/account_partial_reconcile.py b/addons/account/models/account_partial_reconcile.py index 26098971bff..d86198049e7 100644 --- a/addons/account/models/account_partial_reconcile.py +++ b/addons/account/models/account_partial_reconcile.py @@ -8,7 +8,6 @@ from datetime import date class AccountPartialReconcile(models.Model): _name = "account.partial.reconcile" _description = "Partial Reconcile" - _rec_name = "id" # ==== Reconciliation fields ==== debit_move_id = fields.Many2one( @@ -106,6 +105,7 @@ class AccountPartialReconcile(models.Model): # Retrieve the matching number to unlink. full_to_unlink = self.full_reconcile_id + all_reconciled = self.debit_move_id + self.credit_move_id # Retrieve the CABA entries to reverse. moves_to_reverse = self.env['account.move'].search([('tax_cash_basis_rec_id', 'in', self.ids)]) @@ -126,8 +126,57 @@ class AccountPartialReconcile(models.Model): } for move in moves_to_reverse] moves_to_reverse._reverse_moves(default_values_list, cancel=True) + self._update_matching_number(all_reconciled) return res + @api.model_create_multi + def create(self, vals_list): + partials = super().create(vals_list) + self._update_matching_number(partials.debit_move_id + partials.credit_move_id) + return partials + + @api.model + def _update_matching_number(self, amls): + amls = amls._all_reconciled_lines() + self.env['account.partial.reconcile'].flush_model() + amls.flush_recordset(['full_reconcile_id']) + self.env.cr.execute(""" + WITH RECURSIVE partials (line_id, current_id) AS ( + SELECT id, id + FROM account_move_line + WHERE id = ANY(%s) + AND full_reconcile_id IS NULL + + UNION + + SELECT p.line_id, + CASE WHEN partial.debit_move_id = p.current_id THEN partial.credit_move_id + ELSE partial.debit_move_id + END + FROM partials p + JOIN account_partial_reconcile partial ON p.current_id = partial.debit_move_id + OR p.current_id = partial.credit_move_id + ) + SELECT line_id, 'P' || MIN(partial.id) AS matching_number + FROM partials + JOIN account_partial_reconcile partial ON current_id = partial.debit_move_id + OR current_id = partial.credit_move_id + GROUP BY line_id + """, [ + amls.ids, + ]) + + line_matching_number = dict(self.env.cr.fetchall()) + + for line in amls: + if line.full_reconcile_id: + line.matching_number = str(line.full_reconcile_id.id) + elif line.matched_debit_ids or line.matched_credit_ids: + line.matching_number = line_matching_number[line.id] + else: + line.matching_number = False + + # ------------------------------------------------------------------------- # RECONCILIATION METHODS # ------------------------------------------------------------------------- diff --git a/addons/account/tests/test_account_move_reconcile.py b/addons/account/tests/test_account_move_reconcile.py index f8a11433493..6cf0d32a25d 100644 --- a/addons/account/tests/test_account_move_reconcile.py +++ b/addons/account/tests/test_account_move_reconcile.py @@ -4000,6 +4000,104 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon): {'debit': 0.0, 'credit': 0.01, 'tax_ids': [], 'tax_tag_ids': self.tax_tags[5].ids, 'account_id': self.tax_account_2.id}, ]) + def test_matching_number_full_reconcile(self): + currency = self.env.company.currency_id + line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + line_b = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01') + (line_a + line_b).reconcile() + self.assertFullReconcile(line_a.full_reconcile_id, (line_a + line_b)) + self.assertEqual(line_a.matching_number, str(line_a.full_reconcile_id.id)) + self.assertEqual(line_a.matching_number, line_b.matching_number) + + def test_matching_number_partial_single_reconcile(self): + currency = self.env.company.currency_id + line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01') + (line_a + line_b).reconcile() + self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.id}') + self.assertEqual(line_a.matching_number, line_b.matching_number) + + def test_matching_number_partial_multi_reconcile(self): + currency = self.env.company.currency_id + line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01') + line_c = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01') + line_d = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + (line_a + line_b).reconcile() + (line_a + line_c).reconcile() + (line_c + line_d).reconcile() + self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}') + self.assertEqual(line_b.matching_number, line_a.matching_number) + self.assertEqual(line_c.matching_number, line_a.matching_number) + self.assertEqual(line_d.matching_number, line_a.matching_number) + + line_b.remove_move_reconcile() + self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}') + self.assertEqual(line_b.matching_number, False) + self.assertEqual(line_c.matching_number, f'P{line_c.matched_debit_ids.ids[0]}') + self.assertEqual(line_d.matching_number, line_c.matching_number) + + (line_a + line_b).reconcile() # everything should be matched again + self.assertEqual(line_a.matching_number, line_c.matching_number) + self.assertEqual(line_b.matching_number, line_c.matching_number) + self.assertEqual(line_c.matching_number, f'P{line_c.matched_debit_ids.ids[0]}') + self.assertEqual(line_d.matching_number, line_c.matching_number) + + def test_matching_number_partial_multi_separate_reconcile(self): + currency = self.env.company.currency_id + line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01') + (line_a + line_b).reconcile() + self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.id}') + self.assertEqual(line_a.matching_number, line_b.matching_number) + + line_c = self.create_line_for_reconciliation(-300, -300, currency, '2016-01-01') + (line_a + line_c).reconcile() + self.assertEqual(line_a.matching_number, f'P{line_a.matched_credit_ids.ids[0]}') + self.assertEqual(line_a.matching_number, line_b.matching_number) + self.assertEqual(line_a.matching_number, line_c.matching_number) + + def test_matching_number_unreconcile_single(self): + currency = self.env.company.currency_id + full_line_a = self.create_line_for_reconciliation(200, 200, currency, '2016-01-01') + full_line_b = self.create_line_for_reconciliation(-200, -200, currency, '2016-01-01') + partial_line_a = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + partial_line_b = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01') + (full_line_a + full_line_b).reconcile() + (partial_line_a + partial_line_b).reconcile() + (full_line_a + full_line_b + partial_line_a + partial_line_b).remove_move_reconcile() + self.assertFalse(full_line_a.matching_number) + self.assertFalse(full_line_b.matching_number) + self.assertFalse(partial_line_a.matching_number) + self.assertFalse(partial_line_b.matching_number) + + def test_matching_number_unreconcile_multi(self): + currency = self.env.company.currency_id + line_a = self.create_line_for_reconciliation(-500, -500, currency, '2016-01-01') + line_b = self.create_line_for_reconciliation(1000, 1000, currency, '2016-01-01') + line_c = self.create_line_for_reconciliation(-1000, -1000, currency, '2016-01-01') + line_d = self.create_line_for_reconciliation(300, 300, currency, '2016-01-01') + (line_a + line_b).reconcile() + (line_b + line_c).reconcile() + (line_c + line_d).reconcile() + + previous_matching_number = line_a.matching_number + line_a.remove_move_reconcile() + self.assertFalse(line_a.matching_number) + self.assertNotEqual(previous_matching_number, line_b.matching_number) + self.assertEqual(line_b.matching_number, line_c.matching_number) + self.assertEqual(line_b.matching_number, line_d.matching_number) + + previous_matching_number = line_b.matching_number + line_b.remove_move_reconcile() + self.assertFalse(line_b.matching_number) + self.assertNotEqual(previous_matching_number, line_c.matching_number) + self.assertEqual(line_c.matching_number, line_d.matching_number) + + line_c.remove_move_reconcile() + self.assertFalse(line_c.matching_number) + self.assertFalse(line_d.matching_number) + def test_caba_mix_reconciliation(self): """ Test the reconciliation of tax lines (when using a reconcilable tax account) for cases mixing taxes exigible on payment and on invoices. diff --git a/addons/account/views/account_full_reconcile_views.xml b/addons/account/views/account_full_reconcile_views.xml index d96c83359b9..f1760e5d244 100644 --- a/addons/account/views/account_full_reconcile_views.xml +++ b/addons/account/views/account_full_reconcile_views.xml @@ -9,7 +9,7 @@
-

+

diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml index e2400c3cf88..565d4f336e5 100644 --- a/addons/account/views/account_move_views.xml +++ b/addons/account/views/account_move_views.xml @@ -360,7 +360,7 @@ - +