summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2020-12-21 08:45:51 +0000
committerRaphael Collet <rco@odoo.com>2020-12-21 12:12:53 +0000
commit45422d56bce413b8577f1784e10dd22ede93c751 (patch)
tree8cbf3aea81c9a615cd54d51b602ade1bfda428d6
parent61f8ced67992b20edfac139c749ec00939f408a3 (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>
-rw-r--r--odoo/addons/test_new_api/models/test_new_api.py13
-rw-r--r--odoo/addons/test_new_api/tests/test_onchange.py124
-rw-r--r--odoo/models.py7
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