diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index efcf203edf0..05c154bc466 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -624,6 +624,112 @@ class Users(models.Model): def get_company_currency_id(self): return self.env.user.company_id.currency_id.id + def _add_missing_default_values(self, vals): + # Remove the default values of 'group_' and 'has_group' fields at the user creation if + # 'groups_id' is provided in vals and if the fields value is not explicitely given. + res = super(Users, self)._add_missing_default_values(vals) + return { + key: value for (key, value) in res.items() + if 'groups_id' not in vals or key in vals or (not is_boolean_group(key) or not is_selection_groups(key)) + } + + def _get_group_selection(self, category_xml_id): + """Returns the ordered selection for the given ir.module.category xmlid""" + category = self.env.ref(category_xml_id, raise_if_not_found=False) + selections = [] + if category: + groups = self.env['res.groups'].search([('category_id', '=', category.id)]) + order = {group: len(group.trans_implied_ids & groups) for group in groups} + groups = groups.sorted(key=order.get) + selections = [(group.id, group.name) for group in groups] + return selections + + @api.depends('groups_id') + def _compute_groups_id(self): + """Set the value for the group field (Selection of Boolean) + according to the user's groups, defined by the field 'groups_id'. + + These methods should be used on fields defined like one the + following cases: + 1. A boolean field with the compute/inverse method and an attribute + group_xml_id. + Example: + has_group_discount_per_so_line = fields.Boolean( + "Discount on lines", + compute='_compute_groups_id', inverse='_inverse_groups_id', + group_xml_id='sale.group_discount_per_so_line') + 2. A selection field with the compute/inverse method, the selection + got from '_get_group_selection' and an attribute + 'category_xml_id' + Example: + group_sales_team_user = fields.Selection( + selection=lambda self: self._get_group_selection('base.module_category_sales_management'), + string="Sales", compute='_compute_groups_id', inverse='_inverse_groups_id', + category_xml_id='base.module_category_sales_management') + The fields will be set according to the groups defined in the 'groups_id' + field, and the 'groups_id' field will be modified according to the + modifications made on theses fields when saving the users's form. + """ + computed_group_fields = [field for field in self._field_computed.keys() if field.compute == '_compute_groups_id'] + all_groups = self.env['res.groups'].search([]) + category_data = {} + # Retrieve groups order for selection fields + for field in computed_group_fields: + if getattr(field, 'category_xml_id', None): + category = self.env.ref(getattr(field, 'category_xml_id')) + category_groups = all_groups.filtered(lambda group: group.category_id == category) + order = {group: len(group.trans_implied_ids & category_groups) for group in category_groups} + category_data[field] = category_groups.sorted(key=order.get, reverse=True) + # Set group fields values according to groups_id + for user in self: + for field in computed_group_fields: + # Checkbox group + if getattr(field, 'group_xml_id', None): + group = self.env.ref(getattr(field, 'group_xml_id')) + user[field.name] = group in user.groups_id + # Selection group + elif getattr(field, 'category_xml_id', None): + # Take the highest level group the user belongs to + user_groups = [group for group in category_data[field] if group in user.groups_id] + user[field.name] = user_groups[0].id if user_groups else False + else: + _logger.warning(_("No 'group_xml_id' or 'category_xml_id' is set on the computed field %s linked to the method _compute_groups_id") % (field.name)) + + def _inverse_groups_id(self): + """Update 'groups_id' according the group fields values in cache.""" + computed_group_fields = [field for field in self._field_computed.keys() if field.compute == '_compute_groups_id'] + all_groups = self.env['res.groups'].search([]) + for user in self: + # we need to read all values in cache before any prefetch + field_values = {field: user[field.name] for field in computed_group_fields if field.name in user._cache} + groups_id_vals = [] + for field, value in field_values.items(): + # Checkbox group + if getattr(field, 'group_xml_id', None): + selected_group = self.env.ref(getattr(field, 'group_xml_id')) + if value: + groups_id_vals.append((4, selected_group.id)) + else: + groups_id_vals.append((3, selected_group.id)) + for group in selected_group.trans_implied_ids: + groups_id_vals.append((4, group.id)) + # Selection group + elif getattr(field, 'category_xml_id', None): + category = self.env.ref(getattr(field, 'category_xml_id')) + category_groups = [group for group in all_groups if group.category_id == category] + selected_group = [group for group in all_groups if group.id == value] + selected_group = selected_group and selected_group[0] or self.env['res.groups'] + for group in category_groups: + if group in (selected_group.trans_implied_ids | selected_group): + groups_id_vals.append((4, group.id)) + else: + groups_id_vals.append((3, group.id)) + else: + _logger.warning(_("No 'group_xml_id' or 'category_xml_id' is set on the computed field %s linked to the method _compute_groups_id") % (field.name)) + if groups_id_vals: + user.write({'groups_id': groups_id_vals}) + + # # Implied groups #