[FIX] core: o2m modifiers in the SSF

Because the sub-record values would not necessarily get fetched
ever (whether default or stored), the computation of modifiers might
blow up if it relied on one of the un-fetched un-specified fields.

One such situation is trying to create a partner with child partners
if base_address_city is installed: the module adds a readonly attr
predicated upon the parent_id, without explicitly providing such the
field would be missing from the O2M record's values.

Closes #37176

closes odoo/odoo#37452

X-original-commit: 184d1b69eac2b78c72228c81c5c6cf8cc4a56eb4
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
This commit is contained in:
Xavier Morel
2019-09-25 16:37:19 +00:00
committed by fw-bot
parent 98b7129e20
commit 77099c54b6
3 changed files with 62 additions and 4 deletions
@@ -164,4 +164,19 @@
</form>
</field>
</record>
<record id="o2m_modifier" model="ir.ui.view">
<field name="name">Modifier check</field>
<field name="model">test_testing_utilities.parent</field>
<field name="inherit_id" ref="o2m_parent"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="subs" position="inside">
<form>
<field name="value" attrs="{'readonly': [('v', '!=', False)]}"/>
<field name="v"/>
</form>
</field>
</field>
</record>
</odoo>
@@ -497,6 +497,17 @@ class TestO2M(TransactionCase):
def test_o2m_self_recursive(self):
Form(self.env['test_testing_utilities.recursive'], view='test_testing_utilities.o2m_recursive_relation_view')
def test_o2m_attrs(self):
Model = self.env['test_testing_utilities.parent'].with_context(
default_subs=[{
'value': 5,
}, {
'value': 7,
}]
)
f = Form(Model, view='test_testing_utilities.o2m_modifier')
f.save()
class TestEdition(TransactionCase):
""" These use the context manager form as we don't need the record
post-save (we already have it) and it's easier to see what bits act on
+36 -4
View File
@@ -1537,8 +1537,9 @@ class Form(object):
values = {}
fields = self._view['fields']
for f in fields:
descr = fields[f]
v = self._values[f]
if self._get_modifier(f, 'required') and not fields[f]['type'] == 'boolean':
if self._get_modifier(f, 'required') and not descr['type'] == 'boolean':
assert v is not False, "{} is a required field".format(f)
# skip unmodified fields
@@ -1550,8 +1551,8 @@ class Form(object):
if not node.get('force_save'):
continue
if fields[f]['type'] == 'one2many':
view = fields[f]['views']['edition']
if descr['type'] == 'one2many':
view = descr['views']['edition']
modifiers = view['modifiers']
oldvals = v
v = []
@@ -1565,7 +1566,21 @@ class Form(object):
for (c, rid, vs) in oldvals:
if c in (0, 1):
items = list(getattr(vs, 'changed_items', vs.items)())
# FIXME: should be more extensive processing of o2m defaults
fields_ = view['fields']
missing = fields_.keys() - vs.keys()
if missing:
Model = self._env[descr['relation']]
if c == 0:
vs.update(dict.fromkeys(missing, False))
vs.update(
(k, _cleanup_from_default(fields_[k], v))
for k, v in Model.default_get(list(missing)).items()
)
else:
vs.update(record_to_values(
{k: v for k, v in fields_.items() if k not in vs},
Model.browse(rid)
))
vs.setdefault('id', False)
vs['•parent•'] = self._values
vs = {
@@ -1971,6 +1986,23 @@ def record_to_values(fields, record):
r[f] = v
return r
def _cleanup_from_default(type_, value):
if not value:
if type_ == 'many2many':
return [(6, False, [])]
elif type_ == 'one2many':
return []
elif type_ in ('integer', 'float'):
return 0
return value
if type_ == 'one2many':
return [c for c in value if c[0] != 6]
elif type_ == 'datetime' and isinstance(value, datetime):
return odoo.fields.Datetime.to_string(value)
elif type_ == 'date' and isinstance(value, date):
return odoo.fields.Date.to_string(value)
def _get_node(view, f, *arg):
""" Find etree node for the field ``f`` in the view's arch
"""