项目深度

Odoo 子任务为什么不只是层级缩进:父子任务、项目继承与完成度汇总到底怎么联动

很多人把 Odoo 子任务理解成任务下再挂几张卡片,但源码里它还会影响项目归属、客户继承、是否在项目视图显示,以及父任务的数量和完成度汇总。本文把这条链讲清。

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

先说结论

Odoo 里的子任务,不是“父任务下面再缩进几行”。

/home/ubuntu/odoo-temp/addons/project/models/project_task.py 看,parent_id 一旦建立,系统会一起处理至少四件事:

  • 子任务要不要继承父任务的 project_id
  • 子任务要不要在项目主视图里单独显示
  • 子任务在没有客户时,要不要继承父任务或项目的客户
  • 父任务的子任务数量、已关闭数量、完成百分比怎么汇总

所以它本质上不是 UI 层的小功能,而是一套 任务树语义

在 Odoo 里,子任务不是“备注式分解”,而是会改数据归属和统计口径的正式结构。


第一层:父子关系不只决定层级,还决定项目归属

源码里 project_id 不是简单手填字段,而是带有 _compute_project_id()

关键逻辑是:

  • 如果任务本来不该单独显示在项目里
  • 它又有父任务
  • 且父任务有项目

那么当前任务会跟着父任务的 project_id 走。

这意味着什么?

意味着 Odoo 默认把很多子任务当成“父任务内部拆分”,而不是独立项目对象。

对业务人员来说,这个设计很合理:

  • 一个大的交付任务拆成若干技术子任务
  • 子任务本质仍属于同一个项目
  • 你只是想拆工作,不是想把它们变成新的项目节点

所以 parent_id 在这里不是装饰,而是一个“归属继承器”。


第二层:为什么有些子任务不会在项目主列表里重复出现

源码里还有一个很关键但常被忽略的字段:display_in_project

_compute_display_in_project() 的思路很直接:

  • 如果没有项目,显示
  • 如果没有父任务,显示
  • 如果有父任务,但项目和父任务项目不同,也显示
  • 只有当它是父任务下面、并且仍属于同一项目时,才可能不单独显示

配合 _inverse_parent_id() 的逻辑你会发现:

Odoo 在刻意避免“父任务和其同项目子任务同时把项目看板刷满”。

这就是为什么一些子任务看起来像“挂在父任务里面”,而不是项目主面板上再来一份。

这不是显示 bug,而是官方刻意控制的展示层级。


第三层:子任务客户为什么经常会“自己带出来”

partner_id 的逻辑也很能说明问题。

源码的 _compute_partner_id() 明确写了两层默认来源:

  • 优先项目的 partner_id
  • 否则看父任务的 partner_id

_get_default_partner_id() 甚至把优先顺序写得更清楚:

  1. 如果有父任务,且父任务有客户,用父任务客户
  2. 否则如果项目有客户,用项目客户

这说明 Odoo 对子任务的理解不是“完全独立的一条记录”,而是:

  • 它通常是父任务上下文里的延伸
  • 没必要每拆一个子任务就重新选一遍客户

所以你看到子任务自动带出客户,不是偶然方便,而是官方默认的业务假设。


第四层:父任务的子任务统计不是 Python for 循环硬算,而是分组汇总

_compute_subtask_count() 里,Odoo 没有对每个父任务逐条遍历 child_ids 去算。

它用了 _read_group() 做数据库层汇总:

  • parent_id 分组
  • 统计总数 __count
  • 聚合 state:array_agg
  • 再算出关闭数量

这样做有两个好处:

1. 性能更稳

一批父任务一起打开时,不会轻易退化成大量小查询。

2. 统计口径更统一

subtask_countclosed_subtask_count 是正式计算字段,不是前端临时拼出来的数字。

这意味着父任务上的“已完成 x/y”并不是视觉糖,而是模型层认可的统计结果。


第五层:完成度为什么只是“关闭数量 / 总数量”

_compute_subtask_completion_percentage() 很简洁:

  • 如果 subtask_count 不为 0
  • 就用 closed_subtask_count / subtask_count

这揭示了 Odoo 的一个设计取舍:

子任务完成度是按“任务是否关闭”统计,不是按工时权重、优先级权重或复杂度权重统计。

这会带来两个很现实的结果:

  • 3 个很小的子任务做完,完成度可能一下跳很高
  • 1 个巨大子任务没关,完成度也不会细腻反映真实投入

所以项目经理要明白:

  • 这个百分比适合看流程推进
  • 不适合当精确工作量燃尽图

如果你要按工时权重算,就该走 timesheet / allocated hours 那条链,而不是误读 subtask completion。


第六层:子任务和“私有任务 / 跨项目任务”边界是分开的

display_in_project 的设计还隐含了一个常见误区:

很多人以为“子任务一定不在项目主列表出现”。

其实不是。

如果子任务和父任务项目不同,或者本身就是需要单独展示的记录,它仍然可以显示在项目层。

也就是说,Odoo 不是简单把所有子任务都藏起来,而是区分:

  • 同项目内的拆分 → 更像父任务内部结构
  • 跨项目或独立承担的子任务 → 仍可作为独立项目项被看见

这个边界很适合复杂实施场景。


新手最容易误解的 4 件事

1. 以为子任务只是树形展示

不对。它还会影响 project_idpartner_id 和统计字段。

2. 以为子任务一定是完全独立的项目记录

不对。很多时候它会继承父任务的项目上下文,而且默认不单独刷到项目主视图。

3. 以为父任务完成度是按工作量算的

不对。默认就是“关闭子任务数 / 子任务总数”。

4. 以为客户要每张子任务手填

不对。没有显式客户时,系统会从父任务或项目往下带。


实战开发最该注意什么

1. 自定义报表时,不要把 child_ids 数量当场现算

既然官方已经有 subtask_countclosed_subtask_countsubtask_completion_percentage,能复用就复用。

2. 改子任务展示逻辑时,要一并理解 display_in_project

否则很容易出现:

  • 列表重复
  • 父子结构错位
  • 用户误以为数据多了一倍

3. 如果你要做按工时衡量的进度,不要误用 subtask completion

那应该走工时和 timesheet 的逻辑,而不是改这里的百分比公式硬塞业务含义。


一句话记忆法

Odoo 子任务不是“缩进后的任务卡”,而是会继承归属、继承客户、控制显示层级并回写父任务统计的一套正式结构。

DISCUSSION

评论区

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