248 lines
8.7 KiB
Python
248 lines
8.7 KiB
Python
from odoo import api, fields, models, _
|
|
|
|
|
|
class MasterControl(models.Model):
|
|
_name = 'master.control'
|
|
_description = 'Master Control'
|
|
_order = 'sequence, name, id'
|
|
|
|
sequence = fields.Integer()
|
|
name = fields.Char(string='Master Name', required=True)
|
|
code = fields.Char(string='Code', required=True)
|
|
user_ids = fields.Many2many(
|
|
'res.users',
|
|
'master_control_res_users_rel',
|
|
'master_control_id',
|
|
'user_id',
|
|
string='Users',
|
|
)
|
|
menu_line_ids = fields.One2many(
|
|
'master.control.menu.line',
|
|
'master_control_id',
|
|
string='Menus',
|
|
domain=[('menu_id.parent_id', '=', False)],
|
|
)
|
|
submenu_line_ids = fields.One2many(
|
|
'master.control.menu.line',
|
|
'master_control_id',
|
|
string='Sub Menus',
|
|
domain=[('menu_id.parent_id', '!=', False)],
|
|
)
|
|
allowed_menu_ids = fields.Many2many(
|
|
'ir.ui.menu',
|
|
compute='_compute_allowed_menu_ids',
|
|
string='Allowed Menus',
|
|
)
|
|
|
|
_sql_constraints = [
|
|
('master_control_code_unique', 'unique(code)', 'Master code must be unique.'),
|
|
]
|
|
|
|
@api.depends('name', 'code')
|
|
def _compute_display_name(self):
|
|
for record in self:
|
|
if record.name:
|
|
record.display_name = record.name + (f' ({record.code})' if record.code else '')
|
|
else:
|
|
record.display_name = False
|
|
|
|
@api.depends('menu_line_ids.show_menu', 'menu_line_ids.menu_id', 'submenu_line_ids.show_menu', 'submenu_line_ids.menu_id')
|
|
def _compute_allowed_menu_ids(self):
|
|
for record in self:
|
|
all_lines = record.menu_line_ids | record.submenu_line_ids
|
|
visible_menu_ids = record._expand_menu_ids(all_lines.filtered('show_menu').mapped('menu_id').ids)
|
|
hidden_menu_ids = record._expand_menu_ids(all_lines.filtered(lambda line: not line.show_menu).mapped('menu_id').ids)
|
|
menus = self.env['ir.ui.menu'].browse(list(visible_menu_ids - hidden_menu_ids))
|
|
ancestors = self.env['ir.ui.menu']
|
|
for menu in menus:
|
|
parent = menu.parent_id
|
|
while parent:
|
|
ancestors |= parent
|
|
parent = parent.parent_id
|
|
record.allowed_menu_ids = menus | ancestors
|
|
|
|
def _expand_menu_ids(self, menu_ids):
|
|
if not menu_ids:
|
|
return set()
|
|
return set(
|
|
self.env['ir.ui.menu']
|
|
.sudo()
|
|
.with_context({'ir.ui.menu.full_list': True})
|
|
.search([('id', 'child_of', list(menu_ids))]).ids
|
|
)
|
|
|
|
def _get_all_menus_sql(self):
|
|
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 menu.id, menu.parent_id
|
|
FROM ir_ui_menu menu
|
|
JOIN menu_tree tree ON tree.id = menu.parent_id
|
|
WHERE menu.active = true
|
|
)
|
|
SELECT id, parent_id
|
|
FROM menu_tree
|
|
ORDER BY parent_id NULLS FIRST, id
|
|
""")
|
|
return self.env.cr.dictfetchall()
|
|
|
|
def _sync_menu_lines(self, create_missing_only=False):
|
|
line_model = self.env['master.control.menu.line']
|
|
notification_count = 0
|
|
for record in self:
|
|
menus = record._get_all_menus_sql()
|
|
children_map = {}
|
|
for menu in menus:
|
|
children_map.setdefault(menu['parent_id'], []).append(menu)
|
|
|
|
existing_lines = {
|
|
line.menu_id.id: line
|
|
for line in record.menu_line_ids | record.submenu_line_ids
|
|
}
|
|
|
|
if not create_missing_only:
|
|
(record.menu_line_ids | record.submenu_line_ids).unlink()
|
|
existing_lines = {}
|
|
|
|
for main_menu in children_map.get(None, []):
|
|
parent_line = existing_lines.get(main_menu['id'])
|
|
if not parent_line:
|
|
parent_line = line_model.create({
|
|
'master_control_id': record.id,
|
|
'menu_id': main_menu['id'],
|
|
'show_menu': True,
|
|
})
|
|
existing_lines[main_menu['id']] = parent_line
|
|
notification_count += 1
|
|
|
|
stack = list(children_map.get(main_menu['id'], []))
|
|
while stack:
|
|
submenu = stack.pop()
|
|
if submenu['id'] not in existing_lines:
|
|
line_model.create({
|
|
'master_control_id': record.id,
|
|
'menu_id': submenu['id'],
|
|
'show_menu': True,
|
|
'parent_line_id': parent_line.id,
|
|
})
|
|
existing_lines[submenu['id']] = True
|
|
notification_count += 1
|
|
stack.extend(children_map.get(submenu['id'], []))
|
|
return notification_count
|
|
|
|
def action_generate_menus(self):
|
|
self._sync_menu_lines(create_missing_only=False)
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Menus generated successfully.'),
|
|
'type': 'success',
|
|
'sticky': False,
|
|
},
|
|
}
|
|
|
|
def action_update_menus(self):
|
|
created_count = self._sync_menu_lines(create_missing_only=True)
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success') if created_count else _('Info'),
|
|
'message': _('Added %s new menu(s).') % created_count if created_count else _('No new menus found to add.'),
|
|
'type': 'success' if created_count else 'info',
|
|
'sticky': False,
|
|
},
|
|
}
|
|
|
|
@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
|
|
|
|
|
|
class MasterControlMenuLine(models.Model):
|
|
_name = 'master.control.menu.line'
|
|
_description = 'Master Control Menu Line'
|
|
_rec_name = 'menu_id'
|
|
_order = 'menu_id'
|
|
|
|
master_control_id = fields.Many2one('master.control', required=True, 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('master.control.menu.line', string='Parent Line')
|
|
|
|
_sql_constraints = [
|
|
('master_control_menu_unique', 'unique(master_control_id, menu_id)', 'Menu already exists for this master control.'),
|
|
]
|
|
|
|
def open_submenus_popup_view(self):
|
|
self.ensure_one()
|
|
return {
|
|
'name': _('Sub Menus'),
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'master.control.menu.line',
|
|
'view_mode': 'list',
|
|
'views': [
|
|
(self.env.ref('menu_control_center.view_master_submenu_line_list').id, 'list'),
|
|
],
|
|
'target': 'new',
|
|
'domain': [('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
|
|
|
|
|
|
class ResUsers(models.Model):
|
|
_inherit = 'res.users'
|
|
|
|
master_control_ids = fields.Many2many(
|
|
'master.control',
|
|
'master_control_res_users_rel',
|
|
'user_id',
|
|
'master_control_id',
|
|
string='Master Controls',
|
|
help='Masters this user is allowed to access. Updating this field also updates the Users field on the master.',
|
|
)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
records = super().create(vals_list)
|
|
if any('master_control_ids' in vals for vals in vals_list):
|
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
|
return records
|
|
|
|
def write(self, vals):
|
|
result = super().write(vals)
|
|
if 'master_control_ids' in vals:
|
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
|
return result
|