# 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)', "Only one service can exist with a specific control_unit. 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') 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 action_generate_menus(self): """ Generate main menus and all submenus (recursive), and set access_line_id for every submenu. """ for rec in self: # clear old menus rec.access_menu_line_ids.unlink() rec.access_sub_menu_line_ids.unlink() active_menus = self.env['ir.ui.menu'].search([ ('parent_id', '=', False), ('active', '=', True) ]) for menu in active_menus: # 1️⃣ Create main menu line main_line = self.env['menu.access.line'].create({ 'access_control_id': rec.id, 'menu_id': menu.id, 'is_main_menu': True, }) # 2️⃣ Fetch all recursive submenus submenus = self._get_all_submenus(menu) # 3️⃣ Create submenu lines with correct parent for sm in submenus: self.env['menu.access.line'].create({ 'access_control_id': rec.id, 'menu_id': sm.id, 'is_main_menu': True, 'access_line_id': main_line.id, # important }) def action_update_menus(self): line = self.env['menu.access.line'] menu = self.env['ir.ui.menu'] for rec in self: created_count = 0 # All existing menu IDs across BOTH One2manys existing_menu_ids = set( rec.access_menu_line_ids.mapped('menu_id.id') + rec.access_sub_menu_line_ids.mapped('menu_id.id') ) # ---------- Step 1: Ensure all top-level menus exist ---------- top_menus = menu.search([ ('parent_id', '=', False), ('active', '=', True), ]) for menu in top_menus: # Create missing MAIN MENU main_line = line.search([ ('access_control_id', '=', rec.id), ('menu_id', '=', menu.id), ('access_line_id', '=', False) ], limit=1) if not main_line: main_line = line.create({ 'access_control_id': rec.id, 'menu_id': menu.id, 'is_main_menu': True, }) created_count += 1 existing_menu_ids.add(menu.id) # ---------- Step 2: Ensure all SUBMENUS exist ---------- submenus = rec._get_all_submenus(menu) for sm in submenus: # If submenu is missing → create it if sm.id not in existing_menu_ids: line.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) # ---------- Notification ---------- if created_count: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Success'), 'message': _('Added %s new menu(s) (including submenus)') % created_count, 'type': 'success', 'sticky': False, } } else: return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Info'), 'message': _('No new menus found to add.'), 'type': '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}, }