From a313f342d380a4046a765b708dddb06c46a20ef2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 22 Jan 2019 11:12:39 +0000 Subject: [PATCH] [IMP] core: add force_save support to server-side form Cf 3b3f6f04af79d7dcc4461262e14e05096d76180b for basic feature description. Task 1931580 closes odoo/odoo#30428 --- odoo/addons/test_testing_utilities/menu.xml | 2 ++ odoo/addons/test_testing_utilities/models.py | 4 ++++ .../tests/test_form_impl.py | 13 ++++++++++++ odoo/tests/common.py | 21 +++++++++++++++---- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/odoo/addons/test_testing_utilities/menu.xml b/odoo/addons/test_testing_utilities/menu.xml index fa5263efeca..99e409708c2 100644 --- a/odoo/addons/test_testing_utilities/menu.xml +++ b/odoo/addons/test_testing_utilities/menu.xml @@ -101,6 +101,8 @@ + + diff --git a/odoo/addons/test_testing_utilities/models.py b/odoo/addons/test_testing_utilities/models.py index 68dc9172123..98a9fea7e7f 100644 --- a/odoo/addons/test_testing_utilities/models.py +++ b/odoo/addons/test_testing_utilities/models.py @@ -10,10 +10,14 @@ class A(models.Model): f2 = fields.Integer(default=42) f3 = fields.Integer() f4 = fields.Integer(compute='_compute_f4') + f5 = fields.Integer() + f6 = fields.Integer() @api.onchange('f2') def _on_change_f2(self): self.f3 = int(self.f2 / 2) + self.f5 = self.f2 + self.f6 = self.f2 @api.depends('f1', 'f2') def _compute_f4(self): diff --git a/odoo/addons/test_testing_utilities/tests/test_form_impl.py b/odoo/addons/test_testing_utilities/tests/test_form_impl.py index a14d6b93974..e2f0d0d89d3 100644 --- a/odoo/addons/test_testing_utilities/tests/test_form_impl.py +++ b/odoo/addons/test_testing_utilities/tests/test_form_impl.py @@ -65,6 +65,19 @@ class TestBasic(TransactionCase): with self.assertRaises(AssertionError): f.f2 = 42 + def test_readonly_save(self): + """ Should not save readonly fields unless they're force_save + """ + f = Form(self.env['test_testing_utilities.a'], view='test_testing_utilities.non_normalized_attrs') + + f.f1 = 1 + f.f2 = 987 + self.assertEqual(f.f5, 987) + self.assertEqual(f.f6, 987) + r = f.save() + self.assertEqual(r.f5, 0) + self.assertEqual(r.f6, 987) + def test_attrs(self): """ Checks that attrs/modifiers with non-normalized domains work """ diff --git a/odoo/tests/common.py b/odoo/tests/common.py index 412634176d3..56b76e01c84 100644 --- a/odoo/tests/common.py +++ b/odoo/tests/common.py @@ -1098,7 +1098,7 @@ class Form(object): else: view_id = view or False fvg = recordp.fields_view_get(view_id, 'form') - arch = etree.fromstring(fvg['arch']) + fvg['tree'] = etree.fromstring(fvg['arch']) object.__setattr__(self, '_view', fvg) # TODO: make this less crappy? @@ -1107,7 +1107,7 @@ class Form(object): if descr['type'] != 'one2many': continue - node = next(n for n in arch.iter('field') if n.get('name') == f) + node = self._get_node(f) default_view = next( (m for m in node.get('mode', 'tree').split(',') if m != 'form'), 'tree' @@ -1130,10 +1130,12 @@ class Form(object): # if the default view is a kanban or a non-editable list, the # "edition controller" is the form view edition = views['form'] + edition['tree'] = etree.fromstring(edition['arch']) if default_view == 'tree': subarch = etree.fromstring(views['tree']['arch']) if subarch.get('editable'): edition = views['tree'] + edition['tree'] = subarch self._process_fvg(submodel, edition) descr['views']['edition'] = edition @@ -1153,6 +1155,14 @@ class Form(object): else: self._init_from_defaults(self._model) + def _get_node(self, f): + """ Find etree node for the field ``f`` in the current arch + """ + return next( + n for n in self._view['tree'].iter('field') + if n.get('name') == f + ) + def __str__(self): return "<%s %s(%s)>" % ( type(self).__name__, @@ -1171,7 +1181,7 @@ class Form(object): # pre-resolve modifiers & bind to arch toplevel modifiers = fvg['modifiers'] = {} contexts = fvg['contexts'] = {} - for f in etree.fromstring(fvg['arch']).iter('field'): + for f in fvg['tree'].iter('field'): fname = f.get('name') modifiers[fname] = { modifier: domain if isinstance(domain, bool) else normalize_domain(domain) @@ -1356,8 +1366,11 @@ class Form(object): # skip unmodified fields if f not in self._changed: continue + if self._get_modifier(f, 'readonly'): - continue + node = self._get_node(f) + if not node.get('force_save'): + continue # TODO: filter out (1, _, {}) from o2m values values[f] = v return values