Compare commits
3 Commits
005bfe16c5
...
06286495ce
| Author | SHA1 | Date |
|---|---|---|
|
|
06286495ce | |
|
|
b916b0feea | |
|
|
a1317e4ec7 |
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
|
|
@ -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',
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
from . import models
|
||||||
|
from . import menu
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -310,6 +310,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
* @returns {Controller|null}
|
* @returns {Controller|null}
|
||||||
*/
|
*/
|
||||||
function _getCurrentController() {
|
function _getCurrentController() {
|
||||||
|
debugger;
|
||||||
const stack = controllerStack;
|
const stack = controllerStack;
|
||||||
return stack.length ? stack[stack.length - 1] : null;
|
return stack.length ? stack[stack.length - 1] : null;
|
||||||
}
|
}
|
||||||
|
|
@ -440,6 +441,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
* @returns {View | null}
|
* @returns {View | null}
|
||||||
*/
|
*/
|
||||||
function _getView(viewType) {
|
function _getView(viewType) {
|
||||||
|
debugger;
|
||||||
const currentController = controllerStack[controllerStack.length - 1];
|
const currentController = controllerStack[controllerStack.length - 1];
|
||||||
if (currentController.action.type !== 'ir.actions.act_window') {
|
if (currentController.action.type !== 'ir.actions.act_window') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -837,6 +839,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
*/
|
*/
|
||||||
async function _updateUI(controller, options = {}) {
|
async function _updateUI(controller, options = {}) {
|
||||||
let resolve;
|
let resolve;
|
||||||
|
debugger;
|
||||||
let reject;
|
let reject;
|
||||||
let dialogCloseResolve;
|
let dialogCloseResolve;
|
||||||
let removeDialogFn;
|
let removeDialogFn;
|
||||||
|
|
@ -1022,7 +1025,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
debugger;
|
||||||
controllerStack = nextStack; // the controller is mounted, commit the new stack
|
controllerStack = nextStack; // the controller is mounted, commit the new stack
|
||||||
// todo del
|
// todo del
|
||||||
window.router = router
|
window.router = router
|
||||||
|
|
@ -1100,7 +1103,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
};
|
};
|
||||||
return currentActionProm;
|
return currentActionProm;
|
||||||
}
|
}
|
||||||
|
debugger;
|
||||||
const currentController = _getCurrentController();
|
const currentController = _getCurrentController();
|
||||||
if (currentController && currentController.getLocalState) {
|
if (currentController && currentController.getLocalState) {
|
||||||
currentController.exportedState = currentController.getLocalState();
|
currentController.exportedState = currentController.getLocalState();
|
||||||
|
|
@ -1290,7 +1293,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
options.newStack.splice(-1);
|
options.newStack.splice(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debugger;
|
||||||
return _updateUI(controller, options);
|
return _updateUI(controller, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1338,6 +1341,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
});
|
});
|
||||||
controller.displayName ||=
|
controller.displayName ||=
|
||||||
clientAction.displayName?.toString() || '';
|
clientAction.displayName?.toString() || '';
|
||||||
|
debugger;
|
||||||
return _updateUI(controller, options);
|
return _updateUI(controller, options);
|
||||||
} else {
|
} else {
|
||||||
const next = await clientAction(env, action);
|
const next = await clientAction(env, action);
|
||||||
|
|
@ -1367,7 +1371,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
action,
|
action,
|
||||||
..._getActionInfo(action, props),
|
..._getActionInfo(action, props),
|
||||||
});
|
});
|
||||||
|
debugger;
|
||||||
return _updateUI(controller, options);
|
return _updateUI(controller, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1767,6 +1771,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
);
|
);
|
||||||
index = index > -1 ? index : controllerStack.length;
|
index = index > -1 ? index : controllerStack.length;
|
||||||
}
|
}
|
||||||
|
debugger;
|
||||||
return _updateUI(newController, { index });
|
return _updateUI(newController, { index });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1821,6 +1826,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
}
|
}
|
||||||
Object.assign(controller, _getViewInfo(view, action, views, props));
|
Object.assign(controller, _getViewInfo(view, action, views, props));
|
||||||
}
|
}
|
||||||
|
debugger;
|
||||||
return _updateUI(controller, { index });
|
return _updateUI(controller, { index });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1920,6 +1926,7 @@ export function makeActionManager(env, router = _router) {
|
||||||
return _preprocessAction(action, context);
|
return _preprocessAction(action, context);
|
||||||
},
|
},
|
||||||
get currentController() {
|
get currentController() {
|
||||||
|
debugger;
|
||||||
return _getCurrentController();
|
return _getCurrentController();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ patch(ActionContainer.prototype, {
|
||||||
super.setup();
|
super.setup();
|
||||||
this.action_infos = [];
|
this.action_infos = [];
|
||||||
this.controllerStacks = {};
|
this.controllerStacks = {};
|
||||||
// this.action_service = useService('action');
|
this.actionService = useService('action');
|
||||||
|
|
||||||
this.env.bus.addEventListener(
|
this.env.bus.addEventListener(
|
||||||
'ACTION_MANAGER:UPDATE',
|
'ACTION_MANAGER:UPDATE',
|
||||||
|
|
@ -26,30 +26,6 @@ patch(ActionContainer.prototype, {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
get_controllers(info) {
|
|
||||||
const action_infos = [];
|
|
||||||
const entries = Object.entries(info.controllerStacks);
|
|
||||||
|
|
||||||
entries.forEach(([key, stack]) => {
|
|
||||||
const lastController = stack[stack.length - 1];
|
|
||||||
|
|
||||||
const action_info = {
|
|
||||||
key: key,
|
|
||||||
__info__: lastController,
|
|
||||||
Component: lastController.__info__.Component,
|
|
||||||
active: false,
|
|
||||||
componentProps: lastController.__info__.componentProps || {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastController.count == info.count) {
|
|
||||||
action_info.active = true;
|
|
||||||
}
|
|
||||||
action_infos.push(action_info);
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
return action_infos;
|
|
||||||
},
|
|
||||||
|
|
||||||
_on_close_action(action_info) {
|
_on_close_action(action_info) {
|
||||||
this.action_infos = this.action_infos.filter((info) => {
|
this.action_infos = this.action_infos.filter((info) => {
|
||||||
|
|
@ -62,17 +38,103 @@ patch(ActionContainer.prototype, {
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
get_controllers(info) {
|
||||||
|
const action_infos = [];
|
||||||
|
const entries = Object.entries(info.controllerStacks);
|
||||||
|
|
||||||
|
entries.forEach(([key, stack]) => {
|
||||||
|
const lastController = stack[stack.length - 1];
|
||||||
|
|
||||||
|
const action_info = {
|
||||||
|
key: key,
|
||||||
|
__info__: lastController,
|
||||||
|
// Store the exact router state that was active for this tab
|
||||||
|
routerState: lastController.state,
|
||||||
|
Component: lastController.__info__.Component,
|
||||||
|
active: false,
|
||||||
|
componentProps: lastController.__info__.componentProps || {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastController.count == info.count) {
|
||||||
|
action_info.active = true;
|
||||||
|
}
|
||||||
|
action_infos.push(action_info);
|
||||||
|
})
|
||||||
|
|
||||||
|
return action_infos;
|
||||||
|
},
|
||||||
|
|
||||||
_on_active_action(action_info) {
|
_on_active_action(action_info) {
|
||||||
debugger
|
debugger;
|
||||||
this.action_infos.forEach((info) => {
|
this.action_infos.forEach((info) => {
|
||||||
info.active = info.key === action_info.key;
|
info.active = info.key === action_info.key;
|
||||||
});
|
});
|
||||||
const url = _router.stateToUrl(action_info.__info__.state)
|
|
||||||
browser.history.pushState({}, "", url);
|
try {
|
||||||
|
// Use the exact router state that was saved
|
||||||
|
if (action_info.routerState) {
|
||||||
|
_router.pushState(action_info.routerState, { replace: true });
|
||||||
|
|
||||||
|
// Force the action service to reload the state
|
||||||
|
setTimeout(() => {
|
||||||
|
this.actionService.loadState();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error switching controller stack:", e);
|
||||||
|
}
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
// _on_active_action(action_info) {
|
||||||
|
// debugger
|
||||||
|
// this.action_infos.forEach((info) => {
|
||||||
|
// info.active = info.key === action_info.key;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// // Get the action details from the action info
|
||||||
|
// const action = action_info.__info__.jsId ?
|
||||||
|
// { jsId: action_info.__info__.jsId } :
|
||||||
|
// action_info.__info__.action;
|
||||||
|
//
|
||||||
|
// // Get the controller from the controller stack
|
||||||
|
// const controllerStack = this.controllerStacks[action_info.key];
|
||||||
|
// if (!controllerStack || controllerStack.length === 0) {
|
||||||
|
// console.error("Cannot switch tabs: No controller found for tab", action_info);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const controller = controllerStack[controllerStack.length - 1];
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// // Execute the action to switch context
|
||||||
|
// this.actionService.loadAction(action_info.__info__.action.id).then(loadedAction => {
|
||||||
|
// // Update the controller with the loaded action
|
||||||
|
// controller.action = loadedAction;
|
||||||
|
//
|
||||||
|
// // Execute the action to switch context
|
||||||
|
// this.actionService.doAction(loadedAction, {
|
||||||
|
// clear_breadcrumbs: false,
|
||||||
|
// pushState: false,
|
||||||
|
// replaceState: true, // Replace the current state instead of pushing a new one
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// const url = _router.stateToUrl(action_info.__info__.state);
|
||||||
|
// browser.history.pushState({}, "", url);
|
||||||
|
// } catch (e) {
|
||||||
|
// console.error("Error updating URL:", e);
|
||||||
|
// }
|
||||||
|
// this.render();
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
//
|
||||||
|
// const url = _router.stateToUrl(action_info.__info__.state)
|
||||||
|
// browser.history.pushState({}, "", url);
|
||||||
|
// this.render();
|
||||||
|
// },
|
||||||
|
|
||||||
_close_other_action() {
|
_close_other_action() {
|
||||||
this.action_infos = this.action_infos.filter((info) => {
|
this.action_infos = this.action_infos.filter((info) => {
|
||||||
if (info.active == false) {
|
if (info.active == false) {
|
||||||
|
|
@ -84,7 +146,6 @@ patch(ActionContainer.prototype, {
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
_close_current_action() {
|
_close_current_action() {
|
||||||
debugger
|
|
||||||
this.action_infos = this.action_infos.filter((info) => {
|
this.action_infos = this.action_infos.filter((info) => {
|
||||||
if (info.active == true) {
|
if (info.active == true) {
|
||||||
delete this.controllerStacks[info.key];
|
delete this.controllerStacks[info.key];
|
||||||
|
|
@ -95,7 +156,6 @@ patch(ActionContainer.prototype, {
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
_on_close_all_action() {
|
_on_close_all_action() {
|
||||||
debugger
|
|
||||||
this.action_infos.forEach((info) => {
|
this.action_infos.forEach((info) => {
|
||||||
delete this.controllerStacks[info.key];
|
delete this.controllerStacks[info.key];
|
||||||
});
|
});
|
||||||
|
|
@ -108,11 +168,12 @@ ActionContainer.components = {
|
||||||
...ActionContainer.components,
|
...ActionContainer.components,
|
||||||
AklMultiTab,
|
AklMultiTab,
|
||||||
};
|
};
|
||||||
|
|
||||||
ActionContainer.template = xml`
|
ActionContainer.template = xml`
|
||||||
<t t-name="web.ActionContainer">
|
<t t-name="web.ActionContainer">
|
||||||
<t t-set="action_infos" t-value="action_infos" />
|
<t t-set="action_infos" t-value="action_infos" />
|
||||||
<div class="o_action_manager d-flex flex-colum">
|
<div class="o_action_manager d-flex flex-colum">
|
||||||
<AklMultiTab
|
<AklMultiTab
|
||||||
action_infos="action_infos"
|
action_infos="action_infos"
|
||||||
active_action="(action_info) => this._on_active_action(action_info)"
|
active_action="(action_info) => this._on_active_action(action_info)"
|
||||||
close_action="(action_info) => this._on_close_action(action_info)"
|
close_action="(action_info) => this._on_close_action(action_info)"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="0">
|
<data noupdate="0">
|
||||||
<record id="group_proforma_sales" model="res.groups">
|
<record id="group_proforma_sales_dashboard" model="res.groups">
|
||||||
<field name="name">Samashti Dashboard </field>
|
<field name="name">Samashti Dashboard </field>
|
||||||
<field name="category_id" ref="base.module_category_hidden"/>
|
<field name="category_id" ref="base.module_category_hidden"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@
|
||||||
<menuitem id="samashti_dashboard_root"
|
<menuitem id="samashti_dashboard_root"
|
||||||
name="Overview"
|
name="Overview"
|
||||||
action="samashti_action_dashboards"
|
action="samashti_action_dashboards"
|
||||||
sequence="-100" groups="base.group_user"/>
|
sequence="-100" groups="dashboard.group_proforma_sales_dashboard"/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
@ -10,9 +10,9 @@ class StockPicking(models.Model):
|
||||||
def _action_done(self):
|
def _action_done(self):
|
||||||
res = super(StockPicking, self)._action_done()
|
res = super(StockPicking, self)._action_done()
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.purchase_id and rec.picking_type_id.code == 'incoming':
|
if rec.picking_type_id.code == 'incoming':
|
||||||
grn = self.env['grn'].search([('picking_id','=', rec.id)])
|
grn = self.env['grn'].search([('picking_id', '=', rec.id)])
|
||||||
if not grn:
|
if not grn and rec.purchase_id:
|
||||||
grn_data = {
|
grn_data = {
|
||||||
'vendor_id':rec.partner_id.id,
|
'vendor_id':rec.partner_id.id,
|
||||||
'date':fields.Datetime.now(),
|
'date':fields.Datetime.now(),
|
||||||
|
|
@ -32,7 +32,10 @@ class StockPicking(models.Model):
|
||||||
new_grn.picking_id = rec
|
new_grn.picking_id = rec
|
||||||
new_grn.state = 'done'
|
new_grn.state = 'done'
|
||||||
rec.purchase_id.grn_ids |= new_grn
|
rec.purchase_id.grn_ids |= new_grn
|
||||||
elif grn and grn.state != 'done':
|
if grn and grn.state != 'done':
|
||||||
|
for line in rec.move_ids:
|
||||||
|
if line.quantity != grn.grn_line_ids.filtered(lambda x:x.product_id == line.product_id).quantity:
|
||||||
|
grn.grn_line_ids.filtered(lambda x: x.product_id == line.product_id).quantity = line.quantity
|
||||||
grn.state = 'done'
|
grn.state = 'done'
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue