new module department/user wise menu control center

This commit is contained in:
pranay 2025-10-01 12:49:15 +05:30 committed by raman
parent a1317e4ec7
commit b916b0feea
9 changed files with 339 additions and 0 deletions

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,28 @@
{
'name': 'Menu Access Control',
'version': '1.0',
'summary': 'Control menu visibility based on users or companies',
'description': """
This module allows administrators to configure menu visibility
based on selected users or companies. Active parent menus can
be generated and assigned for access control.
""",
'category': 'Tools',
'author': 'PRANAY',
'website': 'https://ftprotech.in',
'depends': ['base','hr'],
'data': [
'security/ir.model.access.csv',
'data/data.xml',
'views/menu_access_control_views.xml',
],
# 'assets': {
# 'web.assets_backend': [
# 'menu_control_center/static/src/js/menu_service.js',
# ],
# },
'installable': True,
'application': True,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="1">
<record id="hr_unit_menu_control" model="menu.control.units">
<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>

View File

@ -0,0 +1,2 @@
from . import models
from . import menu

View File

@ -0,0 +1,67 @@
from odoo import models, fields, api, tools, _
from collections import defaultdict
class IrUiMenu(models.Model):
_inherit = 'ir.ui.menu'
@api.model
@tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug')
def _visible_menu_ids(self, debug=False):
""" Return the ids of the menu items visible to the user. """
# retrieve all menus, and determine which ones are visible
context = {'ir.ui.menu.full_list': True}
menus = self.with_context(context).search_fetch([], ['action', 'parent_id']).sudo()
# first discard all menus with groups the user does not have
group_ids = set(self.env.user._get_group_ids())
if not debug:
hide_menus_list = self.env['menu.access.control'].sudo().search([('user_ids','ilike',self.env.user.id)]).access_menu_line_ids.filtered(lambda menu: not(menu.is_main_menu)).menu_id.ids
menus = menus.filtered(lambda menu: (menu.id not in hide_menus_list))
group_ids = group_ids - {
self.env['ir.model.data']._xmlid_to_res_id('base.group_no_one', raise_if_not_found=False)}
menus = menus.filtered(
lambda menu: not (menu.groups_id and group_ids.isdisjoint(menu.groups_id._ids)))
# take apart menus that have an action
actions_by_model = defaultdict(set)
for action in menus.mapped('action'):
if action:
actions_by_model[action._name].add(action.id)
existing_actions = {
action
for model_name, action_ids in actions_by_model.items()
for action in self.env[model_name].browse(action_ids).exists()
}
action_menus = menus.filtered(lambda m: m.action and m.action in existing_actions)
folder_menus = menus - action_menus
visible = self.browse()
# process action menus, check whether their action is allowed
access = self.env['ir.model.access']
MODEL_BY_TYPE = {
'ir.actions.act_window': 'res_model',
'ir.actions.report': 'model',
'ir.actions.server': 'model_name',
}
# performance trick: determine the ids to prefetch by type
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
action = 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.check(model_name, 'read', False):
# make menu visible, and its folder ancestors, too
visible += menu
menu = menu.parent_id
while menu and menu in folder_menus and menu not in visible:
visible += menu
menu = menu.parent_id
return set(visible.ids)

View File

@ -0,0 +1,65 @@
# 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"
)
def action_generate_menus(self):
"""Button to fetch active top-level menus and populate access lines."""
menu_lines = []
active_menus = self.env['ir.ui.menu'].search([
('parent_id', '=', False), # top-level menus
('active', '=', True)
])
for menu in active_menus:
menu_lines.append((0, 0, {
'menu_id': menu.id,
'is_main_menu': True
}))
self.access_menu_line_ids = menu_lines
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)

View File

@ -0,0 +1,4 @@
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_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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_menu_access_control access.menu.access.control model_menu_access_control hr.group_hr_manager 1 1 1 1
3 access_menu_access_line access.menu.access.line model_menu_access_line hr.group_hr_manager 1 1 1 1
4 access_menu_control_units access.menu.control.units model_menu_control_units hr.group_hr_manager 1 1 1 1

View File

@ -0,0 +1,44 @@
///** @odoo-module **/
//
//import { browser } from "@web/core/browser/browser";
//import { makeEnv, startServices } from "@web/env";
//import { session } from "@web/session";
//import { _t } from "@web/core/l10n/translation";
//import { browser } from "@web/core/browser/browser";
//import { MenuService } from "@web/services/menu_service";
//
//export const menuService = {
// dependencies: MenuService.dependencies,
// start(env, deps) {
// const menu = super.start(env, deps);
//
// // Override the getApps method
// const originalGetApps = menu.getApps;
// menu.getApps = async function() {
// // Get the original apps
// let apps = await originalGetApps.call(this);
//
// // Fetch your custom menu access data
// const accessData = await this.orm.call(
// 'menu.access.control',
// 'get_user_access_menus',
// []
// );
//
// // If access data exists, filter the apps
// if (accessData && accessData.allowed_menu_ids) {
// apps = apps.filter(app =>
// accessData.allowed_menu_ids.includes(app.id)
// );
// }
//
// return apps;
// };
//
// return menu;
// },
//};
//
//// Register the service
//import { registry } from "@web/core/registry";
//registry.category("services").add("menu", menuService, { force: true });

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_menu_control_units_list" model="ir.ui.view">
<field name="name">menu.control.units.list</field>
<field name="model">menu.control.units</field>
<field name="arch" type="xml">
<list>
<field name="unit_name"/>
<field name="user_ids" widget="many2many_tags"/>
</list>
</field>
</record>
<record id="view_menu_control_units_form" model="ir.ui.view">
<field name="name">menu.control.units.form</field>
<field name="model">menu.control.units</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="unit_name"/>
<field name="department_ids" widget="many2many_tags"/>
<button name="generate_department_user_ids" string="Generate Department Users" type="object" class="btn-primary"/>
</group>
<notebook>
<page string="User's">
<field name="user_ids"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_menu_control_units" model="ir.actions.act_window">
<field name="name">Menu Control Units</field>
<field name="res_model">menu.control.units</field>
<field name="view_mode">list,form</field>
</record>
<record id="view_menu_access_control_list" model="ir.ui.view">
<field name="name">menu.access.control.list</field>
<field name="model">menu.access.control</field>
<field name="arch" type="xml">
<list>
<field name="control_unit"/>
<field name="user_ids" widget="many2many_tags"/>
</list>
</field>
</record>
<record id="view_menu_access_control_form" model="ir.ui.view">
<field name="name">menu.access.control.form</field>
<field name="model">menu.access.control</field>
<field name="arch" type="xml">
<form string="Menu Access Control">
<sheet>
<group>
<field name="control_unit"/>
<field name="user_ids" widget="many2many_tags"/>
<button name="action_generate_menus" type="object" string="Generate Menus" class="btn-primary"/>
</group>
<field name="access_menu_line_ids">
<list editable="bottom">
<field name="menu_id"/>
<field name="is_main_menu"/>
</list>
</field>
</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>