Menu Control Center Functionality change
This commit is contained in:
parent
e2d6a8c417
commit
b0ec5ee508
|
|
@ -3,7 +3,7 @@ from odoo.http import request
|
||||||
from odoo.addons.web.controllers.home import Home
|
from odoo.addons.web.controllers.home import Home
|
||||||
|
|
||||||
|
|
||||||
class CustomMasterLogin(Home):
|
class CustomMasterLogin(Home):
|
||||||
|
|
||||||
@http.route()
|
@http.route()
|
||||||
def web_login(self, *args, **kw):
|
def web_login(self, *args, **kw):
|
||||||
|
|
@ -17,19 +17,19 @@ class CustomMasterLogin(Home):
|
||||||
request.env['ir.ui.menu'].sudo().clear_caches()
|
request.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
request.env['ir.ui.menu'].sudo()._visible_menu_ids()
|
request.env['ir.ui.menu'].sudo()._visible_menu_ids()
|
||||||
|
|
||||||
if request.session.uid and master_selected:
|
if request.session.uid and master_selected:
|
||||||
user = request.env.user
|
user = request.env.user
|
||||||
master = request.env['master.control'].sudo().search(
|
master = request.env['master.control'].sudo().search(
|
||||||
[('code', '=', master_selected)], limit=1
|
[('code', '=', master_selected)], limit=1
|
||||||
)
|
)
|
||||||
|
|
||||||
if master.exists() and master.access_group_ids:
|
if master.exists() and master.user_ids:
|
||||||
if not (user.groups_id & master.access_group_ids):
|
if user not in master.user_ids:
|
||||||
request.session.logout(keep_db=True)
|
request.session.logout(keep_db=True)
|
||||||
|
|
||||||
# Create a response with JavaScript alert
|
# Create a response with JavaScript alert
|
||||||
html = f"""
|
html = f"""
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script>
|
<script>
|
||||||
alert("{_("You don't have access to login to '%s'. Please contact the administrator.") % master.display_name}");
|
alert("{_("You don't have access to login to '%s'. Please contact the administrator.") % master.display_name}");
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,4 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="1"/>
|
||||||
<record id="hr_unit_menu_control" model="menu.control.units">
|
</odoo>
|
||||||
<field name="unit_name">Human Resources</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="admin_unit_menu_control" model="menu.control.units">
|
|
||||||
<field name="unit_name">Administration</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="developer_unit_menu_control" model="menu.control.units">
|
|
||||||
<field name="unit_name">Development</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="testing_unit_menu_control" model="menu.control.units">
|
|
||||||
<field name="unit_name">Quality Assurance</field>
|
|
||||||
</record>
|
|
||||||
<record id="it_support_menu_control" model="menu.control.units">
|
|
||||||
<field name="unit_name">IT Support</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="finance_unit_menu_control" model="menu.control.units">
|
|
||||||
<field name="unit_name">FINANCE</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</odoo>
|
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
from odoo import api, fields, models
|
|
||||||
from odoo.http import request
|
|
||||||
|
|
||||||
|
|
||||||
class ResGroups(models.Model):
|
|
||||||
_inherit = 'res.groups'
|
|
||||||
|
|
||||||
is_visible_for_master = fields.Boolean(
|
|
||||||
compute='_compute_is_visible_for_master',
|
|
||||||
search='_search_is_visible_for_master'
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.depends_context('active_master')
|
|
||||||
def _compute_is_visible_for_master(self):
|
|
||||||
master_code = request.session.active_master
|
|
||||||
if not master_code:
|
|
||||||
self.update({'is_visible_for_master': True})
|
|
||||||
return
|
|
||||||
|
|
||||||
master_control = self.env['master.control'].sudo().search([
|
|
||||||
('code', '=', master_code)
|
|
||||||
], limit=1)
|
|
||||||
|
|
||||||
if not master_control:
|
|
||||||
self.update({'is_visible_for_master': True})
|
|
||||||
return
|
|
||||||
|
|
||||||
# If NO access_group_ids -> show ALL groups
|
|
||||||
if not master_control.access_group_ids:
|
|
||||||
self.update({'is_visible_for_master': True})
|
|
||||||
return
|
|
||||||
|
|
||||||
visible_group_ids = master_control.access_group_ids.filtered(
|
|
||||||
lambda line: line.show_group
|
|
||||||
).mapped('group_id').ids
|
|
||||||
|
|
||||||
# If there are no 'show_group = True' -> show ALL groups
|
|
||||||
if not visible_group_ids:
|
|
||||||
self.update({'is_visible_for_master': True})
|
|
||||||
return
|
|
||||||
|
|
||||||
for group in self:
|
|
||||||
group.is_visible_for_master = group.id in visible_group_ids
|
|
||||||
|
|
||||||
|
|
||||||
def _search_is_visible_for_master(self, operator, value):
|
|
||||||
if operator != '=' or not value:
|
|
||||||
return []
|
|
||||||
|
|
||||||
master_code = request.session.active_master
|
|
||||||
if not master_code:
|
|
||||||
return []
|
|
||||||
|
|
||||||
master_control = self.env['master.control'].sudo().search([
|
|
||||||
('code', '=', master_code)
|
|
||||||
], limit=1)
|
|
||||||
|
|
||||||
if not master_control:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# If NO access_group_ids → show ALL groups → no domain filter
|
|
||||||
if not master_control.access_group_ids:
|
|
||||||
return []
|
|
||||||
|
|
||||||
visible_group_ids = master_control.access_group_ids.filtered(
|
|
||||||
lambda line: line.show_group
|
|
||||||
).mapped('group_id').ids
|
|
||||||
|
|
||||||
# If none of the lines have show_group=True → show ALL
|
|
||||||
if not visible_group_ids:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return [('id', 'in', visible_group_ids)]
|
|
||||||
|
|
@ -1,103 +1,247 @@
|
||||||
from odoo import models, fields, _, api
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
class MasterControl(models.Model):
|
|
||||||
_name = 'master.control'
|
class MasterControl(models.Model):
|
||||||
_description = 'Master Control'
|
_name = 'master.control'
|
||||||
|
_description = 'Master Control'
|
||||||
sequence = fields.Integer()
|
_order = 'sequence, name, id'
|
||||||
name = fields.Char(string='Master Name', required=True)
|
|
||||||
code = fields.Char(string='Code', required=True)
|
sequence = fields.Integer()
|
||||||
access_group_ids = fields.Many2many('res.groups',string='Roles')
|
name = fields.Char(string='Master Name', required=True)
|
||||||
|
code = fields.Char(string='Code', required=True)
|
||||||
@api.depends('name', 'code')
|
user_ids = fields.Many2many(
|
||||||
def _compute_display_name(self):
|
'res.users',
|
||||||
for record in self:
|
'master_control_res_users_rel',
|
||||||
if record.name:
|
'master_control_id',
|
||||||
record.display_name = record.name + (f' ({record.code})' if record.code else '')
|
'user_id',
|
||||||
else:
|
string='Users',
|
||||||
record.display_name = False
|
)
|
||||||
|
menu_line_ids = fields.One2many(
|
||||||
# def action_generate_groups(self):
|
'master.control.menu.line',
|
||||||
# """Generate category → groups list"""
|
'master_control_id',
|
||||||
# for rec in self:
|
string='Menus',
|
||||||
#
|
domain=[('menu_id.parent_id', '=', False)],
|
||||||
# # Clear old groups
|
)
|
||||||
# rec.access_group_ids.unlink()
|
submenu_line_ids = fields.One2many(
|
||||||
#
|
'master.control.menu.line',
|
||||||
# groups = self.env['res.groups'].sudo().search([],order='category_id')
|
'master_control_id',
|
||||||
# show_group = True if rec.default_show else False
|
string='Sub Menus',
|
||||||
# print(show_group)
|
domain=[('menu_id.parent_id', '!=', False)],
|
||||||
# for grp in groups:
|
)
|
||||||
# self.env['group.access.line'].create({
|
allowed_menu_ids = fields.Many2many(
|
||||||
# 'master_control_id': rec.id,
|
'ir.ui.menu',
|
||||||
# 'group_id': grp.id,
|
compute='_compute_allowed_menu_ids',
|
||||||
# 'show_group': show_group,
|
string='Allowed Menus',
|
||||||
# })
|
)
|
||||||
#
|
|
||||||
# return {
|
_sql_constraints = [
|
||||||
# 'type': 'ir.actions.client',
|
('master_control_code_unique', 'unique(code)', 'Master code must be unique.'),
|
||||||
# 'tag': 'display_notification',
|
]
|
||||||
# 'params': {
|
|
||||||
# 'title': _('Success'),
|
@api.depends('name', 'code')
|
||||||
# 'message': _('Groups generated successfully!'),
|
def _compute_display_name(self):
|
||||||
# 'type': 'success',
|
for record in self:
|
||||||
# }
|
if record.name:
|
||||||
# }
|
record.display_name = record.name + (f' ({record.code})' if record.code else '')
|
||||||
#
|
else:
|
||||||
# # -----------------------------------------
|
record.display_name = False
|
||||||
# # UPDATE GROUPS (Detect new groups)
|
|
||||||
# # -----------------------------------------
|
@api.depends('menu_line_ids.show_menu', 'menu_line_ids.menu_id', 'submenu_line_ids.show_menu', 'submenu_line_ids.menu_id')
|
||||||
# def action_update_groups(self):
|
def _compute_allowed_menu_ids(self):
|
||||||
# import pdb
|
for record in self:
|
||||||
# pdb.set_trace()
|
all_lines = record.menu_line_ids | record.submenu_line_ids
|
||||||
# for rec in self:
|
visible_menu_ids = record._expand_menu_ids(all_lines.filtered('show_menu').mapped('menu_id').ids)
|
||||||
# created_count = 0
|
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))
|
||||||
# existing_ids = set(rec.access_group_ids.mapped('group_id.id'))
|
ancestors = self.env['ir.ui.menu']
|
||||||
#
|
for menu in menus:
|
||||||
# categories = self.env['ir.module.category'].search([])
|
parent = menu.parent_id
|
||||||
#
|
while parent:
|
||||||
# for category in categories:
|
ancestors |= parent
|
||||||
# groups = self.env['res.groups'].search([
|
parent = parent.parent_id
|
||||||
# ('category_id', '=', category.id)
|
record.allowed_menu_ids = menus | ancestors
|
||||||
# ])
|
|
||||||
#
|
def _expand_menu_ids(self, menu_ids):
|
||||||
# for grp in groups:
|
if not menu_ids:
|
||||||
# # create only missing group
|
return set()
|
||||||
# if grp.id not in existing_ids:
|
return set(
|
||||||
# rec.access_group_ids.create({
|
self.env['ir.ui.menu']
|
||||||
# 'master_control_id': rec.id,
|
.sudo()
|
||||||
# 'category_id': category.id,
|
.with_context({'ir.ui.menu.full_list': True})
|
||||||
# 'group_id': grp.id,
|
.search([('id', 'child_of', list(menu_ids))]).ids
|
||||||
# 'show_group': True if rec.default_show else False,
|
)
|
||||||
# })
|
|
||||||
# created_count += 1
|
def _get_all_menus_sql(self):
|
||||||
# existing_ids.add(grp.id)
|
self.env.cr.execute("""
|
||||||
#
|
WITH RECURSIVE menu_tree AS (
|
||||||
# if created_count:
|
SELECT id, parent_id
|
||||||
# message = f"Added {created_count} new groups."
|
FROM ir_ui_menu
|
||||||
# msg_type = "success"
|
WHERE parent_id IS NULL
|
||||||
# else:
|
AND active = true
|
||||||
# message = "No new groups found."
|
|
||||||
# msg_type = "info"
|
UNION ALL
|
||||||
#
|
|
||||||
# return {
|
SELECT menu.id, menu.parent_id
|
||||||
# 'type': 'ir.actions.client',
|
FROM ir_ui_menu menu
|
||||||
# 'tag': 'display_notification',
|
JOIN menu_tree tree ON tree.id = menu.parent_id
|
||||||
# 'params': {
|
WHERE menu.active = true
|
||||||
# 'title': _('Group Update'),
|
)
|
||||||
# 'message': _(message),
|
SELECT id, parent_id
|
||||||
# 'type': msg_type,
|
FROM menu_tree
|
||||||
# }
|
ORDER BY parent_id NULLS FIRST, id
|
||||||
# }
|
""")
|
||||||
|
return self.env.cr.dictfetchall()
|
||||||
#
|
|
||||||
# class GroupsAccessLine(models.Model):
|
def _sync_menu_lines(self, create_missing_only=False):
|
||||||
# _name = 'group.access.line'
|
line_model = self.env['master.control.menu.line']
|
||||||
# _description = 'Group Access Line'
|
notification_count = 0
|
||||||
# _rec_name = 'group_id'
|
for record in self:
|
||||||
#
|
menus = record._get_all_menus_sql()
|
||||||
# category_id = fields.Many2one('ir.module.category', related='group_id.category_id')
|
children_map = {}
|
||||||
# group_id = fields.Many2one('res.groups', string="Role")
|
for menu in menus:
|
||||||
# show_group = fields.Boolean(string="Show", default=True)
|
children_map.setdefault(menu['parent_id'], []).append(menu)
|
||||||
# master_control_id = fields.Many2one('master.control')
|
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,132 +1,154 @@
|
||||||
from passlib.apps import master_context
|
from collections import defaultdict
|
||||||
|
|
||||||
from odoo import models, fields, api, tools, _
|
from odoo import api, models, tools
|
||||||
from collections import defaultdict
|
from odoo.http import request
|
||||||
from odoo.http import request
|
|
||||||
|
|
||||||
class IrUiMenu(models.Model):
|
class IrUiMenu(models.Model):
|
||||||
_inherit = 'ir.ui.menu'
|
_inherit = 'ir.ui.menu'
|
||||||
|
|
||||||
@api.model
|
def _get_active_master_code(self):
|
||||||
@tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug')
|
return (request.session.get('active_master') if request else False) or self.env.context.get('active_master')
|
||||||
def _visible_menu_ids(self, debug=False):
|
|
||||||
"""Return the IDs of menu items visible to the current user based on permissions and active master."""
|
@api.model
|
||||||
# Clear existing cache to ensure fresh menu visibility calculation
|
@tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug', 'self.env.uid', 'self._get_active_master_code() or ""')
|
||||||
self.env['ir.ui.menu'].sudo().clear_caches()
|
def _visible_menu_ids(self, debug=False):
|
||||||
|
context = {'ir.ui.menu.full_list': True}
|
||||||
# Retrieve all menus with required fields
|
menus = self.with_context(context).search_fetch([], ['action', 'parent_id']).sudo()
|
||||||
context = {'ir.ui.menu.full_list': True}
|
|
||||||
menus = self.with_context(context).search_fetch([], ['action', 'parent_id']).sudo()
|
active_master_code = self._get_active_master_code()
|
||||||
|
master_control = self.env['master.control'].sudo().search([('code', '=', active_master_code)], limit=1) if active_master_code else False
|
||||||
# Get active master and control configuration
|
|
||||||
active_master = request.session.get('active_master')
|
group_ids = set(self.env.user._get_group_ids())
|
||||||
master_control = False
|
if not debug:
|
||||||
control_unit = False
|
group_ids -= {
|
||||||
|
self.env['ir.model.data']._xmlid_to_res_id('base.group_no_one', raise_if_not_found=False)
|
||||||
if active_master:
|
}
|
||||||
master_control = self.env['master.control'].sudo().search(
|
|
||||||
[('code', '=', active_master)], limit=1
|
menus = menus.filtered(
|
||||||
)
|
lambda menu: not (menu.groups_id and group_ids.isdisjoint(menu.groups_id._ids))
|
||||||
if master_control:
|
)
|
||||||
control_unit = self.env['menu.access.control'].sudo().search([
|
|
||||||
('user_ids', 'ilike', self.env.user.id),
|
if master_control:
|
||||||
('master_control', '=', master_control.id)
|
if master_control.user_ids and self.env.user not in master_control.user_ids:
|
||||||
])
|
menus = self.browse()
|
||||||
|
else:
|
||||||
# Get user groups and exclude technical group in non-debug mode
|
hidden_menu_ids = self._get_master_hidden_menu_ids(master_control) | self._get_hidden_menu_ids(master_control)
|
||||||
group_ids = set(self.env.user._get_group_ids())
|
if hidden_menu_ids:
|
||||||
if not debug:
|
menus = menus.filtered(lambda menu: menu.id not in hidden_menu_ids)
|
||||||
group_ids -= {
|
|
||||||
self.env['ir.model.data']._xmlid_to_res_id(
|
visible = self._process_action_menus(menus)
|
||||||
'base.group_no_one', raise_if_not_found=False
|
return set(visible.ids)
|
||||||
)
|
|
||||||
}
|
@api.model
|
||||||
|
def load_menus_root(self):
|
||||||
# Filter menus by group permissions
|
root = super().load_menus_root()
|
||||||
menus = menus.filtered(
|
visible_ids = self._visible_menu_ids(request.session.debug if request else False)
|
||||||
lambda menu: not (menu.groups_id and group_ids.isdisjoint(menu.groups_id._ids))
|
root['children'] = [
|
||||||
)
|
child for child in root.get('children', [])
|
||||||
|
if child['id'] in visible_ids
|
||||||
# Determine menus to hide based on access control
|
]
|
||||||
hide_menus_list = self._get_hidden_menu_ids(control_unit, master_control, debug)
|
root['all_menu_ids'] = [
|
||||||
menus = menus.filtered(lambda menu: menu.id not in hide_menus_list)
|
menu_id for menu_id in root.get('all_menu_ids', [])
|
||||||
|
if menu_id in visible_ids
|
||||||
# Process menus with actions
|
]
|
||||||
visible = self._process_action_menus(menus)
|
return root
|
||||||
|
|
||||||
return set(visible.ids)
|
@api.model
|
||||||
|
def load_menus(self, debug):
|
||||||
def _get_hidden_menu_ids(self, control_unit, master_control, debug):
|
all_menus = super().load_menus(debug)
|
||||||
"""Helper method to determine menu IDs that should be hidden from the user."""
|
visible_ids = self._visible_menu_ids(debug)
|
||||||
if debug and control_unit:
|
|
||||||
# In debug mode with control unit, use its specific menu restrictions
|
filtered_menus = {'root': dict(all_menus['root'])}
|
||||||
parent_menus = control_unit.access_menu_line_ids.filtered(
|
for menu_id, menu_data in all_menus.items():
|
||||||
lambda menu: not menu.is_main_menu
|
if menu_id == 'root':
|
||||||
).menu_id.ids
|
continue
|
||||||
sub_menus = control_unit.access_sub_menu_line_ids.filtered(
|
if menu_id in visible_ids:
|
||||||
lambda menu: not menu.is_main_menu
|
filtered_menus[menu_id] = dict(menu_data)
|
||||||
).menu_id.ids
|
|
||||||
elif not debug:
|
filtered_menus['root']['children'] = [
|
||||||
# In non-debug mode, determine menus to hide based on control configuration
|
child_id for child_id in filtered_menus['root'].get('children', [])
|
||||||
domain = [('user_ids', 'ilike', self.env.user.id)]
|
if child_id in filtered_menus
|
||||||
if master_control:
|
]
|
||||||
domain.append(('master_control', '=', master_control.id))
|
|
||||||
|
for menu_id, menu_data in list(filtered_menus.items()):
|
||||||
access_controls = self.env['menu.access.control'].sudo().search(domain)
|
if menu_id == 'root':
|
||||||
parent_menus = access_controls.access_menu_line_ids.filtered(
|
continue
|
||||||
lambda menu: not menu.is_main_menu
|
menu_data['children'] = [
|
||||||
).menu_id.ids
|
child_id for child_id in menu_data.get('children', [])
|
||||||
sub_menus = access_controls.access_sub_menu_line_ids.filtered(
|
if child_id in filtered_menus
|
||||||
lambda menu: not menu.is_main_menu
|
]
|
||||||
).menu_id.ids
|
|
||||||
else:
|
return filtered_menus
|
||||||
# Default case: no menus to hide
|
|
||||||
return []
|
def _get_hidden_menu_ids(self, master_control):
|
||||||
|
access_controls = self.env['menu.access.control'].sudo().search([
|
||||||
return list(set(parent_menus + sub_menus))
|
('master_control_id', '=', master_control.id),
|
||||||
|
('user_ids', 'in', self.env.user.id),
|
||||||
def _process_action_menus(self, menus):
|
])
|
||||||
"""Process menus with actions and determine visibility based on model access."""
|
hidden_lines = (access_controls.access_menu_line_ids | access_controls.access_sub_menu_line_ids).filtered(
|
||||||
# Separate menus with actions from folder menus
|
lambda line: not line.show_menu
|
||||||
actions_by_model = defaultdict(set)
|
)
|
||||||
for action in menus.mapped('action'):
|
hidden_menu_ids = hidden_lines.mapped('menu_id').ids
|
||||||
if action:
|
if not hidden_menu_ids:
|
||||||
actions_by_model[action._name].add(action.id)
|
return set()
|
||||||
|
return set(
|
||||||
existing_actions = {
|
self.env['ir.ui.menu']
|
||||||
action
|
.sudo()
|
||||||
for model_name, action_ids in actions_by_model.items()
|
.with_context({'ir.ui.menu.full_list': True})
|
||||||
for action in self.env[model_name].browse(action_ids).exists()
|
.search([('id', 'child_of', hidden_menu_ids)]).ids
|
||||||
}
|
)
|
||||||
|
|
||||||
action_menus = menus.filtered(lambda m: m.action and m.action in existing_actions)
|
def _get_master_hidden_menu_ids(self, master_control):
|
||||||
folder_menus = menus - action_menus
|
hidden_lines = (master_control.menu_line_ids | master_control.submenu_line_ids).filtered(
|
||||||
visible = self.browse()
|
lambda line: not line.show_menu
|
||||||
|
)
|
||||||
# Model access check configuration
|
hidden_menu_ids = hidden_lines.mapped('menu_id').ids
|
||||||
access = self.env['ir.model.access']
|
if not hidden_menu_ids:
|
||||||
MODEL_BY_TYPE = {
|
return set()
|
||||||
'ir.actions.act_window': 'res_model',
|
return set(
|
||||||
'ir.actions.report': 'model',
|
self.env['ir.ui.menu']
|
||||||
'ir.actions.server': 'model_name',
|
.sudo()
|
||||||
}
|
.with_context({'ir.ui.menu.full_list': True})
|
||||||
|
.search([('id', 'child_of', hidden_menu_ids)]).ids
|
||||||
# Prefetch action data for performance
|
)
|
||||||
prefetch_ids = defaultdict(list)
|
|
||||||
for action in action_menus.mapped('action'):
|
def _process_action_menus(self, menus):
|
||||||
prefetch_ids[action._name].append(action.id)
|
actions_by_model = defaultdict(set)
|
||||||
|
for action in menus.mapped('action'):
|
||||||
# Check access for each action menu
|
if action:
|
||||||
for menu in action_menus:
|
actions_by_model[action._name].add(action.id)
|
||||||
action = menu.action.with_prefetch(prefetch_ids[action._name])
|
|
||||||
model_name = action._name in MODEL_BY_TYPE and action[MODEL_BY_TYPE[action._name]]
|
existing_actions = {
|
||||||
|
action
|
||||||
if not model_name or access.check(model_name, 'read', False):
|
for model_name, action_ids in actions_by_model.items()
|
||||||
# Make menu visible and its folder ancestors
|
for action in self.env[model_name].browse(action_ids).exists()
|
||||||
visible += menu
|
}
|
||||||
parent = menu.parent_id
|
|
||||||
while parent and parent in folder_menus and parent not in visible:
|
action_menus = menus.filtered(lambda menu: menu.action and menu.action in existing_actions)
|
||||||
visible += parent
|
folder_menus = menus - action_menus
|
||||||
parent = parent.parent_id
|
visible = self.browse()
|
||||||
|
|
||||||
return visible
|
access_model = self.env['ir.model.access']
|
||||||
|
model_by_type = {
|
||||||
|
'ir.actions.act_window': 'res_model',
|
||||||
|
'ir.actions.report': 'model',
|
||||||
|
'ir.actions.server': 'model_name',
|
||||||
|
}
|
||||||
|
|
||||||
|
prefetch_ids = defaultdict(list)
|
||||||
|
for action in action_menus.mapped('action'):
|
||||||
|
prefetch_ids[action._name].append(action.id)
|
||||||
|
|
||||||
|
for menu in action_menus:
|
||||||
|
action = menu.action.with_prefetch(prefetch_ids[action._name])
|
||||||
|
model_name = action._name in model_by_type and action[model_by_type[action._name]]
|
||||||
|
|
||||||
|
if not model_name or access_model.check(model_name, 'read', False):
|
||||||
|
visible += menu
|
||||||
|
parent = menu.parent_id
|
||||||
|
while parent and parent in folder_menus and parent not in visible:
|
||||||
|
visible += parent
|
||||||
|
parent = parent.parent_id
|
||||||
|
|
||||||
|
return visible
|
||||||
|
|
|
||||||
|
|
@ -1,228 +1,192 @@
|
||||||
# models/models.py
|
from odoo import api, fields, models, _
|
||||||
from odoo import models, fields, api, tools, _
|
from odoo.exceptions import ValidationError
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
class MenuControlUnits(models.Model):
|
class MenuAccessControl(models.Model):
|
||||||
_name = 'menu.control.units'
|
_name = 'menu.access.control'
|
||||||
_rec_name = 'unit_name'
|
_description = 'Menu Access Control'
|
||||||
|
_rec_name = 'master_control_id'
|
||||||
_sql_constraints = [
|
|
||||||
('unique_unit_name', 'UNIQUE(unit_name)', "'Unit Name' already defined. Please don't confuse me 😤.")
|
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(
|
||||||
unit_name = fields.Char(string='Unit Name',required=True)
|
'res.users',
|
||||||
department_ids = fields.Many2many('hr.department')
|
compute='_compute_available_user_ids',
|
||||||
|
string='Available Users',
|
||||||
user_ids = fields.Many2many('res.users')
|
)
|
||||||
|
user_ids = fields.Many2many(
|
||||||
def generate_department_user_ids(self):
|
'res.users',
|
||||||
for rec in self:
|
'menu_access_control_res_users_rel',
|
||||||
user_ids = self.env['hr.employee'].sudo().search([('department_id','in',rec.department_ids.ids)]).user_id.ids
|
'access_control_id',
|
||||||
self.write({
|
'user_id',
|
||||||
'user_ids': [(6, 0, user_ids)]
|
string='Users',
|
||||||
})
|
)
|
||||||
|
access_menu_line_ids = fields.One2many(
|
||||||
class MenuAccessControl(models.Model):
|
'menu.access.line',
|
||||||
_name = 'menu.access.control'
|
'access_control_id',
|
||||||
_description = 'Menu Access Control'
|
string='Menus',
|
||||||
_rec_name = 'control_unit'
|
domain=[('menu_id.parent_id', '=', False)],
|
||||||
|
)
|
||||||
_sql_constraints = [
|
access_sub_menu_line_ids = fields.One2many(
|
||||||
('unique_control_unit', 'UNIQUE(control_unit, master_control)', "Only one service can exist with a specific control_unit & Master. Please don't confuse me 🤪.")
|
'menu.access.line',
|
||||||
]
|
'access_control_id',
|
||||||
|
string='Sub Menus',
|
||||||
control_unit = fields.Many2one('menu.control.units',required=True)
|
domain=[('menu_id.parent_id', '!=', False)],
|
||||||
user_ids = fields.Many2many('res.users', string="Users", related='control_unit.user_ids')
|
)
|
||||||
master_control = fields.Many2one('master.control')
|
|
||||||
|
@api.depends('master_control_id', 'master_control_id.user_ids')
|
||||||
|
def _compute_available_user_ids(self):
|
||||||
access_menu_line_ids = fields.One2many(
|
for record in self:
|
||||||
'menu.access.line', 'access_control_id',
|
record.available_user_ids = record.master_control_id.user_ids
|
||||||
string="Accessible Menus", domain=[('menu_id.parent_id','=',False)]
|
|
||||||
)
|
@api.onchange('master_control_id')
|
||||||
|
def _onchange_master_control_id(self):
|
||||||
access_sub_menu_line_ids = fields.One2many('menu.access.line', 'access_control_id',
|
for record in self:
|
||||||
string="Accessible Menus", domain=[('menu_id.parent_id','!=',False)]
|
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()
|
||||||
def _get_all_submenus(self, menu):
|
|
||||||
"""Returns all submenus recursively for a given menu."""
|
@api.onchange('department_ids')
|
||||||
submenus = self.env['ir.ui.menu'].search([('parent_id', '=', menu.id), ('active', '=', True)])
|
def _onchange_department_ids(self):
|
||||||
all_subs = submenus
|
self._add_department_users()
|
||||||
for sm in submenus:
|
|
||||||
all_subs |= self._get_all_submenus(sm)
|
def _add_department_users(self):
|
||||||
return all_subs
|
for record in self:
|
||||||
|
if not record.master_control_id:
|
||||||
def _get_all_menus_sql(self):
|
record.user_ids = [(5, 0, 0)]
|
||||||
"""
|
continue
|
||||||
Fetch all active menus with hierarchy using SQL
|
allowed_users = record.master_control_id.user_ids
|
||||||
Returns list of dicts:
|
department_users = self.env['hr.employee'].sudo().search([
|
||||||
[
|
('department_id', 'in', record.department_ids.ids),
|
||||||
{id, parent_id}
|
('user_id', '!=', False),
|
||||||
]
|
]).mapped('user_id')
|
||||||
"""
|
record.user_ids = (record.user_ids.filtered(lambda user: user in allowed_users) | (department_users & allowed_users))
|
||||||
self.env.cr.execute("""
|
|
||||||
WITH RECURSIVE menu_tree AS (
|
def _sync_menu_lines_from_master(self):
|
||||||
SELECT id, parent_id
|
line_model = self.env['menu.access.line']
|
||||||
FROM ir_ui_menu
|
for record in self:
|
||||||
WHERE parent_id IS NULL
|
master_lines = (record.master_control_id.menu_line_ids | record.master_control_id.submenu_line_ids).filtered('show_menu')
|
||||||
AND active = true
|
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)
|
||||||
UNION ALL
|
|
||||||
|
for line in (record.access_menu_line_ids | record.access_sub_menu_line_ids):
|
||||||
SELECT m.id, m.parent_id
|
if line.menu_id.id not in valid_menu_ids:
|
||||||
FROM ir_ui_menu m
|
line.unlink()
|
||||||
JOIN menu_tree mt ON mt.id = m.parent_id
|
|
||||||
WHERE m.active = true
|
refreshed_lines = {line.menu_id.id: line for line in record.access_menu_line_ids | record.access_sub_menu_line_ids}
|
||||||
)
|
parent_lines = {}
|
||||||
SELECT id, parent_id
|
for master_line in master_lines.filtered(lambda line: not line.menu_id.parent_id):
|
||||||
FROM menu_tree
|
access_line = refreshed_lines.get(master_line.menu_id.id)
|
||||||
ORDER BY parent_id NULLS FIRST, id
|
if not access_line:
|
||||||
""")
|
access_line = line_model.create({
|
||||||
return self.env.cr.dictfetchall()
|
'access_control_id': record.id,
|
||||||
|
'menu_id': master_line.menu_id.id,
|
||||||
def action_generate_menus(self):
|
'show_menu': True,
|
||||||
"""
|
})
|
||||||
Generate main menus and all submenus (SQL-based),
|
parent_lines[master_line.menu_id.id] = access_line
|
||||||
and set access_line_id for every submenu.
|
|
||||||
"""
|
for master_line in master_lines.filtered(lambda line: line.menu_id.parent_id):
|
||||||
MenuLine = self.env['menu.access.line']
|
parent_root_menu = master_line.menu_id
|
||||||
|
while parent_root_menu.parent_id:
|
||||||
for rec in self:
|
parent_root_menu = parent_root_menu.parent_id
|
||||||
# Clear old menus
|
parent_line = parent_lines.get(parent_root_menu.id)
|
||||||
rec.access_menu_line_ids.unlink()
|
access_line = refreshed_lines.get(master_line.menu_id.id)
|
||||||
rec.access_sub_menu_line_ids.unlink()
|
if not access_line:
|
||||||
|
line_model.create({
|
||||||
menus = rec._get_all_menus_sql()
|
'access_control_id': record.id,
|
||||||
|
'menu_id': master_line.menu_id.id,
|
||||||
# Map: parent_id -> children
|
'show_menu': True,
|
||||||
children_map = {}
|
'parent_line_id': parent_line.id if parent_line else False,
|
||||||
for m in menus:
|
})
|
||||||
children_map.setdefault(m['parent_id'], []).append(m)
|
|
||||||
|
@api.model_create_multi
|
||||||
# ---------- Main menus ----------
|
def create(self, vals_list):
|
||||||
for main in children_map.get(None, []):
|
records = super().create(vals_list)
|
||||||
|
records._sync_menu_lines_from_master()
|
||||||
main_line = MenuLine.create({
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
'access_control_id': rec.id,
|
return records
|
||||||
'menu_id': main['id'],
|
|
||||||
'is_main_menu': True,
|
def write(self, vals):
|
||||||
})
|
result = super().write(vals)
|
||||||
|
if {'master_control_id', 'department_ids'} & set(vals):
|
||||||
# ---------- Submenus ----------
|
self._sync_menu_lines_from_master()
|
||||||
stack = children_map.get(main['id'], [])
|
self._validate_users_belong_to_master()
|
||||||
while stack:
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
sm = stack.pop()
|
return result
|
||||||
MenuLine.create({
|
|
||||||
'access_control_id': rec.id,
|
@api.constrains('master_control_id', 'user_ids')
|
||||||
'menu_id': sm['id'],
|
def _validate_users_belong_to_master(self):
|
||||||
'is_main_menu': True,
|
for record in self:
|
||||||
'access_line_id': main_line.id,
|
allowed_users = record.master_control_id.user_ids
|
||||||
})
|
invalid_users = record.user_ids.filtered(lambda user: user not in allowed_users)
|
||||||
stack.extend(children_map.get(sm['id'], []))
|
if invalid_users:
|
||||||
|
raise ValidationError(
|
||||||
def action_update_menus(self):
|
_('Users in Menu Access Control must belong to the selected Master Control.')
|
||||||
MenuLine = self.env['menu.access.line']
|
)
|
||||||
|
|
||||||
for rec in self:
|
def action_refresh_from_master(self):
|
||||||
created_count = 0
|
self._sync_menu_lines_from_master()
|
||||||
|
return {
|
||||||
# Existing menu IDs
|
'type': 'ir.actions.client',
|
||||||
existing_menu_ids = set(
|
'tag': 'display_notification',
|
||||||
rec.access_menu_line_ids.mapped('menu_id.id') +
|
'params': {
|
||||||
rec.access_sub_menu_line_ids.mapped('menu_id.id')
|
'title': _('Success'),
|
||||||
)
|
'message': _('Menus refreshed from Master Control.'),
|
||||||
|
'type': 'success',
|
||||||
menus = rec._get_all_menus_sql()
|
'sticky': False,
|
||||||
|
},
|
||||||
# Map parent -> children
|
}
|
||||||
children_map = {}
|
|
||||||
for m in menus:
|
def unlink(self):
|
||||||
children_map.setdefault(m['parent_id'], []).append(m)
|
result = super().unlink()
|
||||||
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
# ---------- Main menus ----------
|
return result
|
||||||
for main in children_map.get(None, []):
|
|
||||||
|
|
||||||
main_line = MenuLine.search([
|
class MenuAccessLine(models.Model):
|
||||||
('access_control_id', '=', rec.id),
|
_name = 'menu.access.line'
|
||||||
('menu_id', '=', main['id']),
|
_description = 'Menu Access Line'
|
||||||
('access_line_id', '=', False),
|
_rec_name = 'menu_id'
|
||||||
], limit=1)
|
_order = 'menu_id'
|
||||||
|
|
||||||
if not main_line:
|
access_control_id = fields.Many2one('menu.access.control', ondelete='cascade')
|
||||||
main_line = MenuLine.create({
|
menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True)
|
||||||
'access_control_id': rec.id,
|
show_menu = fields.Boolean(string='Show Menu', default=True)
|
||||||
'menu_id': main['id'],
|
parent_menu_id = fields.Many2one('ir.ui.menu', related='menu_id.parent_id', string='Parent Menu', store=True)
|
||||||
'is_main_menu': 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)
|
||||||
created_count += 1
|
|
||||||
existing_menu_ids.add(main['id'])
|
_sql_constraints = [
|
||||||
|
('menu_access_line_unique', 'unique(access_control_id, menu_id)', 'Menu already exists in this access control.'),
|
||||||
# ---------- Submenus ----------
|
]
|
||||||
stack = children_map.get(main['id'], [])
|
|
||||||
while stack:
|
def open_submenus_popup_view(self):
|
||||||
sm = stack.pop()
|
self.ensure_one()
|
||||||
|
return {
|
||||||
if sm['id'] not in existing_menu_ids:
|
'name': _('Sub Menus'),
|
||||||
MenuLine.create({
|
'type': 'ir.actions.act_window',
|
||||||
'access_control_id': rec.id,
|
'res_model': 'menu.access.line',
|
||||||
'menu_id': sm['id'],
|
'view_mode': 'list,form',
|
||||||
'is_main_menu': True,
|
'views': [
|
||||||
'access_line_id': main_line.id,
|
(self.env.ref('menu_control_center.view_submenu_line_list').id, 'list'),
|
||||||
})
|
],
|
||||||
created_count += 1
|
'target': 'new',
|
||||||
existing_menu_ids.add(sm['id'])
|
'domain': [('parent_line_id', '=', self.id)],
|
||||||
|
'context': {'default_parent_line_id': self.id},
|
||||||
stack.extend(children_map.get(sm['id'], []))
|
}
|
||||||
|
|
||||||
# ---------- Notification ----------
|
@api.model_create_multi
|
||||||
return {
|
def create(self, vals_list):
|
||||||
'type': 'ir.actions.client',
|
records = super().create(vals_list)
|
||||||
'tag': 'display_notification',
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
'params': {
|
return records
|
||||||
'title': _('Success') if created_count else _('Info'),
|
|
||||||
'message': (
|
def write(self, vals):
|
||||||
_('Added %s new menu(s) (including submenus)') % created_count
|
result = super().write(vals)
|
||||||
if created_count else
|
self.env['ir.ui.menu'].sudo().clear_caches()
|
||||||
_('No new menus found to add.')
|
return result
|
||||||
),
|
|
||||||
'type': 'success' if created_count else 'info',
|
def unlink(self):
|
||||||
'sticky': False,
|
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'
|
|
||||||
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_menu_access_control,access.menu.access.control,model_menu_access_control,hr.group_hr_manager,1,1,1,1
|
access_menu_access_control,access.menu.access.control,model_menu_access_control,hr.group_hr_manager,1,1,1,1
|
||||||
access_menu_access_line,access.menu.access.line,model_menu_access_line,hr.group_hr_manager,1,1,1,1
|
access_menu_access_line,access.menu.access.line,model_menu_access_line,hr.group_hr_manager,1,1,1,1
|
||||||
access_menu_control_units,access.menu.control.units,model_menu_control_units,hr.group_hr_manager,1,1,1,1
|
access_master_control_menu_line,access.master.control.menu.line,model_master_control_menu_line,hr.group_hr_manager,1,1,1,1
|
||||||
access_master_control_public,master.control.public,model_master_control,base.group_public,1,0,0,0
|
access_master_control_public,master.control.public,model_master_control,base.group_public,1,0,0,0
|
||||||
access_master_control_hr,master.control.hr,model_master_control,hr.group_hr_manager,1,1,1,1
|
access_master_control_hr,master.control.hr,model_master_control,hr.group_hr_manager,1,1,1,1
|
||||||
|
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<record id="base.action_res_groups" model="ir.actions.act_window">
|
<record id="base.action_res_groups" model="ir.actions.act_window">
|
||||||
<field name="name">Roles</field>
|
<field name="name">Roles</field>
|
||||||
<field name="res_model">res.groups</field>
|
<field name="res_model">res.groups</field>
|
||||||
<field name="domain">[('is_visible_for_master', '=', True)]</field>
|
<!-- <field name="domain">[('is_visible_for_master', '=', True)]</field>-->
|
||||||
<!-- <field name="context">{'search_default_filter_no_share': 1}</field>-->
|
<!-- <field name="context">{'search_default_filter_no_share': 1}</field>-->
|
||||||
<!-- <field name="help">A group is a set of functional areas that will be assigned to the user in order to give-->
|
<!-- <field name="help">A group is a set of functional areas that will be assigned to the user in order to give-->
|
||||||
<!-- them access and rights to specific applications and tasks in the system. You can create custom groups or-->
|
<!-- them access and rights to specific applications and tasks in the system. You can create custom groups or-->
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,109 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_master_control_list" model="ir.ui.view">
|
<record id="view_master_submenu_line_list" model="ir.ui.view">
|
||||||
<field name="name">master.control.list</field>
|
<field name="name">master.control.menu.line.submenu.list</field>
|
||||||
<field name="model">master.control</field>
|
<field name="model">master.control.menu.line</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list>
|
<list editable="bottom" create="0">
|
||||||
<field name="sequence" widget="handle"/>
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
<field name="name"/>
|
<field name="parent_menu_id" readonly="1"/>
|
||||||
<field name="code"/>
|
<field name="show_menu"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_master_control_form" model="ir.ui.view">
|
<record id="view_master_control_list" model="ir.ui.view">
|
||||||
<field name="name">master.control.form</field>
|
<field name="name">master.control.list</field>
|
||||||
<field name="model">master.control</field>
|
<field name="model">master.control</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<list>
|
||||||
<!-- <header>-->
|
<field name="sequence" widget="handle"/>
|
||||||
<!-- <button name="action_generate_groups"-->
|
<field name="name"/>
|
||||||
<!-- type="object"-->
|
<field name="code"/>
|
||||||
<!-- string="Generate Groups"-->
|
<field name="user_ids" widget="many2many_tags"/>
|
||||||
<!-- class="btn-primary"/>-->
|
</list>
|
||||||
|
</field>
|
||||||
<!-- <button name="action_update_groups"-->
|
</record>
|
||||||
<!-- type="object"-->
|
|
||||||
<!-- string="Update Groups"-->
|
<record id="view_master_control_form" model="ir.ui.view">
|
||||||
<!-- class="btn-secondary"/>-->
|
<field name="name">master.control.form</field>
|
||||||
<!-- </header>-->
|
<field name="model">master.control</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
<sheet>
|
<form>
|
||||||
<group>
|
<sheet>
|
||||||
<field name="name"/>
|
<group>
|
||||||
<field name="code"/>
|
<field name="name"/>
|
||||||
<!-- <field name="default_show" help="Upon Generating this value be placed in Show Option"/>-->
|
<field name="code"/>
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="Roles">
|
<page string="Users">
|
||||||
<field name="access_group_ids" widget="one2many_search"/>
|
<field name="user_ids" widget="one2many_search">
|
||||||
</page>
|
<list editable="bottom" create="0" delete="1">
|
||||||
</notebook>
|
<field name="name"/>
|
||||||
</sheet>
|
<field name="login"/>
|
||||||
</form>
|
<field name="company_id"/>
|
||||||
</field>
|
</list>
|
||||||
</record>
|
</field>
|
||||||
|
</page>
|
||||||
|
<page string="Menus">
|
||||||
<record id="action_master_module_control" model="ir.actions.act_window">
|
<group>
|
||||||
<field name="name">Master Control</field>
|
<button name="action_generate_menus" type="object" string="Generate Menus" class="btn-primary"/>
|
||||||
<field name="res_model">master.control</field>
|
<button name="action_update_menus" type="object" string="Update Menus" class="btn-secondary"/>
|
||||||
<field name="view_mode">list,form</field>
|
</group>
|
||||||
</record>
|
<field name="menu_line_ids" widget="one2many_search">
|
||||||
|
<list editable="bottom" create="0">
|
||||||
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
<menuitem id="master_module_access_root"
|
<field name="show_menu"/>
|
||||||
name="Masters"
|
<button name="open_submenus_popup_view" string="Sub Menus" type="object" class="btn-primary"/>
|
||||||
parent="base.menu_custom"
|
</list>
|
||||||
sequence="9"/>
|
</field>
|
||||||
|
</page>
|
||||||
<menuitem id="master_module_control_units"
|
<page string="Sub Menus">
|
||||||
name="Login Masters"
|
<field name="submenu_line_ids" widget="one2many_search">
|
||||||
parent="master_module_access_root"
|
<list editable="bottom" create="0" default_group_by="parent_menu_id">
|
||||||
action="action_master_module_control"
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
sequence="19"/>
|
<field name="parent_menu_id" readonly="1"/>
|
||||||
|
<field name="show_menu"/>
|
||||||
|
</list>
|
||||||
</odoo>
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_users_form_master_control_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">res.users.form.master.control.inherit</field>
|
||||||
|
<field name="model">res.users</field>
|
||||||
|
<field name="inherit_id" ref="base.view_users_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='company_ids']" position="after">
|
||||||
|
<field name="master_control_ids"
|
||||||
|
string="Master Access"
|
||||||
|
widget="many2many_tags"
|
||||||
|
options="{'no_create': True, 'no_create_edit': True}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_master_module_control" model="ir.actions.act_window">
|
||||||
|
<field name="name">Master Control</field>
|
||||||
|
<field name="res_model">master.control</field>
|
||||||
|
<field name="view_mode">list,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="master_module_access_root"
|
||||||
|
name="Menu Access Control"
|
||||||
|
parent="base.menu_custom"
|
||||||
|
sequence="1000"/>
|
||||||
|
|
||||||
|
<menuitem id="master_module_control_units"
|
||||||
|
name="Master Control"
|
||||||
|
parent="master_module_access_root"
|
||||||
|
action="action_master_module_control"
|
||||||
|
sequence="1"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -1,149 +1,91 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_submenu_line_list" model="ir.ui.view">
|
<record id="view_submenu_line_list" model="ir.ui.view">
|
||||||
<field name="name">menu.access.line.submenu.list</field>
|
<field name="name">menu.access.line.submenu.list</field>
|
||||||
<field name="model">menu.access.line</field>
|
<field name="model">menu.access.line</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<list editable="bottom" create="0">
|
<list editable="bottom" create="0">
|
||||||
<field name="menu_id" readonly="1" force_save="1"/>
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
<field name="parent_menu"/>
|
<field name="parent_menu_id" readonly="1"/>
|
||||||
<field name="is_main_menu" string="Show Menu"/>
|
<field name="show_menu"/>
|
||||||
<field name="access_line_id" optional="hide" readonly="1" force_save="1"/>
|
<field name="master_control_id" optional="hide" readonly="1"/>
|
||||||
<field name="control_unit" optional="hide" readonly="1"/>
|
</list>
|
||||||
</list>
|
</field>
|
||||||
</field>
|
</record>
|
||||||
</record>
|
|
||||||
|
<record id="action_submenus_popup" model="ir.actions.act_window">
|
||||||
<record id="action_submenus_popup" model="ir.actions.act_window">
|
<field name="name">Sub Menus</field>
|
||||||
<field name="name">Sub Menus</field>
|
<field name="res_model">menu.access.line</field>
|
||||||
<field name="res_model">menu.access.line</field>
|
<field name="view_mode">list</field>
|
||||||
<field name="view_mode">list</field>
|
<field name="target">new</field>
|
||||||
<field name="target">new</field>
|
</record>
|
||||||
</record>
|
|
||||||
|
<record id="view_menu_access_control_list" model="ir.ui.view">
|
||||||
<record id="view_menu_control_units_list" model="ir.ui.view">
|
<field name="name">menu.access.control.list</field>
|
||||||
<field name="name">menu.control.units.list</field>
|
<field name="model">menu.access.control</field>
|
||||||
<field name="model">menu.control.units</field>
|
<field name="arch" type="xml">
|
||||||
<field name="arch" type="xml">
|
<list>
|
||||||
<list>
|
<field name="master_control_id"/>
|
||||||
<field name="unit_name"/>
|
<field name="department_ids" widget="many2many_tags"/>
|
||||||
<field name="user_ids" widget="many2many_tags"/>
|
<field name="user_ids" widget="many2many_tags"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_menu_control_units_form" model="ir.ui.view">
|
<record id="view_menu_access_control_form" model="ir.ui.view">
|
||||||
<field name="name">menu.control.units.form</field>
|
<field name="name">menu.access.control.form</field>
|
||||||
<field name="model">menu.control.units</field>
|
<field name="model">menu.access.control</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form string="Menu Access Control">
|
||||||
<sheet>
|
<sheet>
|
||||||
<group>
|
<group>
|
||||||
<field name="unit_name"/>
|
<field name="master_control_id"/>
|
||||||
<field name="department_ids" widget="many2many_tags"/>
|
<field name="department_ids" widget="many2many_tags"/>
|
||||||
<button name="generate_department_user_ids" string="Generate Department Users" type="object" class="btn-primary"/>
|
<field name="available_user_ids" invisible="1"/>
|
||||||
</group>
|
<field name="user_ids"
|
||||||
<notebook>
|
widget="many2many_tags"
|
||||||
<page string="User's">
|
domain="[('id', 'in', available_user_ids)]"
|
||||||
<field name="user_ids"/>
|
options="{'no_create': True, 'no_create_edit': True}"/>
|
||||||
</page>
|
<button name="action_refresh_from_master"
|
||||||
</notebook>
|
type="object"
|
||||||
</sheet>
|
string="Refresh Menus From Master"
|
||||||
</form>
|
class="btn-primary"/>
|
||||||
</field>
|
</group>
|
||||||
</record>
|
<notebook>
|
||||||
|
<page name="main_menus" string="Menus">
|
||||||
|
<field name="access_menu_line_ids" widget="one2many_search">
|
||||||
<record id="action_menu_control_units" model="ir.actions.act_window">
|
<list editable="bottom" create="0">
|
||||||
<field name="name">Menu Control Units</field>
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
<field name="res_model">menu.control.units</field>
|
<field name="show_menu"/>
|
||||||
<field name="view_mode">list,form</field>
|
<button name="open_submenus_popup_view" string="Sub Menus" type="object" class="btn-primary"/>
|
||||||
</record>
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
<page name="sub_menus" string="Sub Menus">
|
||||||
<record id="view_menu_access_control_list" model="ir.ui.view">
|
<field name="access_sub_menu_line_ids" widget="one2many_search">
|
||||||
<field name="name">menu.access.control.list</field>
|
<list editable="bottom" create="0" default_group_by="parent_menu_id">
|
||||||
<field name="model">menu.access.control</field>
|
<field name="menu_id" readonly="1" force_save="1"/>
|
||||||
<field name="arch" type="xml">
|
<field name="parent_menu_id" readonly="1"/>
|
||||||
<list>
|
<field name="show_menu"/>
|
||||||
<field name="control_unit"/>
|
</list>
|
||||||
<field name="user_ids" widget="many2many_tags"/>
|
</field>
|
||||||
<field name="master_control"/>
|
</page>
|
||||||
</list>
|
</notebook>
|
||||||
</field>
|
</sheet>
|
||||||
</record>
|
</form>
|
||||||
<record id="view_menu_access_control_form" model="ir.ui.view">
|
</field>
|
||||||
<field name="name">menu.access.control.form</field>
|
</record>
|
||||||
<field name="model">menu.access.control</field>
|
|
||||||
<field name="arch" type="xml">
|
<record id="action_menu_access_control" model="ir.actions.act_window">
|
||||||
<form string="Menu Access Control">
|
<field name="name">Menu Access Control</field>
|
||||||
<sheet>
|
<field name="res_model">menu.access.control</field>
|
||||||
<group>
|
<field name="view_mode">list,form</field>
|
||||||
<field name="control_unit"/>
|
</record>
|
||||||
<field name="master_control"/>
|
|
||||||
<field name="user_ids" widget="many2many_tags"/>
|
<menuitem id="menu_menu_access_control"
|
||||||
<div class="row">
|
name="Access Control"
|
||||||
<div class="col-6">
|
parent="menu_control_center.master_module_access_root"
|
||||||
<button name="action_generate_menus" type="object" string="Generate Menus"
|
action="action_menu_access_control"
|
||||||
class="btn-primary"/>
|
sequence="20"/>
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
</odoo>
|
||||||
<button name="action_update_menus" type="object" string="Update Menus"
|
|
||||||
class="btn-secondary"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</group>
|
|
||||||
<notebook>
|
|
||||||
<page name="main_menus" string="Menus">
|
|
||||||
<field name="access_menu_line_ids" widget="one2many_search">
|
|
||||||
<list editable="bottom">
|
|
||||||
<field name="menu_id"/>
|
|
||||||
<field name="is_main_menu"/>
|
|
||||||
<button name="open_submenus_popup_view" string="Sub Menus" type="object"
|
|
||||||
class="btn-primary"/>
|
|
||||||
</list>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
<page name="sub_menus" string="Sub Menus">
|
|
||||||
<field name="access_sub_menu_line_ids" widget="one2many_search">
|
|
||||||
<list create="0" default_group_by="parent_menu" editable="bottom">
|
|
||||||
<field name="menu_id" readonly="1" force_save="1"/>
|
|
||||||
<field name="parent_menu"/>
|
|
||||||
<field name="is_main_menu" string="Show Menu"/>
|
|
||||||
<field name="access_line_id" optional="hide" readonly="1" force_save="1"/>
|
|
||||||
<field name="control_unit" optional="hide" readonly="1"/>
|
|
||||||
</list>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
</notebook>
|
|
||||||
</sheet>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="action_menu_access_control" model="ir.actions.act_window">
|
|
||||||
<field name="name">Menu Access Control</field>
|
|
||||||
<field name="res_model">menu.access.control</field>
|
|
||||||
<field name="view_mode">list,form</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem id="menu_menu_access_root"
|
|
||||||
name="Menu Access Control"
|
|
||||||
parent="base.menu_custom"
|
|
||||||
sequence="10"/>
|
|
||||||
|
|
||||||
<menuitem id="menu_menu_control_units"
|
|
||||||
name="Control Units Master"
|
|
||||||
parent="menu_menu_access_root"
|
|
||||||
action="action_menu_control_units"
|
|
||||||
sequence="19"/>
|
|
||||||
|
|
||||||
<menuitem id="menu_menu_access_control"
|
|
||||||
name="Access Control"
|
|
||||||
parent="menu_menu_access_root"
|
|
||||||
action="action_menu_access_control"
|
|
||||||
sequence="20"/>
|
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue