项目深度

Odoo 任务关闭为什么不是“拖进完成列就行”:Stage、State、阻塞依赖与关闭统计到底谁说了算

很多团队以为 Odoo 项目里把任务拖进 Done 就算结束,但源码里真正影响关闭统计、等待状态、关闭时长和里程碑判断的并不只有 stage。本文把 `stage_id`、`state`、依赖阻塞和关闭口径一次讲清。

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

先说结论

在 Odoo 项目里,“任务看起来做完了”“系统把它当成已关闭” 不是一回事。

如果你去看 /home/ubuntu/odoo-temp/addons/project/doc/stage_status.rstaddons/project/models/project_task.py,会发现官方把任务推进拆成了两层:

  • stage_id:你把任务放在哪一列
  • state:系统根据关闭状态、依赖阻塞等规则计算出来的执行语义

这意味着:

  • 拖到某个阶段,只是改变流程位置
  • 是否计入 closed task、是否停止“腐烂”判断、是否参与关闭时长统计,要看 state
  • 如果依赖未完成,任务可能被自动推回 Waiting
  • 一些项目报表和 milestone 逻辑,也是按 open / closed 口径在工作,不是只看列名像不像“完成”

所以正确理解不是“把列拖对就好了”,而是:列负责表达流程位置,状态负责表达执行语义,二者一起构成任务生命周期。


第一层:为什么 Odoo 早就不再把任务状态等同于按钮状态

官方文档 stage_status.rst 里很明确:老的 state 按钮流被清理掉,项目任务改成以 stage_id 为主来表达流程位置。

但这并不等于“状态彻底消失”。

在新版模型里,project.task 仍有一个 state 字段,只是它不再是你理解中的“手工点按钮换状态”,而是更偏向管理语义:

  • 01_in_progress
  • 02_changes_requested
  • 03_approved
  • 1_done
  • 1_canceled
  • 04_waiting_normal

最关键的是,源码里还有:

  • CLOSED_STATES = {'1_done', '1_canceled'}
  • is_closedstate in CLOSED_STATES 计算

也就是说,系统真正拿来判断“关没关”的,是 closed states 集合,不是阶段名称。

一个列名叫 Done 的阶段,如果没有把任务落到 closed state 语义上,很多统计仍不会按“已关闭”处理。


第二层:Stage 管位置,State 管含义

很多实施项目会把看板列设计得非常业务化,比如:

  • 待分析
  • 开发中
  • 待测试
  • 待客户确认
  • 已上线

这很好,因为阶段本来就应该服务流程可视化。

但源码里 state 的计算逻辑提醒你:

  • Stage 更像“现在在哪个流程节点”
  • State 更像“系统怎么看待这个任务的执行状态”

典型后果有三个:

1. 报表看的是 state,不一定只看 stage

project.project 上的 open_task_countclosed_task_count,以及一些里程碑判断,都按 open / closed 状态口径聚合。

2. 关闭时长是业务信号,不是视觉信号

working_hours_closeworking_days_close 这种字段要计算从创建到关闭的耗时,本质上依赖的是“什么时候算 close”,而不是用户肉眼觉得“已经挪到最后一列”。

3. 你可以有很多阶段,但关闭语义只能有少数几个

列可以很多,关闭语义却必须收敛。否则经营统计会漂移。


第三层:为什么依赖一存在,任务会自动变成 Waiting

project_task.py_compute_state 的逻辑非常关键:

  • 如果开启了 allow_task_dependencies
  • depend_on_ids 中还有未关闭任务
  • 当前任务本身又不在 closed states

那么任务会被系统置成:

  • 04_waiting_normal

反过来,只要阻塞项都清掉了,它又会从 waiting 回到:

  • 01_in_progress

这说明 Odoo 的“等待”不是一个纯手工标签,而是依赖网络驱动出来的计算结果

也正因为这样,很多团队会踩一个误区:

任务明明在“开发中”列,为什么状态却是 Waiting?

答案通常不是界面 bug,而是:

  • 流程位置没有变
  • 但依赖关系让系统认定它当前不能推进

所以排错顺序应该是:

  1. 先看有没有开启任务依赖
  2. 再看 Blocked By 是否存在未关闭任务
  3. 再看是不是有人手动把任务拖到了一个看起来很积极的列
  4. 最后再确认报表为什么没把它算成正常推进任务

第四层:为什么“Done 列”不等于“已关闭任务数”

很多管理层最先看到的问题是:

  • 看板里已经一堆 Done
  • 可项目统计里 closed 数不对
  • milestone 也没按预期推进

这时不要先怀疑统计报表,先怀疑口径。

在源码里:

  • is_closed 依赖 closed states
  • 项目上的 closed/open count 也是按 state 聚合
  • 里程碑是否还能标记 done,也会看关联任务里 open 与 closed 的数量

这意味着,Done 列只是一个可能与 closed 语义对齐的视觉容器,但它本身不是结算口径。

如果你的自定义把列配得很花哨,却没有同时考虑 state 的意义,就会出现:

  • 看板上“似乎完成”
  • 统计上“仍然未关闭”
  • 时长上“关闭时间不准”
  • 里程碑上“为什么还不能结案”

第五层:实施时最容易犯的三个误区

误区一:把阶段当状态字典用

阶段可以无限细分,状态不行。阶段太像状态,会让流程可视化和经营统计搅在一起。

误区二:把等待状态手工化

如果项目启用了依赖,Waiting 本来就有自动计算的一面。你再手工用一个“待前置完成”列去表达同一层意思,往往会造成双重语义。

误区三:把关闭等同于 UI 操作

很多人以为用户完成最后一次拖拽,就是关闭时刻。实际上 Odoo 更关心的是:

  • 当前状态是否进入 closed set
  • 依赖是否清空
  • 统计时是否按 close 口径可聚合

实战建议:如何把规则设计得既清楚又不拧巴

我的建议是:

1. 阶段只表达流程,不表达结算口径

例如:

  • 待分析
  • 执行中
  • 待验收
  • 已完成
  • 已取消

视觉上没问题,但一定要同步定义哪些才是真关闭语义。

2. 依赖项目里,别再复制一个“等待列”滥用

既然系统已经能因为前置任务未关闭而把任务置为 waiting,就不要再额外造很多看起来类似的手工状态。

3. 所有经营统计,先统一 closed 口径

在做:

  • 关闭时长
  • 任务完成率
  • 里程碑完成判断
  • 项目健康度面板

之前,先确认团队内部到底把哪些 state 当 closed。


最后一句

Odoo 任务管理最容易被误读的地方,不是字段多,而是界面上的列太直观,掩盖了底层状态语义

真正稳的理解应该是:

  • stage_id 解决“现在在哪一步”
  • state 解决“系统把它当成什么状态”
  • is_closed 解决“统计与生命周期是否结束”
  • 依赖关系解决“它是不是其实还不能动”

把这四层分清,项目看板、统计、里程碑和排错顺序才会统一。

DISCUSSION

评论区

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