From 243a2e8d03f3a97ce3a416f971a38b0fe5db7bbb Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 12 Jun 2023 12:46:55 +0000 Subject: [PATCH] [FIX] web: make onchange2() apply x2many commands on existing record values When doing an onchange on some existing record, the x2many commands must be applied on the current x2many value of the record. When doing an onchange on a new record, the x2many commands must be applied on empty x2many values. Part-of: odoo/odoo#124612 --- addons/web/models/models.py | 16 ++++++++----- .../test_new_api/tests/test_onchange2.py | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/addons/web/models/models.py b/addons/web/models/models.py index 4d3c2e11daa..997d06dbcfd 100644 --- a/addons/web/models/models.py +++ b/addons/web/models/models.py @@ -983,10 +983,8 @@ class Base(models.AbstractModel): # values of the move, among which the one2many that contains the line # itself, with old values! # - changed_values = {fname: values[fname] for fname in field_names} - # set changed values to null in initial_values; not setting them - # triggers default_get() on the new record when creating snapshot0 - initial_values = dict(values, **dict.fromkeys(field_names, False)) + initial_values = dict(values) + changed_values = {fname: initial_values.pop(fname) for fname in field_names} # do not force delegate fields to False for parent_name in self._inherits.values(): @@ -999,7 +997,13 @@ class Base(models.AbstractModel): # fill in the cache of record with the values of self cache_values = {fname: self[fname] for fname in fields_spec} record._update_cache(cache_values, validate=False) - record._update_cache(initial_values) + # apply initial values on top of the values of self + record._update_cache(initial_values) + else: + # set changed values to null in initial_values; not setting them + # triggers default_get() on the new record when creating snapshot0 + initial_values.update(dict.fromkeys(field_names, False)) + record._update_cache(initial_values, validate=False) # make parent records match with the form values; this ensures that # computed fields on parent records have all their dependencies at @@ -1016,7 +1020,7 @@ class Base(models.AbstractModel): # 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=False) + record._update_cache(changed_values) # update snapshot0 with changed values for field_name in field_names: diff --git a/odoo/addons/test_new_api/tests/test_onchange2.py b/odoo/addons/test_new_api/tests/test_onchange2.py index b3db8ef59a7..b948d8c2011 100644 --- a/odoo/addons/test_new_api/tests/test_onchange2.py +++ b/odoo/addons/test_new_api/tests/test_onchange2.py @@ -353,6 +353,29 @@ class TestOnchange2(SavepointCaseWithUserDemo): result = multi.onchange2(values, ['partner'], fields_spec) self.assertEqual(result['value'], expected_value) + def test_onchange_one2many_default(self): + """ test onchange on x2many field with default value in context """ + default_messages = [Command.create({'body': 'A'})] + model = self.Discussion.with_context(default_messages=default_messages) + + # the command CREATE below must be applied on the empty value, instead + # of the default value above + values = { + 'name': 'Stuff', + 'messages': [(Command.CREATE, 'virtual1', {'name': '[] ', 'body': 'B'})], + } + fields_spec = { + 'name': {}, + 'messages': {'fields': { + 'name': {}, + 'body': {}, + }}, + } + result = model.onchange2(values, ['name'], fields_spec) + self.assertEqual(result['value'], { + 'messages': [Command.update('virtual1', {'name': '[Stuff] OdooBot'})], + }) + def test_fields_specific(self): """ test the effect of field-specific onchange method """ discussion = self.env.ref('test_new_api.discussion_0')