220 lines
10 KiB
Python
220 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import re
|
|
|
|
from odoo import models, fields, api, tools
|
|
from odoo.osv import expression
|
|
|
|
|
|
def compute_badge_count_recursive(web_menus, menu_id):
|
|
"""
|
|
递归计算菜单的徽章数
|
|
|
|
:param web_menus: 包含所有菜单项的字典
|
|
:param menu_id: 当前菜单项的ID
|
|
:return: 如果有子菜单,则返回子菜单的 badge_count 的和;如果没有子菜单, 则返回当前菜单的 badge_count
|
|
"""
|
|
menu = web_menus[menu_id]
|
|
|
|
# 如果有子菜单,返回所有子菜单的badge_count之和
|
|
if menu['children']:
|
|
children_badge_count = 0
|
|
for child_id in menu['children']:
|
|
children_badge_count += compute_badge_count_recursive(web_menus, child_id)
|
|
return children_badge_count
|
|
|
|
# 如果没有子菜单,返回当前菜单的badge_count
|
|
return menu['badge_count']
|
|
|
|
|
|
class IrUiMenu(models.Model):
|
|
_inherit = "ir.ui.menu"
|
|
|
|
show_badge_count = fields.Boolean(string="显示菜单角标", default=True)
|
|
is_phone = fields.Boolean(string="手机菜单",default=False)
|
|
is_pc = fields.Boolean(string="PC菜单",default=True)
|
|
|
|
badge_function = fields.Char(string="菜单角标计算函数")
|
|
|
|
@api.model
|
|
@tools.ormcache_context('self._uid', 'debug', keys=('lang',))
|
|
def load_menus(self, debug):
|
|
""" Loads all menu items (all applications and their sub-menus).
|
|
|
|
:return: the menu root
|
|
:rtype: dict('children': menu_nodes)
|
|
"""
|
|
fields = ['name', 'sequence', 'parent_id', 'action', 'web_icon','show_badge_count','badge_function']
|
|
menu_roots = self.get_user_roots()
|
|
menu_roots_data = menu_roots.read(fields) if menu_roots else []
|
|
menu_root = {
|
|
'id': False,
|
|
'name': 'root',
|
|
'parent_id': [-1, ''],
|
|
'children': [menu['id'] for menu in menu_roots_data],
|
|
}
|
|
|
|
all_menus = {'root': menu_root}
|
|
|
|
if not menu_roots_data:
|
|
return all_menus
|
|
|
|
# menus are loaded fully unlike a regular tree view, cause there are a
|
|
# limited number of items (752 when all 6.1 addons are installed)
|
|
menus_domain = [('id', 'child_of', menu_roots.ids)]
|
|
blacklisted_menu_ids = self._load_menus_blacklist()
|
|
if blacklisted_menu_ids:
|
|
menus_domain = expression.AND([menus_domain, [('id', 'not in', blacklisted_menu_ids)]])
|
|
menus = self.search(menus_domain)
|
|
menu_items = menus.read(fields)
|
|
xmlids = (menu_roots + menus)._get_menuitems_xmlids()
|
|
|
|
# add roots at the end of the sequence, so that they will overwrite
|
|
# equivalent menu items from full menu read when put into id:item
|
|
# mapping, resulting in children being correctly set on the roots.
|
|
menu_items.extend(menu_roots_data)
|
|
|
|
mi_attachments = self.env['ir.attachment'].sudo().search_read(
|
|
domain=[('res_model', '=', 'ir.ui.menu'),
|
|
('res_id', 'in', [menu_item['id'] for menu_item in menu_items if menu_item['id']]),
|
|
('res_field', '=', 'web_icon_data')],
|
|
fields=['res_id', 'datas', 'mimetype'])
|
|
|
|
mi_attachment_by_res_id = {attachment['res_id']: attachment for attachment in mi_attachments}
|
|
|
|
# set children ids and xmlids
|
|
menu_items_map = {menu_item["id"]: menu_item for menu_item in menu_items}
|
|
for menu_item in menu_items:
|
|
menu_item.setdefault('children', [])
|
|
parent = menu_item['parent_id'] and menu_item['parent_id'][0]
|
|
menu_item['xmlid'] = xmlids.get(menu_item['id'], "")
|
|
if parent in menu_items_map:
|
|
menu_items_map[parent].setdefault(
|
|
'children', []).append(menu_item['id'])
|
|
attachment = mi_attachment_by_res_id.get(menu_item['id'])
|
|
if attachment:
|
|
menu_item['web_icon_data'] = attachment['datas']
|
|
menu_item['web_icon_data_mimetype'] = attachment['mimetype']
|
|
else:
|
|
menu_item['web_icon_data'] = False
|
|
menu_item['web_icon_data_mimetype'] = False
|
|
all_menus.update(menu_items_map)
|
|
|
|
# sort by sequence
|
|
for menu_id in all_menus:
|
|
all_menus[menu_id]['children'].sort(key=lambda id: all_menus[id]['sequence'])
|
|
|
|
# recursively set app ids to related children
|
|
def _set_app_id(app_id, menu):
|
|
menu['app_id'] = app_id
|
|
for child_id in menu['children']:
|
|
_set_app_id(app_id, all_menus[child_id])
|
|
|
|
for app in menu_roots_data:
|
|
app_id = app['id']
|
|
_set_app_id(app_id, all_menus[app_id])
|
|
|
|
# filter out menus not related to an app (+ keep root menu)
|
|
all_menus = {menu['id']: menu for menu in all_menus.values() if menu.get('app_id')}
|
|
all_menus['root'] = menu_root
|
|
|
|
return all_menus
|
|
|
|
|
|
def load_web_menus(self, debug):
|
|
""" Loads all menu items (all applications and their sub-menus) and
|
|
processes them to be used by the webclient. Mainly, it associates with
|
|
each application (top level menu) the action of its first child menu
|
|
that is associated with an action (recursively), i.e. with the action
|
|
to execute when the opening the app.
|
|
|
|
:return: the menus (including the images in Base64)
|
|
"""
|
|
menus = self.load_menus(debug)
|
|
|
|
web_menus = {}
|
|
for menu in menus.values():
|
|
if not menu['id']:
|
|
web_menus['root'] = {
|
|
"id": 'root',
|
|
"name": menu['name'],
|
|
"children": menu['children'],
|
|
"appID": False,
|
|
"xmlid": "",
|
|
"actionID": False,
|
|
"actionModel": False,
|
|
"actionPath": False,
|
|
"webIcon": None,
|
|
"webIconData": None,
|
|
"webIconDataMimetype": None,
|
|
"backgroundImage": menu.get('backgroundImage')
|
|
}
|
|
else:
|
|
action = menu['action']
|
|
if menu['id'] == menu['app_id']:
|
|
child = menu
|
|
while child and not action:
|
|
action = child['action']
|
|
child = menus[child['children'][0]] if child['children'] else False
|
|
action_model, action_id = action.split(',') if action else (False, False)
|
|
action_id = int(action_id) if action_id else False
|
|
badge_count = 0
|
|
res_model = False
|
|
# 增加 show_badge_count 限制是否显示"角标".
|
|
if action_model and action_id and menu['show_badge_count']:
|
|
action_record = self.env[action_model].browse(action_id).sudo()
|
|
if action_model == 'ir.actions.act_window' and action_record.view_mode != 'form':
|
|
# 不仅仅是 form 表单,获取 model
|
|
res_model = action_record.res_model
|
|
if res_model in self.env:
|
|
# 先检查 menu['badge_function']
|
|
if 'badge_function' in menu and menu['badge_function'] and hasattr(self.env[res_model], menu['badge_function']):
|
|
badge_count = getattr(self.env[res_model], menu['badge_function'])(action_record)
|
|
elif hasattr(self.env[res_model], 'get_menu_badge_count'):
|
|
badge_count = self.env[res_model].get_menu_badge_count(action_record)
|
|
elif hasattr(self.env[res_model], 'get_no_read_count'):
|
|
badge_count = self.env[res_model].get_no_read_count()
|
|
elif res_model == 'soong.workflow.record':
|
|
# 待办流程 / 传阅流程 共用同一模型,按菜单 xmlid 区分角标
|
|
if menu.get('xmlid') and 'circulation' in menu['xmlid']:
|
|
badge_count = self.env[res_model].get_workflow_circulation()
|
|
else:
|
|
badge_count = self.env[res_model].get_workflow_count()
|
|
elif hasattr(self.env[res_model], 'get_workflow_count'):
|
|
badge_count = self.env[res_model].get_workflow_count()
|
|
elif hasattr(self.env[res_model], 'get_workflow_circulation'):
|
|
badge_count = self.env[res_model].get_workflow_circulation()
|
|
elif hasattr(res_model, 'read_number'):
|
|
badge_count = self.env[res_model].read_number()
|
|
elif hasattr(res_model, 'read_coming_count'):
|
|
badge_count = self.env[res_model].read_coming_count()
|
|
elif hasattr(res_model, 'read_due_count'):
|
|
badge_count = self.env[res_model].read_due_count()
|
|
elif hasattr(res_model, 'get_is_logs2_count'):
|
|
badge_count = self.env[res_model].get_is_logs2_count()
|
|
elif hasattr(res_model, 'get_contract_count'):
|
|
badge_count = self.env[res_model].get_contract_count()
|
|
|
|
web_menus[menu['id']] = {
|
|
"id": menu['id'],
|
|
"name": menu['name'],
|
|
"children": menu['children'],
|
|
"appID": menu['app_id'],
|
|
"xmlid": menu['xmlid'],
|
|
"actionID": action_id,
|
|
"actionModel": action_model,
|
|
"webIcon": menu['web_icon'],
|
|
"webIconData": menu['web_icon_data'],
|
|
"webIconDataMimetype": menu['web_icon_data_mimetype'],
|
|
"badge_count": badge_count,
|
|
"res_model": res_model
|
|
}
|
|
|
|
# 计算父级的菜单的徽章数
|
|
for k, v in web_menus.items():
|
|
if v['children']:
|
|
web_menus[k]['badge_count'] = compute_badge_count_recursive(web_menus, k)
|
|
# 对 root 节点重新计算
|
|
return web_menus
|