From 355dea8b83b6d70451b07fb8cb7f5d2c604dc1dd Mon Sep 17 00:00:00 2001 From: william-andre Date: Tue, 28 Mar 2023 12:02:04 +0000 Subject: [PATCH] [FIX] account: protect currency recompute without blocking completely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix[^1] was preventing the recomputation of the currency as soon as there was one set. For instance, the currency wouldn't change if we changed the journal for one with another currency. [^1]: 7f7743470ac1309d5e047563d06362e54edb552a closes odoo/odoo#125671 X-original-commit: 9f8bd34818414698f9b3fd9fe926659781c13c67 Signed-off-by: John Laterre (jol) Signed-off-by: William André (wan) --- addons/account/models/account_move.py | 59 ++++++++++++------- .../tests/test_account_move_out_invoice.py | 40 ++++++++++++- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 0841eed48fe..9c9fc1fbb85 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -142,7 +142,7 @@ class AccountMove(models.Model): journal_id = fields.Many2one( 'account.journal', string='Journal', - compute='_compute_journal_id', store=True, readonly=False, precompute=True, + compute='_compute_journal_id', inverse='_inverse_journal_id', store=True, readonly=False, precompute=True, required=True, states={'draft': [('readonly', False)]}, check_company=True, @@ -660,10 +660,6 @@ class AccountMove(models.Model): def _compute_journal_id(self): for record in self.filtered(lambda r: r.journal_id.type not in r._get_valid_journal_types()): record.journal_id = record._search_default_journal() - if not record.company_id or record.company_id != record.journal_id.company_id: - self.env.add_to_compute(self._fields['company_id'], record) - if not record.currency_id or record.journal_id.currency_id and record.currency_id != record.journal_id.currency_id: - self.env.add_to_compute(self._fields['currency_id'], record) def _get_valid_journal_types(self): if self.is_sale_document(include_receipts=True): @@ -687,10 +683,13 @@ class AccountMove(models.Model): domain = [('company_id', '=', company_id), ('type', 'in', journal_types)] journal = None - currency_id = self.currency_id.id or self._context.get('default_currency_id') - if currency_id and currency_id != self.company_id.currency_id.id: - currency_domain = domain + [('currency_id', '=', currency_id)] - journal = self.env['account.journal'].search(currency_domain, limit=1) + # the currency is not a hard dependence, it triggers via manual add_to_compute + # avoid computing the currency before all it's dependences are set (like the journal...) + if self.env.cache.contains(self, self._fields['currency_id']): + currency_id = self.currency_id.id or self._context.get('default_currency_id') + if currency_id and currency_id != self.company_id.currency_id.id: + currency_domain = domain + [('currency_id', '=', currency_id)] + journal = self.env['account.journal'].search(currency_domain, limit=1) if not journal: journal = self.env['account.journal'].search(domain, limit=1) @@ -847,8 +846,7 @@ class AccountMove(models.Model): def _compute_currency_id(self): for invoice in self: currency = ( - invoice.currency_id - or invoice.statement_line_id.foreign_currency_id + invoice.statement_line_id.foreign_currency_id or invoice.journal_id.currency_id or invoice.journal_id.company_id.currency_id ) @@ -1589,15 +1587,27 @@ class AccountMove(models.Model): @api.onchange('company_id') def _inverse_company_id(self): - for move in self: - if move.journal_id.company_id != move.company_id: - self.env.add_to_compute(self._fields['journal_id'], move) + self._conditional_add_to_compute('journal_id', lambda m: ( + m.journal_id.company_id != m.company_id + )) @api.onchange('currency_id') def _inverse_currency_id(self): - for invoice in self: - if invoice.journal_id.currency_id and invoice.journal_id.currency_id != invoice.currency_id: - self.env.add_to_compute(self._fields['journal_id'], invoice) + self._conditional_add_to_compute('journal_id', lambda m: ( + m.journal_id.currency_id + and m.journal_id.currency_id != m.currency_id + )) + + @api.onchange('journal_id') + def _inverse_journal_id(self): + self._conditional_add_to_compute('company_id', lambda m: ( + not m.company_id + or m.company_id != m.journal_id.company_id + )) + self._conditional_add_to_compute('currency_id', lambda m: ( + not m.currency_id + or m.journal_id.currency_id and m.currency_id != m.journal_id.currency_id + )) def _inverse_payment_reference(self): self.line_ids._conditional_add_to_compute('name', lambda line: ( @@ -2313,8 +2323,13 @@ class AccountMove(models.Model): raise UserError(_('The Journal Entry sequence is not conform to the current format. Only the Accountant can change it.')) move.journal_id.sequence_override_regex = False + to_protect = [] + for fname in vals: + field = self._fields[fname] + if field.compute and not field.readonly: + to_protect.append(field) container = {'records': self} - with self._check_balanced(container): + with self.env.protecting(to_protect, self), self._check_balanced(container): with self._sync_dynamic_lines(container): res = super(AccountMove, self.with_context( skip_account_move_synchronization=True, @@ -2377,7 +2392,10 @@ class AccountMove(models.Model): # del values[to_del] # KeyError: 'line_ids' values.pop(to_del, None) - return super().onchange(values, field_name, field_onchange) + if field_name and not isinstance(field_name, list): + field_name = [field_name] + with self.env.protecting([self._fields[fname] for fname in field_name or []], self): + return super().onchange(values, field_name, field_onchange) # ------------------------------------------------------------------------- # RECONCILIATION METHODS @@ -4352,7 +4370,8 @@ class AccountMove(models.Model): field = self._fields[fname] to_reset = self.filtered(lambda move: condition(move) - and not self.env.is_protected(field, move) + and not self.env.is_protected(field, move._origin) + and (move._origin or not move[fname]) ) to_reset.invalidate_recordset([fname]) self.env.add_to_compute(field, to_reset) diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index dec0096eb36..e6f25fd6d4f 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -3438,15 +3438,51 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): self.assertEqual(move.currency_id, self.currency_data['currency']) self.assertRecordValues(move.line_ids, [ { + 'display_type': 'product', + 'currency_id': self.currency_data['currency'].id, 'debit': 0.0, - 'credit': self.currency_data['currency']._convert(750.0, self.company_data['currency'], self.env.company, fields.Date.today()), + 'credit': 375.0, }, { - 'debit': self.currency_data['currency']._convert(750.0, self.company_data['currency'], self.env.company, fields.Date.today()), + 'display_type': 'payment_term', + 'currency_id': self.currency_data['currency'].id, + 'debit': 375.0, 'credit': 0.0, }, ]) + move.currency_id = self.company_data['currency'] + with Form(move) as move_form: + move_form.currency_id = self.currency_data['currency'] + self.assertEqual(move.currency_id, self.currency_data['currency']) + + with Form(self.env['account.move'].with_context(default_move_type='out_invoice')) as move_form: + move_form.currency_id = self.currency_data['currency'] + self.assertEqual(move_form.currency_id, self.currency_data['currency']) + + def test_change_journal_currency(self): + second_journal = self.company_data['default_journal_sale'].copy({ + 'currency_id': self.currency_data['currency'].id, + }) + move = self.env['account.move'].create({ + 'move_type': 'out_invoice', + 'partner_id': self.partner_a.id, + 'journal_id': self.company_data['default_journal_sale'].id, + 'invoice_line_ids': [ + Command.create({ + 'name': 'My super product.', + 'quantity': 1.0, + 'price_unit': 750.0, + 'account_id': self.product_a.property_account_income_id.id, + 'tax_ids': False, + }) + ], + }) + + self.assertEqual(move.currency_id, self.company_data['currency']) + move.journal_id = second_journal + self.assertEqual(move.currency_id, self.currency_data['currency']) + @freeze_time('2019-01-01') def test_date_reversal_exchange_move(self): """