diff options
| author | Raphael Collet <rco@odoo.com> | 2019-10-28 14:59:27 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2020-08-20 13:39:04 +0000 |
| commit | 3e91fa1dbf9056f1557ff7a747ba3407e851b18e (patch) | |
| tree | 83026623a533cedb334a35c0b128b7bede175622 | |
| parent | 38b7462dcf7541bbcb51ac75cc1f7b6715f13ace (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.py | 2 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_onchange.py | 13 | ||||
| -rw-r--r-- | odoo/fields.py | 9 | ||||
| -rw-r--r-- | odoo/models.py | 20 |
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 |
