[FIX] base: virtual group fields on users with new default/onchange
* override `onchange` in res.users in order to properly generate and
send the reified group fields alongside the rest: while default_get
sets them up, those fields then get stripped by the onchange
machinery as they don't actually exist on the model
* add a test to check for it
* fix SSF in relation with the new default/onchange system
- because fields may not actually exist on the record,
`record_to_values` needs to `read` the record data rather than
directly access the recordset: the recordset likely will not
know about fake fields
- after the initial onchange has run the form must be filled with
falsy values in case the onchange has not sent defaults for
everything
- on creation, all fields in the form should be considered modified
Task 2341153
closes odoo/odoo#58152
X-original-commit: f8658f229180a595d69cd6daefeded082c7f64ea
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
This commit is contained in:
@@ -1387,6 +1387,16 @@ class UsersView(models.Model):
|
||||
self._add_reified_groups(group_fields, values)
|
||||
return values
|
||||
|
||||
def onchange(self, values, field_name, field_onchange):
|
||||
field_onchange['groups_id'] = ''
|
||||
result = super().onchange(values, field_name, field_onchange)
|
||||
if not field_name: # merged default_get
|
||||
self._add_reified_groups(
|
||||
filter(is_reified_group, field_onchange),
|
||||
result.setdefault('value', {})
|
||||
)
|
||||
return result
|
||||
|
||||
def read(self, fields=None, load='_classic_read'):
|
||||
# determine whether reified groups fields are required, and which ones
|
||||
fields1 = fields or list(self.fields_get())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests.common import TransactionCase, Form, tagged
|
||||
|
||||
|
||||
class TestUsers(TransactionCase):
|
||||
@@ -114,3 +114,18 @@ class TestUsers(TransactionCase):
|
||||
"On user company change, if its partner_id has already a company_id,"
|
||||
"the company_id of the partner_id shall be updated"
|
||||
)
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestUsers2(TransactionCase):
|
||||
def test_reified_groups(self):
|
||||
""" The groups handler doesn't use the "real" view with pseudo-fields
|
||||
during installation, so it always works (because it uses the normal
|
||||
groups_id field).
|
||||
"""
|
||||
# use the specific views which has the pseudo-fields
|
||||
f = Form(self.env['res.users'], view='base.view_users_form')
|
||||
f.name = "bob"
|
||||
f.login = "bob"
|
||||
user = f.save()
|
||||
|
||||
self.assertIn(self.env.ref('base.group_user'), user.groups_id)
|
||||
|
||||
+22
-8
@@ -1701,6 +1701,15 @@ class Form(object):
|
||||
# call onchange with an empty list of fields; this retrieves default
|
||||
# values, applies onchanges and return the result
|
||||
self._perform_onchange([])
|
||||
# fill in whatever fields are still missing with falsy values
|
||||
vals.update(
|
||||
(f, _cleanup_from_default(descr['type'], False))
|
||||
for f, descr in self._view['fields'].items()
|
||||
if f not in vals
|
||||
)
|
||||
# mark all fields as modified (though maybe this should be done on
|
||||
# save when creating for better reliability?)
|
||||
self._changed.update(self._view['fields'])
|
||||
|
||||
def _init_from_values(self, values):
|
||||
self._values.update(
|
||||
@@ -1981,7 +1990,7 @@ class Form(object):
|
||||
_logger.getChild('onchange').warning("%(title)s %(message)s" % result.get('warning'))
|
||||
values = result.get('value', {})
|
||||
# mark onchange output as changed
|
||||
self._changed.update(values.keys())
|
||||
self._changed.update(values.keys() & self._view['fields'].keys())
|
||||
self._values.update(
|
||||
(k, self._cleanup_onchange(
|
||||
self._view['fields'][k],
|
||||
@@ -2385,16 +2394,21 @@ class M2MProxy(X2MProxy, collections.Sequence):
|
||||
|
||||
def record_to_values(fields, record):
|
||||
r = {}
|
||||
for f, descr in fields.items():
|
||||
v = record[f]
|
||||
# don't read the id explicitly, not sure why but if any of the "magic" hr
|
||||
# field is read alongside `id` then it blows up e.g.
|
||||
# james.read(['barcode']) works fine but james.read(['id', 'barcode'])
|
||||
# triggers an ACL error on barcode, likewise km_home_work or
|
||||
# emergency_contact or whatever. Since we always get the id anyway, just
|
||||
# remove it from the fields to read
|
||||
to_read = list(fields.keys() - {'id'})
|
||||
for f, v in record.read(to_read)[0].items():
|
||||
descr = fields[f]
|
||||
if descr['type'] == 'many2one':
|
||||
assert v._name == descr['relation']
|
||||
v = v.id
|
||||
v = v and v[0]
|
||||
elif descr['type'] == 'many2many':
|
||||
assert v._name == descr['relation']
|
||||
v = [(6, 0, v.ids)]
|
||||
v = [(6, 0, v or [])]
|
||||
elif descr['type'] == 'one2many':
|
||||
v = [(1, r.id, None) for r in v]
|
||||
v = [(1, r, None) for r in v or []]
|
||||
elif descr['type'] == 'datetime' and isinstance(v, datetime):
|
||||
v = odoo.fields.Datetime.to_string(v)
|
||||
elif descr['type'] == 'date' and isinstance(v, date):
|
||||
|
||||
Reference in New Issue
Block a user