From 126ebbc905af9f7eb5a4887cfb57ef7944bec019 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Aug 2020 08:40:47 +0000 Subject: [PATCH] [IMP] core: error message for computed field left unassigned Non-stored readonly computed fields must be assigned by compute method. Make the error message more explicit. closes odoo/odoo#56269 Signed-off-by: Raphael Collet (rco) --- .../test_new_api/models/test_new_api.py | 27 +++++++++++++++++++ .../test_new_api/security/ir.model.access.csv | 1 + .../test_new_api/tests/test_new_fields.py | 19 +++++++++++++ odoo/fields.py | 4 +-- 4 files changed, 49 insertions(+), 2 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 99f0fd65b68..4ca9417ae5a 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -594,6 +594,33 @@ class ComputeOnchangeLine(models.Model): required=True, ondelete='cascade') +class ComputeUnassigned(models.Model): + _name = 'test_new_api.compute.unassigned' + _description = "Model with computed fields left unassigned" + + foo = fields.Char() + bar = fields.Char(compute='_compute_bar') + bare = fields.Char(compute='_compute_bare', readonly=False) + bars = fields.Char(compute='_compute_bars', store=True) + bares = fields.Char(compute='_compute_bares', readonly=False, store=True) + + @api.depends('foo') + def _compute_bar(self): + pass + + @api.depends('foo') + def _compute_bare(self): + pass + + @api.depends('foo') + def _compute_bars(self): + pass + + @api.depends('foo') + def _compute_bares(self): + pass + + class ModelBinary(models.Model): _name = 'test_new_api.model_binary' _description = 'Test Image field' diff --git a/odoo/addons/test_new_api/security/ir.model.access.csv b/odoo/addons/test_new_api/security/ir.model.access.csv index be706801d98..37b088db27e 100644 --- a/odoo/addons/test_new_api/security/ir.model.access.csv +++ b/odoo/addons/test_new_api/security/ir.model.access.csv @@ -26,6 +26,7 @@ access_test_new_api_cascade,access_test_new_api_cascade,model_test_new_api_casca access_test_new_api_compute_readwrite,access_test_new_api_compute_readwrite,model_test_new_api_compute_readwrite,,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_compute_onchange_line,access_test_new_api_compute_onchange_line,model_test_new_api_compute_onchange_line,,1,1,1,1 +access_test_new_api_compute_unassigned,access_test_new_api_compute_unassigned,model_test_new_api_compute_unassigned,,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/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 5591ce7e4c5..f8e9e6db49c 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -615,6 +615,25 @@ class TestFields(TransactionCaseWithUserDemo): discussion.name = "X" discussion.flush() + def test_16_compute_unassigned(self): + model = self.env['test_new_api.compute.unassigned'] + + # real record + record = model.create({}) + with self.assertRaises(ValueError): + record.bar + self.assertEqual(record.bare, False) + self.assertEqual(record.bars, False) + self.assertEqual(record.bares, False) + + # new record + record = model.new() + with self.assertRaises(ValueError): + record.bar + self.assertEqual(record.bare, False) + self.assertEqual(record.bars, False) + self.assertEqual(record.bares, False) + def test_20_float(self): """ test rounding of float fields """ record = self.env['test_new_api.mixed'].create({}) diff --git a/odoo/fields.py b/odoo/fields.py index 2838bfbef56..f4e26e7b935 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -966,8 +966,8 @@ class Field(MetaField('DummyField', (object,), {})): try: value = env.cache.get(record, self) except CacheMiss: - if self.readonly: - raise + if self.readonly and not self.store: + raise ValueError("Compute method failed to assign %s.%s" % (record, self.name)) # fallback to null value if compute gives nothing value = self.convert_to_cache(False, record, validate=False) env.cache.set(record, self, value)