diff options
| author | Raphael Collet <rco@odoo.com> | 2020-12-21 08:45:51 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2020-12-21 12:12:53 +0000 |
| commit | 45422d56bce413b8577f1784e10dd22ede93c751 (patch) | |
| tree | 8cbf3aea81c9a615cd54d51b602ade1bfda428d6 /odoo | |
| parent | 61f8ced67992b20edfac139c749ec00939f408a3 (diff) | |
[FIX] core: first onchange should always compute fields
When a form view is opened, the first call to `onchange` should always
compute fields, even if their dependencies have no default value. Make
sure it is the case for main records, and for records inside one2many
fields. The latter case was actually not working as expected.
closes odoo/odoo#63627
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Diffstat (limited to 'odoo')
| -rw-r--r-- | odoo/addons/test_new_api/models/test_new_api.py | 13 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_onchange.py | 124 | ||||
| -rw-r--r-- | odoo/models.py | 7 |
3 files changed, 85 insertions, 59 deletions
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 2cb28702520..e8c868f212f 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -556,13 +556,13 @@ class ComputeOnchange(models.Model): @api.depends('foo') def _compute_bar(self): for record in self: - record.bar = record.foo + record.bar = (record.foo or "") + "r" @api.depends('active', 'foo') def _compute_baz(self): for record in self: if record.active: - record.baz = record.foo + record.baz = (record.foo or "") + "z" @api.depends('foo') def _compute_line_ids(self): @@ -590,9 +590,14 @@ class ComputeOnchangeLine(models.Model): _name = 'test_new_api.compute.onchange.line' _description = "Line-like model for test_new_api.compute.onchange" + record_id = fields.Many2one('test_new_api.compute.onchange', ondelete='cascade') foo = fields.Char() - record_id = fields.Many2one('test_new_api.compute.onchange', - required=True, ondelete='cascade') + bar = fields.Char(compute='_compute_bar') + + @api.depends('foo') + def _compute_bar(self): + for line in self: + line.bar = (line.foo or "") + "r" class ComputeDynamicDepends(models.Model): diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 1b424777bc0..a56348cf050 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -542,24 +542,29 @@ class TestComputeOnchange(common.TransactionCase): def test_create(self): model = self.env['test_new_api.compute.onchange'] + # compute 'bar' (readonly) and 'baz' (editable) + record = model.create({'active': True}) + self.assertEqual(record.bar, "r") + self.assertEqual(record.baz, "z") + # compute 'bar' and 'baz' record = model.create({'active': True, 'foo': "foo"}) - self.assertEqual(record.bar, "foo") - self.assertEqual(record.baz, "foo") + self.assertEqual(record.bar, "foor") + self.assertEqual(record.baz, "fooz") # compute 'bar' but not 'baz' record = model.create({'active': True, 'foo': "foo", 'bar': "bar", 'baz': "baz"}) - self.assertEqual(record.bar, "foo") + self.assertEqual(record.bar, "foor") self.assertEqual(record.baz, "baz") # compute 'bar' and 'baz', but do not change its value record = model.create({'active': False, 'foo': "foo"}) - self.assertEqual(record.bar, "foo") + self.assertEqual(record.bar, "foor") self.assertEqual(record.baz, False) # compute 'bar' but not 'baz' record = model.create({'active': False, 'foo': "foo", 'bar': "bar", 'baz': "baz"}) - self.assertEqual(record.bar, "foo") + self.assertEqual(record.bar, "foor") self.assertEqual(record.baz, "baz") def test_copy(self): @@ -571,10 +576,10 @@ class TestComputeOnchange(common.TransactionCase): {'name': 'bar1'}, ]) - # compute 'bar', 'baz', 'line_ids' and 'tag_ids' + # compute 'bar' (readonly), 'baz', 'line_ids' and 'tag_ids' (editable) record = Model.create({'active': True, 'foo': "foo1"}) - self.assertEqual(record.bar, "foo1") - self.assertEqual(record.baz, "foo1") + self.assertEqual(record.bar, "foo1r") + self.assertEqual(record.baz, "foo1z") self.assertEqual(record.line_ids.mapped('foo'), ['foo1']) self.assertEqual(record.tag_ids, tag_foo) @@ -584,7 +589,7 @@ class TestComputeOnchange(common.TransactionCase): 'line_ids': [(0, 0, {'foo': 'bar'})], 'tag_ids': [(4, tag_bar.id)], }) - self.assertEqual(record.bar, "foo1") + self.assertEqual(record.bar, "foo1r") self.assertEqual(record.baz, "baz1") self.assertEqual(record.line_ids.mapped('foo'), ['foo1', 'bar']) self.assertEqual(record.tag_ids, tag_foo + tag_bar) @@ -592,7 +597,7 @@ class TestComputeOnchange(common.TransactionCase): # copy the record, and check results copied = record.copy() self.assertEqual(copied.foo, "foo1 (copy)") # copied and modified - self.assertEqual(copied.bar, "foo1 (copy)") # computed + self.assertEqual(copied.bar, "foo1 (copy)r") # computed self.assertEqual(copied.baz, "baz1") # copied self.assertEqual(record.line_ids.mapped('foo'), ['foo1', 'bar']) # copied self.assertEqual(record.tag_ids, tag_foo + tag_bar) # copied @@ -600,128 +605,141 @@ class TestComputeOnchange(common.TransactionCase): def test_write(self): model = self.env['test_new_api.compute.onchange'] record = model.create({'active': True, 'foo': "foo"}) - self.assertEqual(record.bar, "foo") - self.assertEqual(record.baz, "foo") + self.assertEqual(record.bar, "foor") + self.assertEqual(record.baz, "fooz") - # recompute 'bar' and 'baz' + # recompute 'bar' (readonly) and 'baz' (editable) record.write({'foo': "foo1"}) - self.assertEqual(record.bar, "foo1") - self.assertEqual(record.baz, "foo1") + self.assertEqual(record.bar, "foo1r") + self.assertEqual(record.baz, "foo1z") # recompute 'bar' but not 'baz' record.write({'foo': "foo2", 'bar': "bar2", 'baz': "baz2"}) - self.assertEqual(record.bar, "foo2") + self.assertEqual(record.bar, "foo2r") self.assertEqual(record.baz, "baz2") # recompute 'bar' and 'baz', but do not change its value record.write({'active': False, 'foo': "foo3"}) - self.assertEqual(record.bar, "foo3") + self.assertEqual(record.bar, "foo3r") self.assertEqual(record.baz, "baz2") # recompute 'bar' but not 'baz' record.write({'active': False, 'foo': "foo4", 'bar': "bar4", 'baz': "baz4"}) - self.assertEqual(record.bar, "foo4") + self.assertEqual(record.bar, "foo4r") self.assertEqual(record.baz, "baz4") def test_set(self): model = self.env['test_new_api.compute.onchange'] record = model.create({'active': True, 'foo': "foo"}) - self.assertEqual(record.bar, "foo") - self.assertEqual(record.baz, "foo") + self.assertEqual(record.bar, "foor") + self.assertEqual(record.baz, "fooz") - # recompute 'bar' and 'baz' + # recompute 'bar' (readonly) and 'baz' (editable) record.foo = "foo1" - self.assertEqual(record.bar, "foo1") - self.assertEqual(record.baz, "foo1") + self.assertEqual(record.bar, "foo1r") + self.assertEqual(record.baz, "foo1z") # do not recompute 'baz' record.baz = "baz2" - self.assertEqual(record.bar, "foo1") + self.assertEqual(record.bar, "foo1r") self.assertEqual(record.baz, "baz2") # recompute 'baz', but do not change its value record.active = False - self.assertEqual(record.bar, "foo1") + self.assertEqual(record.bar, "foo1r") self.assertEqual(record.baz, "baz2") # recompute 'baz', but do not change its value record.foo = "foo3" - self.assertEqual(record.bar, "foo3") + self.assertEqual(record.bar, "foo3r") self.assertEqual(record.baz, "baz2") # do not recompute 'baz' record.baz = "baz4" - self.assertEqual(record.bar, "foo3") + self.assertEqual(record.bar, "foo3r") self.assertEqual(record.baz, "baz4") def test_set_new(self): model = self.env['test_new_api.compute.onchange'] - record = model.new({'active': True, 'foo': "foo"}) - self.assertEqual(record.bar, "foo") - self.assertEqual(record.baz, "foo") + record = model.new({'active': True}) + self.assertEqual(record.bar, "r") + self.assertEqual(record.baz, "z") - # recompute 'bar' and 'baz' + # recompute 'bar' (readonly) and 'baz' (editable) record.foo = "foo1" - self.assertEqual(record.bar, "foo1") - self.assertEqual(record.baz, "foo1") + self.assertEqual(record.bar, "foo1r") + self.assertEqual(record.baz, "foo1z") # do not recompute 'baz' record.baz = "baz2" - self.assertEqual(record.bar, "foo1") + self.assertEqual(record.bar, "foo1r") self.assertEqual(record.baz, "baz2") # recompute 'baz', but do not change its value record.active = False - self.assertEqual(record.bar, "foo1") + self.assertEqual(record.bar, "foo1r") self.assertEqual(record.baz, "baz2") # recompute 'baz', but do not change its value record.foo = "foo3" - self.assertEqual(record.bar, "foo3") + self.assertEqual(record.bar, "foo3r") self.assertEqual(record.baz, "baz2") # do not recompute 'baz' record.baz = "baz4" - self.assertEqual(record.bar, "foo3") + self.assertEqual(record.bar, "foo3r") self.assertEqual(record.baz, "baz4") def test_onchange(self): + # check computations of 'bar' (readonly) and 'baz' (editable) form = common.Form(self.env['test_new_api.compute.onchange']) + self.assertEqual(form.bar, "r") + self.assertEqual(form.baz, False) form.active = True + self.assertEqual(form.bar, "r") + self.assertEqual(form.baz, "z") form.foo = "foo1" - self.assertEqual(form.bar, "foo1") - self.assertEqual(form.baz, "foo1") + self.assertEqual(form.bar, "foo1r") + self.assertEqual(form.baz, "foo1z") form.baz = "baz2" - self.assertEqual(form.bar, "foo1") + self.assertEqual(form.bar, "foo1r") self.assertEqual(form.baz, "baz2") form.active = False - self.assertEqual(form.bar, "foo1") + self.assertEqual(form.bar, "foo1r") self.assertEqual(form.baz, "baz2") form.foo = "foo3" - self.assertEqual(form.bar, "foo3") + self.assertEqual(form.bar, "foo3r") self.assertEqual(form.baz, "baz2") form.active = True - self.assertEqual(form.bar, "foo3") - self.assertEqual(form.baz, "foo3") + self.assertEqual(form.bar, "foo3r") + self.assertEqual(form.baz, "foo3z") + + with form.line_ids.new() as line: + # check computation of 'bar' (readonly) + self.assertEqual(line.foo, False) + self.assertEqual(line.bar, "r") + line.foo = "foo" + self.assertEqual(line.foo, "foo") + self.assertEqual(line.bar, "foor") record = form.save() - self.assertEqual(record.bar, "foo3") - self.assertEqual(record.baz, "foo3") + self.assertEqual(record.bar, "foo3r") + self.assertEqual(record.baz, "foo3z") form = common.Form(record) - self.assertEqual(form.bar, "foo3") - self.assertEqual(form.baz, "foo3") + self.assertEqual(form.bar, "foo3r") + self.assertEqual(form.baz, "foo3z") form.foo = "foo4" - self.assertEqual(form.bar, "foo4") - self.assertEqual(form.baz, "foo4") + self.assertEqual(form.bar, "foo4r") + self.assertEqual(form.baz, "foo4z") form.baz = "baz5" - self.assertEqual(form.bar, "foo4") + self.assertEqual(form.bar, "foo4r") self.assertEqual(form.baz, "baz5") form.active = False - self.assertEqual(form.bar, "foo4") + self.assertEqual(form.bar, "foo4r") self.assertEqual(form.baz, "baz5") form.foo = "foo6" - self.assertEqual(form.bar, "foo6") + self.assertEqual(form.bar, "foo6r") self.assertEqual(form.baz, "baz5") def test_onchange_one2many(self): diff --git a/odoo/models.py b/odoo/models.py index 80cd18e3be4..8993fbc9de3 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -6159,8 +6159,11 @@ Fields: for name in names: snapshot0.fetch(name) - # determine which field(s) should be triggered an onchange - todo = list(names or nametree) + # Determine which field(s) should be triggered an onchange. On the first + # call, 'names' only contains fields with a default. If 'self' is a new + # line in a one2many field, 'names' also contains the one2many's inverse + # field, and that field may not be in nametree. + todo = list(names) + list(nametree) if first_call else list(names) done = set() # dummy assignment: trigger invalidations on the record |
