[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:
@@ -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
@@ -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
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user