先说结论
很多人打开 Odoo 项目的分析页面,会觉得它不过是:
- 任务列表
- 加几张透视图 / 图表
- 再多几个统计字段
如果你去读 /home/ubuntu/odoo-temp/addons/project/report/project_report.py 和 /home/ubuntu/odoo-temp/addons/sale_timesheet/report/project_report.py,会发现官方根本不是这么做的。
Odoo 专门维护了一张 SQL 视图模型:
report.project.task.user
它不是“任务表的另一个名字”,而是一张专门给分析场景准备的汇总底座,用来把:
- 任务主信息
- 负责人维度
- 阶段 / 状态
- 评分
- 里程碑逾期
- 依赖关系计数
- 工时开关与关闭时长
- 销售剩余工时
统一揉进一个可 group by、可图表化、可透视的分析模型里。
所以项目分析页不是“把任务字段原样展示出来”,而是站在一张分析视图上重新看任务执行系统。
第一层:为什么分析不用直接查 project.task
如果你只看业务对象,似乎直接查 project.task 就够了。
但分析场景有几个特殊要求:
- 很多指标需要聚合
- 有些指标来自别的表,如 rating、dependency、milestone
- 图表与 pivot 希望字段是“现成可 group 的”
- 还要排除模板项目等不该混进经营视图的数据
于是官方在 project_report.py 里选择 _auto = False,自己建一张 SQL view。
这其实是一个很典型的 Odoo 设计信号:
当业务对象的原子记录不适合直接做经营分析时,官方更愿意抽一张报告视图,而不是在原表上硬堆分析字段。
第二层:这张视图不是只搬运字段,而是在重新定义“任务分析的最小粒度”
report.project.task.user 的 _select() 里,不只是简单取 task 字段,还加入了很多分析语义。
例如:
nbr固定为 1,方便统计任务数is_closed按状态归类,而不是只用原始 statehas_late_and_unreached_milestone判断里程碑是否逾期未达成rating_avg来自评分表聚合delay_endings_days用 deadline 和当前时间动态算dependent_ids_count统计依赖数量
这说明这张视图在做的事情不是“把 task 表复制一遍”,而是把任务转译成一组更适合经营与管理观察的分析指标。
换句话说,原始任务表讲的是“任务是什么”;报告视图讲的是“这个任务在管理上意味着什么”。
第三层:为什么评分、依赖、里程碑这些跨表信息要提前揉进来
在 _from() 里,这张视图会 left join:
rating_ratingproject_milestonetask_dependencies_relproject_project
这几个 join 很能说明官方对项目分析的理解。
1. 评分不是附属信息,而是任务质量反馈的一部分
rating_avg、rating_last_value 被直接拉进分析视图,说明客户反馈在 Odoo 里不是只给详情页看的,而是应该进入任务管理分析。
2. 依赖数量不是结构信息,而是执行复杂度信号
任务被多少别的任务依赖、它又依赖多少任务,这些信息在执行层很关键。把依赖计数放进视图后,才能在图表和透视里直接看到哪些任务天生更复杂、更容易卡住。
3. 逾期未完成的 milestone 是项目风险信号
pm.is_reached = False 且 deadline 已过,就会形成一个风险标记。这不是单纯项目配置数据,而是执行健康度的一部分。
第四层:为什么视图要明确排除模板项目
在 _where() 里,官方特意限定:
t.project_id IS NOT NULLp.is_template IS NOT TRUE
这一步看起来普通,其实很重要。
因为模板任务虽然结构上像任务,但它不是正在执行的交付事实。如果把模板项目也混入分析:
- 任务数量会虚高
- 阶段统计会失真
- 平均关闭时长、评分、依赖计数都会被噪声污染
所以 Odoo 很明确地把“可复用交付蓝图”和“真实执行任务”分开看待。
这也是做项目分析时特别值得学的一点:
不要把模板、草稿结构和真实履约数据混进同一张经营看板。
第五层:为什么 sale_timesheet 只加了一个字段,却很有分量
到了 sale_timesheet/report/project_report.py,扩展看起来很轻:
- 新增
remaining_hours_so - 在
_from()里 joinsale_order_line - 在
_select()/_group_by()里把它带进来
但这一个字段非常有业务含金量。
因为它把原本偏执行分析的任务视图,进一步接到了销售合同侧。
于是任务分析不再只是看:
- 什么时候创建
- 什么时候指派
- 什么时候关闭
- 评分如何
还可以顺便看:
- 这张任务背后的销售工时余额还有多少
这代表项目分析从“纯项目视角”迈向了“项目 + 商业承诺”视角。
第六层:为什么官方宁可在报告视图里加字段,也不直接在图表查询时临时 join
很多自定义开发喜欢这样做:
- 打开图表页时动态拼 SQL
- 或在 read_group 里临时做扩展
官方这里选择的是更稳的路线:
- 先把分析底座定义好
- 再让上层 pivot / graph / search 直接复用
这样做的好处是:
1. 语义稳定
所有分析界面看到的“任务分析”都共享同一套字段定义。
2. 复用简单
报表、搜索、图表、透视不需要各自重复拼 join。
3. 更容易扩展
像 sale_timesheet 这样,只要在这张视图上增量扩字段,就能把销售语义接进项目分析体系。
这也是 Odoo 报表层很典型的设计哲学:
- 先抽象分析模型
- 再让 UI 和 read_group 站在分析模型上工作
第七层:这张视图到底在帮助管理者看什么
如果把视角拉高一点,report.project.task.user 最终帮管理者回答的其实是三类问题。
1. 执行效率问题
- 平均多久被分派
- 平均多久关闭
- 截止日期还差多少天
2. 交付风险问题
- 是否有逾期未完成里程碑
- 依赖关系是否过重
- 是否长时间停留在特定阶段
3. 经营协同问题
- 客户评分如何
- 项目任务与销售剩余工时是否开始脱节
这也解释了为什么这张视图会同时站在:
- 项目任务
- 客户反馈
- 依赖关系
- 里程碑
- 销售合同
这些边界上。
因为真正的项目分析,本来就不该只盯任务表本身。
新手最容易误解的 5 件事
1. 以为项目分析页就是直接查 project.task
不对。核心底座是 report.project.task.user 这张 SQL 视图。
2. 以为分析视图只是把任务字段搬过去
不对。它会重新生成很多管理语义字段。
3. 以为评分、依赖、里程碑只是细节数据
不对。官方把它们直接揉进分析模型,说明它们本来就是项目健康度指标。
4. 以为模板任务也应该参与任务分析
不对。模板项目会被主动排除。
5. 以为销售剩余工时只属于销售页
不对。sale_timesheet 已把它扩进项目任务分析视图。
实战里最该注意什么
1. 如果你要做项目分析自定义,优先考虑扩展报告视图,而不是直接污染 project.task
这样后续图表、搜索、透视更容易保持一致。
2. 项目分析指标要尽量站在“管理问题”上设计
不要只堆字段,先想清楚它回答的是:
- 效率
- 风险
- 经营协同
哪一类问题。
3. 如果你的项目是按预付工时包交付,别忽略 remaining_hours_so
它是把执行分析和合同消耗连接起来的关键指标。
一句话记忆法
Odoo 的任务分析不是从
project.task直接画图,而是先用report.project.task.user把执行、风险、反馈与销售余额揉成一张分析视图,再让前台报表站在这张视图上工作。
DISCUSSION
评论区