from odoo import api, fields, models, _ from odoo.exceptions import ValidationError class MenuAccessControl(models.Model): _name = 'menu.access.control' _description = 'Menu Access Control' _rec_name = 'master_control_id' master_control_id = fields.Many2one('master.control', string='Master Control', required=True) department_ids = fields.Many2many('hr.department', string='Departments') available_user_ids = fields.Many2many( 'res.users', compute='_compute_available_user_ids', string='Available Users', ) user_ids = fields.Many2many( 'res.users', 'menu_access_control_res_users_rel', 'access_control_id', 'user_id', string='Users', ) access_menu_line_ids = fields.One2many( 'menu.access.line', 'access_control_id', string='Menus', domain=[('menu_id.parent_id', '=', False)], ) access_sub_menu_line_ids = fields.One2many( 'menu.access.line', 'access_control_id', string='Sub Menus', domain=[('menu_id.parent_id', '!=', False)], ) @api.depends('master_control_id', 'master_control_id.user_ids') def _compute_available_user_ids(self): for record in self: record.available_user_ids = record.master_control_id.user_ids @api.onchange('master_control_id') def _onchange_master_control_id(self): for record in self: available_users = record.master_control_id.user_ids record.user_ids = record.user_ids.filtered(lambda user: user in available_users) record._add_department_users() @api.onchange('department_ids') def _onchange_department_ids(self): self._add_department_users() def _add_department_users(self): for record in self: if not record.master_control_id: record.user_ids = [(5, 0, 0)] continue allowed_users = record.master_control_id.user_ids department_users = self.env['hr.employee'].sudo().search([ ('department_id', 'in', record.department_ids.ids), ('user_id', '!=', False), ]).mapped('user_id') record.user_ids = (record.user_ids.filtered(lambda user: user in allowed_users) | (department_users & allowed_users)) def _sync_menu_lines_from_master(self): line_model = self.env['menu.access.line'] for record in self: master_lines = (record.master_control_id.menu_line_ids | record.master_control_id.submenu_line_ids).filtered('show_menu') existing_lines = {line.menu_id.id: line for line in record.access_menu_line_ids | record.access_sub_menu_line_ids} valid_menu_ids = set(master_lines.mapped('menu_id').ids) for line in (record.access_menu_line_ids | record.access_sub_menu_line_ids): if line.menu_id.id not in valid_menu_ids: line.unlink() refreshed_lines = {line.menu_id.id: line for line in record.access_menu_line_ids | record.access_sub_menu_line_ids} parent_lines = {} for master_line in master_lines.filtered(lambda line: not line.menu_id.parent_id): access_line = refreshed_lines.get(master_line.menu_id.id) if not access_line: access_line = line_model.create({ 'access_control_id': record.id, 'menu_id': master_line.menu_id.id, 'show_menu': True, }) parent_lines[master_line.menu_id.id] = access_line for master_line in master_lines.filtered(lambda line: line.menu_id.parent_id): parent_root_menu = master_line.menu_id while parent_root_menu.parent_id: parent_root_menu = parent_root_menu.parent_id parent_line = parent_lines.get(parent_root_menu.id) access_line = refreshed_lines.get(master_line.menu_id.id) if not access_line: line_model.create({ 'access_control_id': record.id, 'menu_id': master_line.menu_id.id, 'show_menu': True, 'parent_line_id': parent_line.id if parent_line else False, }) @api.model_create_multi def create(self, vals_list): records = super().create(vals_list) records._sync_menu_lines_from_master() self.env['ir.ui.menu'].sudo().clear_caches() return records def write(self, vals): result = super().write(vals) if {'master_control_id', 'department_ids'} & set(vals): self._sync_menu_lines_from_master() self._validate_users_belong_to_master() self.env['ir.ui.menu'].sudo().clear_caches() return result @api.constrains('master_control_id', 'user_ids') def _validate_users_belong_to_master(self): for record in self: allowed_users = record.master_control_id.user_ids invalid_users = record.user_ids.filtered(lambda user: user not in allowed_users) if invalid_users: raise ValidationError( _('Users in Menu Access Control must belong to the selected Master Control.') ) def action_refresh_from_master(self): self._sync_menu_lines_from_master() return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Success'), 'message': _('Menus refreshed from Master Control.'), 'type': 'success', 'sticky': False, }, } def unlink(self): result = super().unlink() self.env['ir.ui.menu'].sudo().clear_caches() return result class MenuAccessLine(models.Model): _name = 'menu.access.line' _description = 'Menu Access Line' _rec_name = 'menu_id' _order = 'menu_id' access_control_id = fields.Many2one('menu.access.control', ondelete='cascade') menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True) show_menu = fields.Boolean(string='Show Menu', default=True) parent_menu_id = fields.Many2one('ir.ui.menu', related='menu_id.parent_id', string='Parent Menu', store=True) parent_line_id = fields.Many2one('menu.access.line', string='Parent Line') master_control_id = fields.Many2one('master.control', related='access_control_id.master_control_id', store=True) _sql_constraints = [ ('menu_access_line_unique', 'unique(access_control_id, menu_id)', 'Menu already exists in this access control.'), ] def open_submenus_popup_view(self): self.ensure_one() return { 'name': _('Sub Menus'), 'type': 'ir.actions.act_window', 'res_model': 'menu.access.line', 'view_mode': 'list,form', 'views': [ (self.env.ref('menu_control_center.view_submenu_line_list').id, 'list'), ], 'target': 'new', 'domain': [('parent_line_id', '=', self.id)], 'context': {'default_parent_line_id': self.id}, } @api.model_create_multi def create(self, vals_list): records = super().create(vals_list) self.env['ir.ui.menu'].sudo().clear_caches() return records def write(self, vals): result = super().write(vals) self.env['ir.ui.menu'].sudo().clear_caches() return result def unlink(self): result = super().unlink() self.env['ir.ui.menu'].sudo().clear_caches() return result