diff --git a/odoo/addons/test_new_api/models/test_new_api.py b/odoo/addons/test_new_api/models/test_new_api.py index 147cf78188c..3041daeadc0 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -1147,3 +1147,35 @@ class ComputeMember(models.Model): container = self.env['test_new_api.compute.container'] for member in self: member.container_id = container.search([('name', '=', member.name)], limit=1) + + +class ComputeEditable(models.Model): + _name = _description = 'test_new_api.compute_editable' + + line_ids = fields.One2many('test_new_api.compute_editable.line', 'parent_id') + + @api.onchange('line_ids') + def _onchange_line_ids(self): + for line in self.line_ids: + # even if 'same' is not in the view, it should be the same as 'value' + line.count += line.same + + +class ComputeEditableLine(models.Model): + _name = _description = 'test_new_api.compute_editable.line' + + parent_id = fields.Many2one('test_new_api.compute_editable') + value = fields.Integer() + same = fields.Integer(compute='_compute_same', store=True) + edit = fields.Integer(compute='_compute_edit', store=True, readonly=False) + count = fields.Integer() + + @api.depends('value') + def _compute_same(self): + for line in self: + line.same = line.value + + @api.depends('value') + def _compute_edit(self): + for line in self: + line.edit = line.value diff --git a/odoo/addons/test_new_api/security/ir.model.access.csv b/odoo/addons/test_new_api/security/ir.model.access.csv index 71a309d5d1a..021d7127b05 100644 --- a/odoo/addons/test_new_api/security/ir.model.access.csv +++ b/odoo/addons/test_new_api/security/ir.model.access.csv @@ -66,3 +66,5 @@ access_test_new_api_model_shared_cache_compute_parent,access_test_new_api.model_ access_test_new_api_model_shared_cache_compute_line,access_test_new_api.model_shared_cache_compute_line,model_test_new_api_model_shared_cache_compute_line,,1,1,1,1 access_test_new_api_compute_container,access_test_new_api_compute_container,model_test_new_api_compute_container,,1,1,1,1 access_test_new_api_compute_member,access_test_new_api_compute_member,model_test_new_api_compute_member,,1,1,1,1 +access_test_new_api_compute_editable,access_test_new_api_compute_editable,model_test_new_api_compute_editable,,1,1,1,1 +access_test_new_api_compute_editable_line,access_test_new_api_compute_editable_line,model_test_new_api_compute_editable_line,,1,1,1,1 diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 634da243664..76be6c6f9b5 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -1338,7 +1338,7 @@ class TestFields(TransactionCaseWithUserDemo): new_origin = Model.new({'name': 'Bar'}, origin=real_record) new_record = Model.new({'name': 'Baz'}) self.assertEqual(real_record.display_name, 'Foo') - self.assertEqual(new_origin.display_name, 'Foo') + self.assertEqual(new_origin.display_name, 'Bar') self.assertEqual(new_record.display_name, 'Baz') # computed stored field with recomputation: always computed diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 62da8a1a8b0..c417ef9dba5 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -4,7 +4,7 @@ from unittest.mock import patch from odoo.addons.base.tests.common import SavepointCaseWithUserDemo -from odoo.tests import common +from odoo.tests import common, Form from odoo import Command def strip_prefix(prefix, names): @@ -752,3 +752,39 @@ class TestComputeOnchange(common.TransactionCase): with form.child_ids.edit(2) as line: line.cost = 30 self.assertEqual(form.cost, 61) + + def test_onchange_editable_compute_one2many(self): + # create a record with a computed editable field ('edit') on lines + record = self.env['test_new_api.compute_editable'].create({'line_ids': [(0, 0, {'value': 7})]}) + record.flush() + line = record.line_ids + self.assertRecordValues(line, [{'value': 7, 'edit': 7, 'count': 0}]) + + # retrieve the onchange spec for calling 'onchange' + spec = Form(record)._view['onchange'] + + # The onchange on 'line_ids' should increment 'count' and keep the value + # of 'edit' (this field should not be recomputed), whatever the order of + # the fields in the dictionary. This ensures that the value set by the + # user on a computed editable field on a line is not lost. + line_ids = [ + Command.update(line.id, {'value': 8, 'edit': 9, 'count': 0}), + Command.create({'value': 8, 'edit': 9, 'count': 0}), + ] + result = record.onchange({'line_ids': line_ids}, 'line_ids', spec) + expected = {'value': { + 'line_ids': [ + Command.clear(), + Command.update(line.id, {'value': 8, 'edit': 9, 'count': 8}), + Command.create({'value': 8, 'edit': 9, 'count': 8}), + ], + }} + self.assertEqual(result, expected) + + # change dict order in lines, and try again + line_ids = [ + (op, id_, dict(reversed(list(vals.items())))) + for op, id_, vals in line_ids + ] + result = record.onchange({'line_ids': line_ids}, 'line_ids', spec) + self.assertEqual(result, expected) diff --git a/odoo/addons/test_new_api/views/test_new_api_views.xml b/odoo/addons/test_new_api/views/test_new_api_views.xml index 7fbce13b316..56e6a773b28 100644 --- a/odoo/addons/test_new_api/views/test_new_api_views.xml +++ b/odoo/addons/test_new_api/views/test_new_api_views.xml @@ -341,5 +341,31 @@ + + + + test_new_api.compute_editable.form + test_new_api.compute_editable + +
+ + + + + + + + + + + + + + +
+ +
+
+ diff --git a/odoo/fields.py b/odoo/fields.py index 4758b4949fd..e1417bb6017 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -961,7 +961,8 @@ class Field(MetaField('DummyField', (object,), {})): # not stored and not computed -> default # # on a new record w/ origin: - # stored -> fetch from origin (computation done above) + # stored and not (computed and readonly) -> fetch from origin + # stored and computed and readonly -> compute # not stored and computed -> compute # not stored and not computed -> default # @@ -987,7 +988,7 @@ class Field(MetaField('DummyField', (object,), {})): ])) value = env.cache.get(record, self) - elif self.store and record._origin: + elif self.store and record._origin and not (self.compute and self.readonly): # new record with origin: fetch from origin value = self.convert_to_cache(record._origin[self.name], record) env.cache.set(record, self, value) diff --git a/odoo/models.py b/odoo/models.py index c9071c269dd..bfe6f3d09cd 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -6132,7 +6132,7 @@ Fields: # store changed values in cache; also trigger recomputations based on # subfields (e.g., line.a has been modified, line.b is computed stored # and depends on line.a, but line.b is not in the form view) - record._update_cache(changed_values, validate=True) + record._update_cache(changed_values, validate=False) # update snapshot0 with changed values for name in names: