diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 755f0d1190c..33b2ac9146d 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2401,6 +2401,13 @@ class AccountMove(models.Model): del vals['invoice_line_ids'] return vals + def _stolen_move(self, vals): + for command in vals.get('line_ids', ()): + if command[0] == Command.LINK: + yield self.env['account.move.line'].browse(command[1]).move_id.id + if command[0] == Command.SET: + yield from self.env['account.move.line'].browse(command[2]).move_id.ids + @api.model_create_multi def create(self, vals_list): if any('state' in vals and vals.get('state') == 'posted' for vals in vals_list): @@ -2408,8 +2415,11 @@ class AccountMove(models.Model): container = {'records': self} with self._check_balanced(container): with self._sync_dynamic_lines(container): - moves = super().create([self._sanitize_vals(vals) for vals in vals_list]) - container['records'] = moves + for vals in vals_list: + self._sanitize_vals(vals) + stolen_moves = self.browse(set(move for vals in vals_list for move in self._stolen_move(vals))) + moves = super().create(vals_list) + container['records'] = moves | stolen_moves for move, vals in zip(moves, vals_list): if 'tax_totals' in vals: move.tax_totals = vals['tax_totals'] @@ -2452,7 +2462,8 @@ class AccountMove(models.Model): field = self._fields[fname] if field.compute and not field.readonly: to_protect.append(field) - container = {'records': self} + stolen_moves = self.browse(set(move for move in self._stolen_move(vals))) + container = {'records': self | stolen_moves} with self.env.protecting(to_protect, self), self._check_balanced(container): with self._sync_dynamic_lines(container): res = super(AccountMove, self.with_context( diff --git a/addons/account/tests/test_account_move_entry.py b/addons/account/tests/test_account_move_entry.py index 7034c11b967..fb3b8d6e7ad 100644 --- a/addons/account/tests/test_account_move_entry.py +++ b/addons/account/tests/test_account_move_entry.py @@ -1130,3 +1130,33 @@ class TestAccountMove(AccountTestInvoicingCommon): [("account_id", "=", account.id)], ["balance:sum"], ["account_root_id"] )[0]["balance"] self.assertEqual(balance, 500) + + def test_line_steal(self): + honest_move = self.env['account.move'].create({ + 'line_ids': [ + Command.create({ + 'name': 'receivable', + 'account_id': self.company_data['default_account_receivable'].id, + 'balance': 500.0, + }), + Command.create({ + 'name': 'tax', + 'account_id': self.company_data['default_account_tax_sale'].id, + 'balance': -500.0, + }), + ] + }) + honest_move.action_post() + + with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint(): + self.env['account.move'].create({'line_ids': [Command.set(honest_move.line_ids[0].ids)]}) + + with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint(): + self.env['account.move'].create({'line_ids': [Command.link(honest_move.line_ids[0].id)]}) + + stealer_move = self.env['account.move'].create({}) + with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint(): + stealer_move.write({'line_ids': [Command.set(honest_move.line_ids[0].ids)]}) + + with self.assertRaisesRegex(UserError, 'not balanced'), self.env.cr.savepoint(): + stealer_move.write({'line_ids': [Command.link(honest_move.line_ids[0].id)]})