summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-08-19 11:51:16 +0000
committerRaphael Collet <rco@odoo.com>2019-09-03 12:02:45 +0000
commitcb2117a4876ae1077e7cd26436cb4a40cb490a0a (patch)
tree4bc5a8791b3b6d1426157f53181798c25ac7cd4d
parent0518b23127a5997d286054129b942337bb1f089e (diff)
[FIX] models: compute-onchange
-rw-r--r--odoo/addons/test_new_api/ir.model.access.csv1
-rw-r--r--odoo/addons/test_new_api/models.py25
-rw-r--r--odoo/addons/test_new_api/tests/test_onchange.py153
-rw-r--r--odoo/models.py14
4 files changed, 182 insertions, 11 deletions
diff --git a/odoo/addons/test_new_api/ir.model.access.csv b/odoo/addons/test_new_api/ir.model.access.csv
index 66d10310f43..5a11d121179 100644
--- a/odoo/addons/test_new_api/ir.model.access.csv
+++ b/odoo/addons/test_new_api/ir.model.access.csv
@@ -23,6 +23,7 @@ access_test_new_api_compute_protected,access_test_new_api_compute_protected,mode
access_test_new_api_multi_compute_inverse,access_test_new_api_multi_compute_inverse,model_test_new_api_multi_compute_inverse,,1,1,1,1
access_test_new_api_recursive,access_test_new_api_recursive,model_test_new_api_recursive,,1,1,1,1
access_test_new_api_cascade,access_test_new_api_cascade,model_test_new_api_cascade,,1,1,1,1
+access_test_new_api_compute_onchange,access_test_new_api_compute_onchange,model_test_new_api_compute_onchange,,1,1,1,1
access_test_new_api_binary_svg,access_test_new_api_binary_svg,model_test_new_api_binary_svg,,1,1,1,1
access_test_new_api_monetary_base,access_test_new_api_monetary_base,model_test_new_api_monetary_base,,1,1,1,1
access_test_new_api_monetary_related,access_test_new_api_monetary_related,model_test_new_api_monetary_related,,1,1,1,1
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index 18bcbdb8c51..aa97b9f2281 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -373,8 +373,8 @@ class Related(models.Model):
related_related_name = fields.Char(related='related_name', string='A related on a related on Name', readonly=False)
message = fields.Many2one('test_new_api.message')
- message_name = fields.Text(related="message.body", related_sudo=False, string='Message Body', readonly=False)
- message_currency = fields.Many2one(related="message.author", string='Message Author', readonly=False)
+ message_name = fields.Text(related="message.body", related_sudo=False, string='Message Body')
+ message_currency = fields.Many2one(related="message.author", string='Message Author')
class ComputeProtected(models.Model):
_name = 'test_new_api.compute.protected'
@@ -495,6 +495,27 @@ class ComputeCascade(models.Model):
record.baz = "<%s>" % (record.bar or "")
+class ComputeOnchange(models.Model):
+ _name = 'test_new_api.compute.onchange'
+ _description = "Compute method as an onchange"
+
+ active = fields.Boolean()
+ foo = fields.Char()
+ bar = fields.Char(compute='_compute_bar', store=True)
+ baz = fields.Char(compute='_compute_baz', store=True, readonly=False)
+
+ @api.depends('foo')
+ def _compute_bar(self):
+ for record in self:
+ record.bar = record.foo
+
+ @api.depends('active', 'foo')
+ def _compute_baz(self):
+ for record in self:
+ if record.active:
+ record.baz = record.foo
+
+
class ModelImage(models.Model):
_name = 'test_new_api.model_image'
_description = 'Test Image field'
diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py
index b3a765ed7e9..2359acd78fe 100644
--- a/odoo/addons/test_new_api/tests/test_onchange.py
+++ b/odoo/addons/test_new_api/tests/test_onchange.py
@@ -522,3 +522,156 @@ class TestOnChange(common.TransactionCase):
self.Message.onchange(values, 'discussion', field_onchange)
self.assertFalse(called[0], "discussion.messages has been read")
+
+
+class TestComputeOnchange(common.TransactionCase):
+
+ def test_create(self):
+ model = self.env['test_new_api.compute.onchange']
+
+ # compute 'bar' and 'baz'
+ record = model.create({'active': True, 'foo': "foo"})
+ self.assertEqual(record.bar, "foo")
+ self.assertEqual(record.baz, "foo")
+
+ # compute 'bar' but not 'baz'
+ record = model.create({'active': True, 'foo': "foo", 'bar': "bar", 'baz': "baz"})
+ self.assertEqual(record.bar, "foo")
+ 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.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.baz, "baz")
+
+ 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")
+
+ # recompute 'bar' and 'baz'
+ record.write({'foo': "foo1"})
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "foo1")
+
+ # recompute 'bar' but not 'baz'
+ record.write({'foo': "foo2", 'bar': "bar2", 'baz': "baz2"})
+ self.assertEqual(record.bar, "foo2")
+ 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.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.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")
+
+ # recompute 'bar' and 'baz'
+ record.foo = "foo1"
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "foo1")
+
+ # do not recompute 'baz'
+ record.baz = "baz2"
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "baz2")
+
+ # recompute 'baz', but do not change its value
+ record.active = False
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "baz2")
+
+ # recompute 'baz', but do not change its value
+ record.foo = "foo3"
+ self.assertEqual(record.bar, "foo3")
+ self.assertEqual(record.baz, "baz2")
+
+ # do not recompute 'baz'
+ record.baz = "baz4"
+ self.assertEqual(record.bar, "foo3")
+ 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")
+
+ # recompute 'bar' and 'baz'
+ record.foo = "foo1"
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "foo1")
+
+ # do not recompute 'baz'
+ record.baz = "baz2"
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "baz2")
+
+ # recompute 'baz', but do not change its value
+ record.active = False
+ self.assertEqual(record.bar, "foo1")
+ self.assertEqual(record.baz, "baz2")
+
+ # recompute 'baz', but do not change its value
+ record.foo = "foo3"
+ self.assertEqual(record.bar, "foo3")
+ self.assertEqual(record.baz, "baz2")
+
+ # do not recompute 'baz'
+ record.baz = "baz4"
+ self.assertEqual(record.bar, "foo3")
+ self.assertEqual(record.baz, "baz4")
+
+ def test_onchange(self):
+ form = common.Form(self.env['test_new_api.compute.onchange'])
+ form.active = True
+ form.foo = "foo1"
+ self.assertEqual(form.bar, "foo1")
+ self.assertEqual(form.baz, "foo1")
+ form.baz = "baz2"
+ self.assertEqual(form.bar, "foo1")
+ self.assertEqual(form.baz, "baz2")
+ form.active = False
+ self.assertEqual(form.bar, "foo1")
+ self.assertEqual(form.baz, "baz2")
+ form.foo = "foo3"
+ self.assertEqual(form.bar, "foo3")
+ self.assertEqual(form.baz, "baz2")
+ form.active = True
+ self.assertEqual(form.bar, "foo3")
+ self.assertEqual(form.baz, "foo3")
+
+ record = form.save()
+ self.assertEqual(record.bar, "foo3")
+ self.assertEqual(record.baz, "foo3")
+
+ form = common.Form(record)
+ self.assertEqual(form.bar, "foo3")
+ self.assertEqual(form.baz, "foo3")
+ form.foo = "foo4"
+ self.assertEqual(form.bar, "foo4")
+ self.assertEqual(form.baz, "foo4")
+ form.baz = "baz5"
+ self.assertEqual(form.bar, "foo4")
+ self.assertEqual(form.baz, "baz5")
+ form.active = False
+ self.assertEqual(form.bar, "foo4")
+ self.assertEqual(form.baz, "baz5")
+ form.foo = "foo6"
+ self.assertEqual(form.bar, "foo6")
+ self.assertEqual(form.baz, "baz5")
diff --git a/odoo/models.py b/odoo/models.py
index 2914fc93685..f46b1a560f2 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3315,7 +3315,8 @@ Fields:
records_to_inverse[field] = self.filtered('id')
if field.relational or self._field_inverses[field]:
relational_names.append(fname)
- protected.update(self._field_computed.get(field, [field]))
+ if field.compute and not field.readonly:
+ protected.update(self._field_computed.get(field, [field]))
# protect fields being written against recomputation
with env.protecting(protected, self):
@@ -3516,11 +3517,8 @@ Fields:
elif field.inverse:
inversed[key] = val
inversed_fields.add(field)
- # ignore the protection of compute fields which do not have an
- # inverse, otherwise their computation are not correctly
- # performed, neither the fields which are computed in the same
- # compute method (`test_validation_error`)
- if not field.compute or field.inverse:
+ # protect non-readonly computed fields against (re)computation
+ if field.compute and not field.readonly:
protected.update(self._field_computed.get(field, [field]))
data_list.append(data)
@@ -5452,9 +5450,7 @@ Fields:
# Dont force the recomputation of compute fields which are
# not stored as this is not really necessary.
if field.compute and field.store:
- records_to_invalidate = records.filtered(lambda r: not r.id)
- self.env.add_to_compute(field, records - records_to_invalidate)
- self.env.cache.invalidate([(field, records_to_invalidate._ids)])
+ self.env.add_to_compute(field, records)
else:
self.env.cache.invalidate([(field, records._ids)])
# recursively trigger recomputation of field's dependents