项目深度

Odoo 燃尽图为什么不是“按截止日期画条线”:阶段历史、邮件追踪与 stage burndown 的真实底座

很多人以为 Odoo 项目燃尽图只是把任务数量按日期做累计,但官方其实是根据任务阶段变更历史重建“某一天任务处在哪个阶段”。这篇文章把 `project.task.burndown.chart.report` 的 SQL 逻辑讲透。

项目
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

Odoo 里的项目燃尽图,不是简单把“当前看板里有多少任务”按日期倒着画出来。

如果你去看 /home/ubuntu/odoo-temp/addons/project/report/project_task_burndown_chart_report.py,会发现官方做的是一件更重但也更靠谱的事情:

  • 先从 project.task 找任务集合
  • 再去 mail_message + mail_tracking_value 里回放 stage_id 的变更历史
  • 把每段“某任务在某阶段停留的时间区间”重建出来
  • 最后用 generate_series 在时间轴上展开,得到每个阶段在每个周期的任务数量和预估工时

所以这张燃尽图的本质不是“看当前状态”,而是:

根据阶段变更日志,回放任务曾经怎么流过各阶段。


第一层:为什么燃尽图不能直接查当前 project.task

如果只查当前任务表,你最多只能知道:

  • 任务现在在哪个阶段
  • 任务当前分配了多少工时
  • 任务现在是否关闭

但燃尽图要回答的是另一个问题:

  • 上个月这个项目还压了多少任务在 To Do
  • 哪个阶段什么时候开始堆积
  • 测试阶段的任务数量是逐步下降还是突然清空

这类问题都不是“当前值”能回答的,而是历史轨迹问题

所以官方没有偷懒直接查 project.task,而是从阶段追踪日志里恢复时间切片。


第二层:Odoo 依赖的是邮件追踪,不是额外建一张阶段流水表

project.task.burndown.chart.report 的 SQL 里,核心数据源不是单独的审计表,而是:

  • mail_message
  • mail_tracking_value

当任务 stage_id 发生变化时,邮件追踪机制会留下旧值、新值以及对应时间点。燃尽图正是利用这套现成的 tracking 基础设施,把每次阶段切换当成一段历史边界。

这说明官方的设计思路很明确:

不再维护一套“项目专用阶段历史表”,而是复用 mail tracking 作为状态时间线的数据来源。

这也解释了为什么 addons/project/models/mail_message.py 里还专门为燃尽图准备了索引:它不是附带功能,而是一个确实会吃历史数据的报表。


第三层:为什么 SQL 里要同时处理“有过变更的任务”和“从没变更过的任务”

这是这张报表最容易被忽略、但最见功力的一段。

官方在 SQL 里分了两类任务:

1. 有阶段变更记录的任务

这种任务可以通过 mail_message / mail_tracking_value 拿到:

  • 上一个阶段从什么时候开始
  • 什么时候切到下一个阶段

于是系统就能还原它在各阶段停留的区间。

2. 从创建后就没变过阶段的任务

如果一个任务一直停在创建时的默认阶段,没有任何跟踪记录,那燃尽图也不能把它漏掉。

所以 SQL 又专门处理了一类“没有 tracking 记录的任务”,把:

  • create_date 当成开始时间
  • 当前阶段当成整个区间的阶段

这才保证图表不会只统计“动过的任务”,而漏掉那些长期静止但真实存在的积压项。


第四层:为什么要区分 stage_idis_closed

源码里除了阶段,还会额外算一个字段:

  • is_closed,值为 openclosed

而这个 closed/open 的判断并不是看阶段折叠,而是结合任务 state 是否处于关闭态。

这很关键,因为燃尽图有两种常见分析方式:

  • 按具体阶段分层看积压
  • 按 open / closed 看关闭趋势

也就是说,Odoo 并不把“阶段列位置”和“业务上是否结束”混为一谈。

这和很多实现只看 kanban 列的轻量工具很不一样。官方是在告诉你:

燃尽图既要回答任务在哪一列,也要回答任务是不是已经真正结束。


第五层:为什么 allocated_hours 会跟着一起进图

这张模型不只数任务数量,还会汇总:

  • allocated_hours

所以燃尽图并不是单纯的 ticket count chart,它还可以站在预估工时的角度看趋势。

这意味着你在项目里看到的燃尽,不只是“卡片少了多少”,而是还能进一步回答:

  • 工作包的预计工作量有没有下降
  • 哪个阶段堆的不是任务数,而是大工时任务
  • 任务数量变少,但剩余工时是不是依旧很高

这个设计很值钱,因为真正的项目管理里,10 个小任务和 2 个超大任务,对交付压力完全不是一回事。


第六层:generate_series 在这里到底解决了什么问题

SQL 里最像“魔法”的部分,是最后把每段阶段停留区间丢给 generate_series

它做的事可以理解成:

  • 某任务在 2 月 10 日到 4 月 9 日期间位于 In Progress
  • 那就为每个统计周期生成一个落点
  • 这样图表才能在月、周、季度维度上看到这段区间的存在

换句话说,Odoo 不是把事件点直接画成图,而是把阶段停留区间展开成时间序列数据

这一步决定了它能真正做出阶段堆叠燃尽,而不是只给你一个“阶段切换日志列表”。


新手最容易误解的 4 件事

1. 以为燃尽图就是当前任务列表的统计投影

不对,它依赖阶段历史回放。

2. 以为只有改过阶段的任务才会进入图表

不对,没改过阶段的任务也会按创建日期和当前阶段补进去。

3. 以为图表只关心任务数量

不对,allocated_hours 也被一并聚合。

4. 以为阶段和关闭态是同一个概念

不对,源码明确把 stage_idis_closed 分开处理。


实战里最该注意什么

1. 不要轻易关闭 task stage 的 tracking 或乱改 mail tracking 机制

因为燃尽图就建立在这套历史上。你把 tracking 语义改坏,后面图表就会失真。

2. 自定义阶段流转时,要考虑历史报表还能不能解释

你新增再多阶段都没问题,但要明白燃尽图会把它们当成时间区间状态,而不是一次性标签。

3. 如果客户抱怨燃尽图“不准”,先查是不是历史缺失,而不是先怀疑图表组件

很多时候问题在于:

  • 任务数据是批量导入的
  • 阶段被直接 SQL 改过
  • tracking 记录不完整

图表本身只是忠实呈现它能拿到的历史。


一句话记忆法

Odoo 的项目燃尽图不是按当前任务状态倒推出来的,而是基于 stage_id 的邮件追踪历史重建阶段区间,再把区间展开成真正可分析的时间序列。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。