[FIX] core: in onchange, assign inherited fields that have changed

This allows onchange to recompute fields that depend on the inherited
field that has changed.  The use-case we have is a model that inherits
from 'account.move'.  On its form view, setting the (inherited) journal
field does not recompute the (inherited) company field.

When the field 'journal_id' is modified, the onchange should:
 - assign the new value to move_id.journal_id (*)
 - recompute move_id.company_id (related='journal_id.company_id')
 - recompute company_id

This commit adds the missing part (*).

X-original-commit: 2e05d0a361aa06a34eecf64613821f2e92295298
This commit is contained in:
Raphael Collet
2021-07-08 15:29:17 +00:00
parent f70ad06775
commit be73b9ae26
4 changed files with 53 additions and 1 deletions
@@ -455,6 +455,8 @@ class Move(models.Model):
line_ids = fields.One2many('test_new_api.move_line', 'move_id', domain=[('visible', '=', True)])
quantity = fields.Integer(compute='_compute_quantity', store=True)
tag_id = fields.Many2one('test_new_api.multi.tag')
tag_name = fields.Char(related='tag_id.name')
@api.depends('line_ids.quantity')
def _compute_quantity(self):
@@ -471,6 +473,14 @@ class MoveLine(models.Model):
quantity = fields.Integer()
class Payment(models.Model):
_name = 'test_new_api.payment'
_description = 'Payment inherits from Move'
_inherits = {'test_new_api.move': 'move_id'}
move_id = fields.Many2one('test_new_api.move', required=True, ondelete='cascade')
class CompanyDependent(models.Model):
_name = 'test_new_api.company'
_description = 'Test New API Company'
@@ -66,6 +66,7 @@ access_test_new_api_model_selection_non_stored,access_test_new_api_model_selecti
access_test_new_api_model_selection_required_for_write_override,access_test_new_api_model_selection_required_for_write_override,model_test_new_api_model_selection_required_for_write_override,,1,1,1,1
access_test_new_api_move,access_test_new_api_move,model_test_new_api_move,,1,1,1,1
access_test_new_api_move_line,access_test_new_api_move_line,model_test_new_api_move_line,,1,1,1,1
access_test_new_api_payment,access_test_new_api_payment,model_test_new_api_payment,,1,1,1,1
access_test_new_api_model_shared_cache_compute_parent,access_test_new_api.model_shared_cache_compute_parent,model_test_new_api_model_shared_cache_compute_parent,,1,1,1,1
access_test_new_api_model_shared_cache_compute_line,access_test_new_api.model_shared_cache_compute_line,model_test_new_api_model_shared_cache_compute_line,,1,1,1,1
access_test_new_api_compute_container,access_test_new_api_compute_container,model_test_new_api_compute_container,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
66 access_test_new_api_model_selection_required_for_write_override access_test_new_api_model_selection_required_for_write_override model_test_new_api_model_selection_required_for_write_override 1 1 1 1
67 access_test_new_api_move access_test_new_api_move model_test_new_api_move 1 1 1 1
68 access_test_new_api_move_line access_test_new_api_move_line model_test_new_api_move_line 1 1 1 1
69 access_test_new_api_payment access_test_new_api_payment model_test_new_api_payment 1 1 1 1
70 access_test_new_api_model_shared_cache_compute_parent access_test_new_api.model_shared_cache_compute_parent model_test_new_api_model_shared_cache_compute_parent 1 1 1 1
71 access_test_new_api_model_shared_cache_compute_line access_test_new_api.model_shared_cache_compute_line model_test_new_api_model_shared_cache_compute_line 1 1 1 1
72 access_test_new_api_compute_container access_test_new_api_compute_container model_test_new_api_compute_container 1 1 1 1
@@ -537,6 +537,38 @@ class TestOnChange(SavepointCaseWithUserDemo):
self.assertFalse(called[0], "discussion.messages has been read")
def test_onchange_inherited(self):
""" Setting an inherited field should assign the field on the parent record. """
foo, bar = self.env['test_new_api.multi.tag'].create([{'name': 'Foo'}, {'name': 'Bar'}])
view = self.env['ir.ui.view'].create({
'name': 'Payment form view',
'model': 'test_new_api.payment',
'arch': """
<form>
<field name="tag_id"/>
<field name="tag_name"/>
</form>
""",
})
# both fields 'tag_id' and 'tag_name' are inherited through 'move_id';
# assigning 'tag_id' should modify 'move_id.tag_id' accordingly, which
# should in turn recompute `move.tag_name` and `tag_name`
form = Form(self.env['test_new_api.payment'], view)
form.tag_id = foo
self.assertEqual(form.tag_name, 'Foo')
payment = form.save()
self.assertEqual(payment.tag_id, foo)
self.assertEqual(payment.tag_name, 'Foo')
with Form(payment, view) as form:
form.tag_id = bar
self.assertEqual(form.tag_name, 'Bar')
self.assertEqual(payment.tag_id, bar)
self.assertEqual(payment.tag_name, 'Bar')
class TestComputeOnchange(common.TransactionCase):
+10 -1
View File
@@ -6111,7 +6111,7 @@ Fields:
presence of ``other_fields``.
"""
return (field.name in self._onchange_methods) or any(
dep in other_fields for dep in self._dependent_fields(field)
dep in other_fields for dep in self._dependent_fields(field.base_field)
)
@api.model
@@ -6404,6 +6404,15 @@ Fields:
protected = [self._fields[name] for name in names]
with self.env.protecting(protected, record):
record.modified(todo)
for name in todo:
field = self._fields[name]
if field.inherited:
# modifying an inherited field should modify the parent
# record accordingly; because we don't actually assign the
# modified field on the record, the modification on the
# parent record has to be done explicitly
parent = record[field.related.split('.')[0]]
parent[name] = record[name]
result = {'warnings': OrderedSet()}