odoo18/addons_extensions/menu_control_center/models/models.py

229 lines
7.8 KiB
Python

# 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},
}