summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-10-28 14:59:27 +0000
committerRaphael Collet <rco@odoo.com>2020-08-20 13:39:04 +0000
commit3e91fa1dbf9056f1557ff7a747ba3407e851b18e (patch)
tree83026623a533cedb334a35c0b128b7bede175622
parent38b7462dcf7541bbcb51ac75cc1f7b6715f13ace (diff)
[FIX] core: first onchange on new one2many line
Consider models `M` and `L`, with a one2many field `M.l_ids` with inverse `L.m_id`. Consider also a computed field `L.foo` that depends on `L.m_id`. The field `L.foo` is not computed correctly by `onchange` on a new line in the form view of `M`: its value is forced to `False` because it has no default, and is not recomputed by `onchange` since the field `L.m_id` is never triggered an `onchange`. Modify the method `onchange` to not force fields without a default to `False`, and return all fields.
-rw-r--r--odoo/addons/test_new_api/models/test_new_api.py2
-rw-r--r--odoo/addons/test_new_api/tests/test_onchange.py13
-rw-r--r--odoo/fields.py9
-rw-r--r--odoo/models.py20
4 files changed, 32 insertions, 12 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 602173c4145..99f0fd65b68 100644
--- a/odoo/addons/test_new_api/models/test_new_api.py
+++ b/odoo/addons/test_new_api/models/test_new_api.py
@@ -244,7 +244,7 @@ class MultiLine(models.Model):
multi = fields.Many2one('test_new_api.multi', ondelete='cascade')
name = fields.Char()
- partner = fields.Many2one('res.partner')
+ partner = fields.Many2one(related='multi.partner', store=True)
tags = fields.Many2many('test_new_api.multi.tag')
diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py
index 5bffeae5f86..38c7b8be531 100644
--- a/odoo/addons/test_new_api/tests/test_onchange.py
+++ b/odoo/addons/test_new_api/tests/test_onchange.py
@@ -378,6 +378,19 @@ class TestOnChange(SavepointCaseWithUserDemo):
result = Foo.onchange(values, 'value1', field_onchange)
self.assertEqual(result['value'], {})
+ def test_onchange_one2many_first(self):
+ partner = self.env['res.partner'].create({
+ 'name': 'X',
+ 'country_id': self.env.ref('base.be').id,
+ })
+ with common.Form(self.env['test_new_api.multi']) as form:
+ form.partner = partner
+ self.assertEqual(form.partner, partner)
+ self.assertEqual(form.name, partner.name)
+ with form.lines.new() as line:
+ # the first onchange() must have computed partner
+ self.assertEqual(line.partner, partner)
+
def test_onchange_one2many_value(self):
""" test the value of the one2many field inside the onchange """
discussion = self.env.ref('test_new_api.discussion_0')
diff --git a/odoo/fields.py b/odoo/fields.py
index 46d65d4e6f4..2838bfbef56 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -963,7 +963,14 @@ class Field(MetaField('DummyField', (object,), {})):
self.compute_value(recs)
except (AccessError, MissingError):
self.compute_value(record)
- value = env.cache.get(record, self)
+ try:
+ value = env.cache.get(record, self)
+ except CacheMiss:
+ if self.readonly:
+ raise
+ # fallback to null value if compute gives nothing
+ value = self.convert_to_cache(False, record, validate=False)
+ env.cache.set(record, self, value)
elif self.type == 'many2one' and self.delegate and not record.id:
# parent record of a new record: new record, with the same
diff --git a/odoo/models.py b/odoo/models.py
index f3a2b4d7530..991573a4155 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -5859,8 +5859,8 @@ Fields:
on_change attribute
When ``field_name`` is falsy, the method first adds default values
- to ``values``, applies onchange methods to them, and return all the
- fields in ``field_onchange``.
+ to ``values``, computes the remaining fields, applies onchange
+ methods to them, and return all the fields in ``field_onchange``.
"""
# this is for tests using `Form`
self.flush()
@@ -5902,11 +5902,12 @@ Fields:
""" A dict with the values of a record, following a prefix tree. """
__slots__ = ()
- def __init__(self, record, tree):
+ def __init__(self, record, tree, fetch=True):
# put record in dict to include it when comparing snapshots
super(Snapshot, self).__init__({'<record>': record, '<tree>': tree})
- for name in tree:
- self.fetch(name)
+ if fetch:
+ for name in tree:
+ self.fetch(name)
def fetch(self, name):
""" Set the value of field ``name`` from the record's value. """
@@ -5920,6 +5921,8 @@ Fields:
def has_changed(self, name):
""" Return whether a field on record has changed. """
+ if name not in self:
+ return True
record = self['<record>']
subnames = self['<tree>'][name]
if record._fields[name].type not in ('one2many', 'many2many'):
@@ -5977,14 +5980,11 @@ Fields:
nametree = PrefixTree(self.browse(), field_onchange)
if first_call:
- names = list(nametree)
values.update(self.default_get([
name
- for name in names
+ for name in nametree
if name not in values
]))
- for name in names:
- values.setdefault(name, False)
# prefetch x2many lines without data (for the initial snapshot)
for name, subnames in nametree.items():
@@ -6032,7 +6032,7 @@ Fields:
record = self.new(initial_values, origin=self)
# make a snapshot based on the initial values of record
- snapshot0 = Snapshot(record, nametree)
+ snapshot0 = Snapshot(record, nametree, fetch=(not first_call))
# store changed values in cache; also trigger recomputations based on
# subfields (e.g., line.a has been modified, line.b is computed stored