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 6cb379478cd..b55129e6ac5 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -457,12 +457,27 @@ class Move(models.Model): quantity = fields.Integer(compute='_compute_quantity', store=True) tag_id = fields.Many2one('test_new_api.multi.tag') tag_name = fields.Char(related='tag_id.name') + tag_repeat = fields.Integer() + tag_string = fields.Char(compute='_compute_tag_string') + + # This field can fool the ORM during onchanges! When editing a payment + # record, modified fields are assigned to the parent record. When + # determining the dependent records, the ORM looks for the payments related + # to this record by the field `move_id`. As this field is an inverse of + # `move_id`, it uses it. If that field was not initialized properly, the + # ORM determines its value to be... empty (instead of the payment record.) + payment_ids = fields.One2many('test_new_api.payment', 'move_id') @api.depends('line_ids.quantity') def _compute_quantity(self): for record in self: record.quantity = sum(line.quantity for line in record.line_ids) + @api.depends('tag_name', 'tag_repeat') + def _compute_tag_string(self): + for record in self: + record.tag_string = (record.tag_name or "") * record.tag_repeat + class MoveLine(models.Model): _name = 'test_new_api.move_line' diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 424a88c71f9..0d5b94495b0 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -548,6 +548,8 @@ class TestOnChange(SavepointCaseWithUserDemo): + + """, }) @@ -556,19 +558,32 @@ class TestOnChange(SavepointCaseWithUserDemo): # assigning 'tag_id' should modify 'move_id.tag_id' accordingly, which # should in turn recompute `move.tag_name` and `tag_name` form = Form(self.env['test_new_api.payment'], view) + self.assertEqual(form.tag_name, False) form.tag_id = foo self.assertEqual(form.tag_name, 'Foo') + self.assertEqual(form.tag_string, '') + form.tag_repeat = 2 + self.assertEqual(form.tag_name, 'Foo') + self.assertEqual(form.tag_string, 'FooFoo') payment = form.save() self.assertEqual(payment.tag_id, foo) self.assertEqual(payment.tag_name, 'Foo') + self.assertEqual(payment.tag_repeat, 2) + self.assertEqual(payment.tag_string, 'FooFoo') with Form(payment, view) as form: form.tag_id = bar self.assertEqual(form.tag_name, 'Bar') + self.assertEqual(form.tag_string, 'BarBar') + form.tag_repeat = 3 + self.assertEqual(form.tag_name, 'Bar') + self.assertEqual(form.tag_string, 'BarBarBar') self.assertEqual(payment.tag_id, bar) self.assertEqual(payment.tag_name, 'Bar') + self.assertEqual(payment.tag_repeat, 3) + self.assertEqual(payment.tag_string, 'BarBarBar') class TestComputeOnchange(common.TransactionCase): diff --git a/odoo/fields.py b/odoo/fields.py index f916a741b19..1ba2fb16d66 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -1122,8 +1122,10 @@ class Field(MetaField('DummyField', (object,), {})): for name, value in record._cache.items() if is_inherited_field(name) }) - value = self.convert_to_cache(parent, record) - env.cache.set(record, self, value) + # in case the delegate field has inverse one2many fields, this + # updates the inverse fields as well + record._update_cache({self.name: parent}, validate=False) + value = env.cache.get(record, self) else: # non-stored field or stored field on new record: default value diff --git a/odoo/models.py b/odoo/models.py index dda5837c306..8c519fdc52f 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -6395,13 +6395,22 @@ Fields: initial_values = dict(values, **dict.fromkeys(names, False)) # do not force delegate fields to False - for name in self._inherits.values(): - if not initial_values.get(name, True): - initial_values.pop(name) + for parent_name in self._inherits.values(): + if not initial_values.get(parent_name, True): + initial_values.pop(parent_name) # create a new record with values record = self.new(initial_values, origin=self) + # make parent records match with the form values; this ensures that + # computed fields on parent records have all their dependencies at + # their expected value + for name in initial_values: + field = self._fields.get(name) + if field and field.inherited: + parent_name, name = field.related.split('.', 1) + record[parent_name]._update_cache({name: record[name]}) + # make a snapshot based on the initial values of record snapshot0 = Snapshot(record, nametree, fetch=(not first_call))