# models/models.py from odoo import models, fields, api, tools, _ from collections import defaultdict class MenuControlUnits(models.Model): _name = 'menu.control.units' _rec_name = 'unit_name' _sql_constraints = [ ('unique_unit_name', 'UNIQUE(unit_name)', "'Unit Name' already defined. Please don't confuse me 😤.") ] unit_name = fields.Char(string='Unit Name',required=True) department_ids = fields.Many2many('hr.department') user_ids = fields.Many2many('res.users') def generate_department_user_ids(self): for rec in self: user_ids = self.env['hr.employee'].sudo().search([('department_id','in',rec.department_ids.ids)]).user_id.ids self.write({ 'user_ids': [(6, 0, user_ids)] }) class MenuAccessControl(models.Model): _name = 'menu.access.control' _description = 'Menu Access Control' _rec_name = 'control_unit' _sql_constraints = [ ('unique_control_unit', 'UNIQUE(control_unit, master_control)', "Only one service can exist with a specific control_unit & Master. Please don't confuse me 🤪.") ] control_unit = fields.Many2one('menu.control.units',required=True) user_ids = fields.Many2many('res.users', string="Users", related='control_unit.user_ids') master_control = fields.Many2one('master.control') access_menu_line_ids = fields.One2many( 'menu.access.line', 'access_control_id', string="Accessible Menus", domain=[('menu_id.parent_id','=',False)] ) access_sub_menu_line_ids = fields.One2many('menu.access.line', 'access_control_id', string="Accessible Menus", domain=[('menu_id.parent_id','!=',False)] ) def _get_all_submenus(self, menu): """Returns all submenus recursively for a given menu.""" submenus = self.env['ir.ui.menu'].search([('parent_id', '=', menu.id), ('active', '=', True)]) all_subs = submenus for sm in submenus: all_subs |= self._get_all_submenus(sm) return all_subs def _get_all_menus_sql(self): """ Fetch all active menus with hierarchy using SQL Returns list of dicts: [ {id, parent_id} ] """ self.env.cr.execute(""" WITH RECURSIVE menu_tree AS ( SELECT id, parent_id FROM ir_ui_menu WHERE parent_id IS NULL AND active = true UNION ALL SELECT m.id, m.parent_id FROM ir_ui_menu m JOIN menu_tree mt ON mt.id = m.parent_id WHERE m.active = true ) SELECT id, parent_id FROM menu_tree ORDER BY parent_id NULLS FIRST, id """) return self.env.cr.dictfetchall() def action_generate_menus(self): """ Generate main menus and all submenus (SQL-based), and set access_line_id for every submenu. """ MenuLine = self.env['menu.access.line'] for rec in self: # Clear old menus rec.access_menu_line_ids.unlink() rec.access_sub_menu_line_ids.unlink() menus = rec._get_all_menus_sql() # Map: parent_id -> children children_map = {} for m in menus: children_map.setdefault(m['parent_id'], []).append(m) # ---------- Main menus ---------- for main in children_map.get(None, []): main_line = MenuLine.create({ 'access_control_id': rec.id, 'menu_id': main['id'], 'is_main_menu': True, }) # ---------- Submenus ---------- stack = children_map.get(main['id'], []) while stack: sm = stack.pop() MenuLine.create({ 'access_control_id': rec.id, 'menu_id': sm['id'], 'is_main_menu': True, 'access_line_id': main_line.id, }) stack.extend(children_map.get(sm['id'], [])) def action_update_menus(self): MenuLine = self.env['menu.access.line'] for rec in self: created_count = 0 # Existing menu IDs existing_menu_ids = set( rec.access_menu_line_ids.mapped('menu_id.id') + rec.access_sub_menu_line_ids.mapped('menu_id.id') ) menus = rec._get_all_menus_sql() # Map parent -> children children_map = {} for m in menus: children_map.setdefault(m['parent_id'], []).append(m) # ---------- Main menus ---------- for main in children_map.get(None, []): main_line = MenuLine.search([ ('access_control_id', '=', rec.id), ('menu_id', '=', main['id']), ('access_line_id', '=', False), ], limit=1) if not main_line: main_line = MenuLine.create({ 'access_control_id': rec.id, 'menu_id': main['id'], 'is_main_menu': True, }) created_count += 1 existing_menu_ids.add(main['id']) # ---------- Submenus ---------- stack = children_map.get(main['id'], []) while stack: sm = stack.pop() if sm['id'] not in existing_menu_ids: MenuLine.create({ 'access_control_id': rec.id, 'menu_id': sm['id'], 'is_main_menu': True, 'access_line_id': main_line.id, }) created_count += 1 existing_menu_ids.add(sm['id']) stack.extend(children_map.get(sm['id'], [])) # ---------- Notification ---------- return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Success') if created_count else _('Info'), 'message': ( _('Added %s new menu(s) (including submenus)') % created_count if created_count else _('No new menus found to add.') ), 'type': 'success' if created_count else 'info', 'sticky': False, } } class MenuAccessLine(models.Model): _name = 'menu.access.line' _description = 'Menu Access Line' _rec_name = 'menu_id' access_control_id = fields.Many2one('menu.access.control', ondelete='cascade') menu_id = fields.Many2one('ir.ui.menu', string="Menu") is_main_menu = fields.Boolean(string="Is Main Menu", default=True) parent_menu = fields.Many2one('ir.ui.menu',related='menu_id.parent_id') access_line_id = fields.Many2one('menu.access.line') control_unit = fields.Many2one( 'menu.control.units', related='access_control_id.control_unit', store=True ) 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": [("access_line_id", "=", self.id)], "context": {"default_access_line_id": self.id}, }