From c5e380fc56797a8983f075a7d8fcafebca0c1667 Mon Sep 17 00:00:00 2001 From: sesn-odoo Date: Thu, 1 Feb 2024 07:20:49 +0100 Subject: [PATCH] [FIX] account, sale: prevent reversal of moves across companies/branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce: - Create a child company. - In the child company, create a new sales journal. - Create and confirm an invoice using this new journal. - Attempt to create a credit note from that invoice. In this scenario, you would encounter an error. Cause: The `AccountMoveReversal` wizard is currently setting its company to the root company of the moves, which in this case is the parent company. However, its journal is set to the one created in the child company. This mismatch causes an error due to company inconsistency. Fix: The `company_id` of `AccountMoveReversal` will now be assigned to the company of the moves, rather than the root company. To ensure this works correctly, we also added a check to guarantee that all moves being reversed are from the same company. Note: This fix also resolves an issue where a traceback occurred if two invoices were created (one in the child company and another in the parent company) and an attempt was made to reverse both simultaneously. opw-3640719 closes odoo/odoo#153396 X-original-commit: cd62e3fcaab3e71e606607308cace739bc480c4f Signed-off-by: Florian Gilbert (flg) Signed-off-by: Séna Serge Nshimiyimana (sesn) --- .../tests/test_account_move_out_invoice.py | 29 +++++++++++++++++++ .../account/wizard/account_move_reversal.py | 5 +++- addons/sale/tests/test_credit_limit.py | 12 ++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index 2023e922394..4e5f815a3a1 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -4050,3 +4050,32 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): # The integrity check should work integrity_check = invoice.company_id._check_hash_integrity()['results'][0] self.assertEqual(integrity_check['msg_cover'], 'All entries are hashed.') + + def test_out_invoice_create_cross_branch_refund(self): + """You should not be able to reverse moves from different branches.""" + + # create a new branch + self.env.company.write({ + 'child_ids': [ + Command.create({'name': 'Branch A'}), + ], + }) + self.cr.precommit.run() # load the CoA + + # create an invoice on the new branch + branch_a = self.env.company.child_ids + branch_invoice = self.init_invoice('out_invoice', products=self.product_a, company=branch_a) + + branch_invoice.action_post() + self.invoice.action_post() + + with self.assertRaises(UserError) as error_catcher: + # attempt to reverse both the parent's and the branch's move at once + move_reversal = self.env['account.move.reversal'].with_context( + active_model="account.move", + active_ids=(branch_invoice + self.invoice).ids, + ).create({}) + + move_reversal.refund_moves() + + self.assertEqual(error_catcher.exception.args[0], "All selected moves for reversal must belong to the same company.") diff --git a/addons/account/wizard/account_move_reversal.py b/addons/account/wizard/account_move_reversal.py index 25f1b771465..0306b567c54 100644 --- a/addons/account/wizard/account_move_reversal.py +++ b/addons/account/wizard/account_move_reversal.py @@ -68,10 +68,13 @@ class AccountMoveReversal(models.TransientModel): res = super(AccountMoveReversal, self).default_get(fields) move_ids = self.env['account.move'].browse(self.env.context['active_ids']) if self.env.context.get('active_model') == 'account.move' else self.env['account.move'] + if len(move_ids.company_id) > 1: + raise UserError(_("All selected moves for reversal must belong to the same company.")) + if any(move.state != "posted" for move in move_ids): raise UserError(_('You can only reverse posted moves.')) if 'company_id' in fields: - res['company_id'] = move_ids.company_id.root_id.id or self.env.company.id + res['company_id'] = move_ids.company_id.id or self.env.company.id if 'move_ids' in fields: res['move_ids'] = [(6, 0, move_ids.ids)] return res diff --git a/addons/sale/tests/test_credit_limit.py b/addons/sale/tests/test_credit_limit.py index 426210ea9d2..df53466bd52 100644 --- a/addons/sale/tests/test_credit_limit.py +++ b/addons/sale/tests/test_credit_limit.py @@ -93,12 +93,12 @@ class TestSaleOrderCreditLimit(TestSaleCommon): invoice.action_post() # Create a credit note reversing the invoice - self.env['account.move.reversal'].with_company(self.env.company).create( - { - 'move_ids': [Command.set((invoice.id,))], - 'journal_id': invoice.journal_id.id - } - ).reverse_moves() + self.env['account.move.reversal'].with_company(self.env.company).with_context( + active_model="account.move", + active_ids=invoice.ids, + ).create({ + 'journal_id': invoice.journal_id.id, + }).reverse_moves() credit_note = sale_order.invoice_ids[1] credit_note.action_post()