yuthon_base/project_md/models/project_mindmap_extend.py
李鹏宇 1fa24f7558 new
2026-07-23 16:08:07 +08:00

107 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from odoo import api, models
class ProjectProject(models.Model):
_inherit = 'project.project'
@api.model
def get_mindmap_tree(self, single_id=None):
"""返回 项目→阶段→任务 的树形结构dict供 mindmap 视图渲染。
不建中间表:直接利用已有的
project.project.type_ids阶段/ project.task.project_id / project.task.stage_id。
字段是否存在在服务端用 _fields 直接判断,杜绝 JS 侧写死字段导致的
Invalid field / 白屏。
"""
self = self.sudo()
# 1) 项目(单项目视角则只取当前项目)
proj_domain = [('id', '=', single_id)] if single_id else []
projects = self.search_read(proj_domain, ['name', 'type_ids'])
# 2) 阶段:经各项目 type_ids 收集、保序去重
stage_ids = []
for p in projects:
stage_ids += p['type_ids']
stage_ids = list(dict.fromkeys(stage_ids))
stages = self.env['project.task.type'].search_read(
[('id', 'in', stage_ids)], ['name', 'sequence']
) if stage_ids else []
stage_by_id = {s['id']: s for s in stages}
# 3) 任务:进度字段在部分版本/客制下不存在,服务端安全探测
task_fields = ['name', 'display_name', 'project_id', 'stage_id']
tk = self.env['project.task']
progress_field = None
if 'subtask_completion_percentage' in tk._fields:
progress_field = 'subtask_completion_percentage'
elif 'progress' in tk._fields:
progress_field = 'progress'
if progress_field:
task_fields.append(progress_field)
task_domain = [('project_id', '=', single_id)] if single_id else []
tasks = tk.search_read(task_domain, task_fields)
def _norm(v):
if v is None or v is False:
return 0
try:
v = float(v)
except (TypeError, ValueError):
return 0
# 部分字段存 0-1有的存 0-100统一归一到 0-100
return int(v) if v > 1 else int(v * 100)
project_nodes = {}
for p in projects:
project_nodes[p['id']] = {
'id': 'P%d' % p['id'],
'name': p['name'] or ('项目%s' % p['id']),
'kind': 'project',
'progress': 0,
'children': [],
}
for p in projects:
pn = project_nodes[p['id']]
proj_stages = [stage_by_id[i] for i in p['type_ids'] if i in stage_by_id]
proj_stages.sort(key=lambda s: s.get('sequence') or 0)
for st in proj_stages:
sn = {
'id': 'P%d_S%d' % (p['id'], st['id']),
'name': st['name'],
'kind': 'stage',
'progress': 0,
'children': [],
}
pn['children'].append(sn)
st_tasks = [
t for t in tasks
if t.get('project_id') and t['project_id'][0] == p['id']
and t.get('stage_id') and t['stage_id'][0] == st['id']
]
for t in st_tasks:
raw = t.get(progress_field) if progress_field else None
sn['children'].append({
'id': 'T%d' % t['id'],
'name': t.get('display_name') or t['name'],
'kind': 'task',
'resId': t['id'],
'progress': _norm(raw),
})
sn['progress'] = round(
sum(c['progress'] for c in sn['children']) / len(sn['children'])
) if sn['children'] else 0
pn['progress'] = round(
sum(c['progress'] for c in pn['children']) / len(pn['children'])
) if pn['children'] else 0
roots = list(project_nodes.values())
if single_id and len(roots) == 1:
return roots[0]
return {
'id': 0,
'name': '所有项目' if not single_id else '项目阶段与任务',
'children': roots,
}