From 400efc4d40d822a99f744faf50dadff6cd684333 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 3 Oct 2019 11:48:32 +0000 Subject: [PATCH] [FIX] models: make inverse of x2many fields work --- odoo/addons/test_inherits/ir.model.access.csv | 1 + odoo/addons/test_inherits/models.py | 9 +++++++++ .../test_inherits/tests/test_inherits.py | 20 +++++++++++++++++++ odoo/models.py | 20 ++++++++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/odoo/addons/test_inherits/ir.model.access.csv b/odoo/addons/test_inherits/ir.model.access.csv index 09aeec489d8..9766ca0acdd 100644 --- a/odoo/addons/test_inherits/ir.model.access.csv +++ b/odoo/addons/test_inherits/ir.model.access.csv @@ -1,5 +1,6 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" access_test_unit,access_test_unit,model_test_unit,,1,1,1,1 +access_test_unit_line,access_test_unit_line,model_test_unit_line,,1,1,1,1 access_test_box,access_test_box,model_test_box,,1,1,1,1 access_test_pallet,access_test_pallet,model_test_pallet,,1,1,1,1 access_test_another_unit,access_test_another_unit,model_test_another_unit,,1,1,1,1 diff --git a/odoo/addons/test_inherits/models.py b/odoo/addons/test_inherits/models.py index f20785d1b67..be583b889f5 100644 --- a/odoo/addons/test_inherits/models.py +++ b/odoo/addons/test_inherits/models.py @@ -11,6 +11,7 @@ class Unit(models.Model): name = fields.Char('Name', required=True) state = fields.Selection([('a', 'A'), ('b', 'B')], string='State') surname = fields.Char(compute='_compute_surname') + line_ids = fields.One2many('test.unit.line', 'unit_id') @api.depends('name') def _compute_surname(self): @@ -18,6 +19,14 @@ class Unit(models.Model): unit.surname = unit.name or '' +class UnitLine(models.Model): + _name = 'test.unit.line' + _description = 'Test Unit Line' + + name = fields.Char('Name', required=True) + unit_id = fields.Many2one('test.unit', required=True) + + # We want to _inherits from the parent model and we add some fields # in the child object class Box(models.Model): diff --git a/odoo/addons/test_inherits/tests/test_inherits.py b/odoo/addons/test_inherits/tests/test_inherits.py index 3328f23e2ed..68aa44759b2 100644 --- a/odoo/addons/test_inherits/tests/test_inherits.py +++ b/odoo/addons/test_inherits/tests/test_inherits.py @@ -28,6 +28,26 @@ class test_inherits(common.TransactionCase): pallet.write({'name': 'C'}) self.assertEqual(pallet.name, 'C') + def test_write_4_one2many(self): + """ Check that we can write on an inherited one2many field. """ + box = self.env.ref('test_inherits.box_a') + box.write({'line_ids': [(0, 0, {'name': 'Line 1'})]}) + self.assertTrue(all(box.line_ids._ids)) + self.assertEqual(box.line_ids.mapped('name'), ['Line 1']) + self.assertEqual(box.line_ids, box.unit_id.line_ids) + box.flush() + box.invalidate_cache(['line_ids']) + box.write({'line_ids': [(0, 0, {'name': 'Line 2'})]}) + self.assertTrue(all(box.line_ids._ids)) + self.assertEqual(box.line_ids.mapped('name'), ['Line 1', 'Line 2']) + self.assertEqual(box.line_ids, box.unit_id.line_ids) + box.flush() + box.invalidate_cache(['line_ids']) + box.write({'line_ids': [(1, box.line_ids[0].id, {'name': 'First line'})]}) + self.assertTrue(all(box.line_ids._ids)) + self.assertEqual(box.line_ids.mapped('name'), ['First line', 'Line 2']) + self.assertEqual(box.line_ids, box.unit_id.line_ids) + def test_ir_model_data_inherits(self): """ Check the existence of the correct ir.model.data """ IrModelData = self.env['ir.model.data'] diff --git a/odoo/models.py b/odoo/models.py index 16ee190dd7d..166f5c84284 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3396,6 +3396,14 @@ Record ids: %(records)s for fname in vals: field = self._fields[fname] if field.inverse: + if field.type in ('one2many', 'many2many'): + # The written value is a list of commands that must applied + # on the field's current value. Because the field is + # protected while being written, the field's current value + # will not be computed and default to an empty recordset. So + # make sure the field's value is in cache before writing, in + # order to avoid an inconsistent update. + self[fname] determine_inverses[field.inverse].append(field) # DLE P150: `test_cancel_propagation`, `test_manufacturing_3_steps`, `test_manufacturing_flow` # TODO: check whether still necessary @@ -3403,7 +3411,17 @@ Record ids: %(records)s if field.relational or self._field_inverses[field]: relational_names.append(fname) if field.compute and not field.readonly: - protected.update(self._field_computed.get(field, [field])) + if field.store or field.type not in ('one2many', 'many2many'): + # Protect the field from being recomputed while being + # inversed. In the case of non-stored x2many fields, the + # field's value may contain unexpeced new records (created + # by command 0). Those new records are necessary for + # inversing the field, but should no longer appear if the + # field is recomputed afterwards. Not protecting the field + # will automatically invalidate the field from the cache, + # forcing its value to be recomputed once dependencies are + # up-to-date. + protected.update(self._field_computed.get(field, [field])) if fname == 'company_id' or (field.relational and field.check_company): check_company = True