先说结论
Odoo 的子任务,不只是 parent_id 指向一下父任务那么简单。
在 /home/ubuntu/odoo-temp/addons/project/models/project_task.py 和 addons/project/tests/test_multicompany.py 里,官方实际上维护了三层东西:
- 父子层级关系
- 项目归属继承
- 在项目中是否单独显示(
display_in_project)
再加上 private task 约束和公司一致性,你会发现:
- 子任务默认会继承父任务项目
- 同项目子任务通常不单独占据项目主列表
- 子任务也可以脱离父项目、单独显示
- private task 不能再挂父任务
- 父任务换项目时,子任务可能被一起带走
所以理解子任务的关键不是“树结构”,而是:Odoo 在同时管理层级、展示和归属。
第一层:为什么 private task 不能再往下挂父任务
源码里有一个很直接的约束:
- private task 不能有 parent
也就是:
project_id IS NULL- 且
parent_id IS NOT NULL
这种情况会被禁止。
原因并不复杂。
private task 的语义是:
- 它不属于某个项目
- 更像个人或独立待办
而父子任务在项目模块中的主要价值,是把复杂工作拆进项目执行结构里。
如果允许 private task 任意挂在项目任务下面,就会出现语义混乱:
- 上层是项目工作
- 下层却是脱离项目的私人记录
- 统计和展示很难定义
所以 Odoo 干脆直接不让这种混搭成立。
第二层:子任务为什么默认继承父任务项目
_compute_project_id 和相关测试都说明了一件事:
- 当子任务不应该单独在项目里展示时
- 如果它有父任务
- 并且父任务有项目
那么它会继承父任务的项目。
这其实非常符合业务直觉。
因为大多数子任务,本来就是父任务的拆解步骤:
- 需求评审
- 开发
- 自测
- 客户确认
它们不是新的项目归属,只是同一项目下更细的执行颗粒。
所以继承项目,是默认策略。
第三层:display_in_project 才是“它算不算项目主任务”的关键
很多人会忽略 display_in_project,但这个字段非常重要。
它的作用不是控制归属,而是控制展示层级。
从源码逻辑可以读出:
- 如果子任务和父任务在同一个项目里
- 且它只是父任务的内部拆分
- 那么通常
display_in_project = False
这意味着它不一定作为项目主列表中的独立任务突出展示。
但如果:
- 子任务被改到另一个项目
- 或者它需要在当前项目中单独显露
那 display_in_project 可以变成 True。
这就是为什么一些团队会困惑:
- 我明明创建了子任务
- 为什么项目主面板看着不像多了一条主任务
因为 Odoo 认为它更像父任务内部结构,而不是项目一级卡片。
第四层:跨项目子任务不是禁用,而是有边界
很多系统干脆不允许跨项目父子关系,Odoo 不是完全不允许,而是把边界做得很明确。
测试中可以看到:
- 子任务可以因为项目变动而重新计算展示和公司
- 改变父任务或子任务的项目时,系统会同步处理 company 与 display_in_project
换句话说,Odoo 允许你表达复杂结构,但要求你接受它的后果:
- 归属会变
- 公司会变
- 展示方式会变
所以跨项目子任务不是“点一下关系就行”,而是一次归属重算。
第五层:为什么父任务一换项目,子任务会被一起牵动
测试中很关键的一点是:
- 父任务换项目
- 子任务如果仍然属于这个父层级链路
- 它的项目、显示方式、公司都可能跟着变
这背后的原则非常稳定:
父子关系不是纯文本引用,而是结构性归属关系。
既然是结构归属,父项迁移,子项当然不能假装没看见。
这也是很多实施项目里会踩坑的地方:
- 先随手建一堆子任务
- 后来想把父任务挪到另一个项目
- 结果一串数据都跟着变了
这不是副作用,而是模型本来就这么设计的。
第六层:排错时最常见的四个问题
问题一:子任务不见了
先看是不是:
display_in_project = False- 它被折叠在父任务下面,而不是消失
问题二:子任务怎么突然成 private task
通常不是“自己掉了”,而是:
- 公司或项目归属冲突
- 系统为了保证一致性,把它从项目里摘出来
问题三:为什么不能给 private task 指定 parent
因为 private task 本来就不属于项目执行树,这不是 UI 限制,是模型约束。
问题四:父任务换项目后,为什么子任务也被带走
因为父子结构在 Odoo 里带有归属含义,不是单纯参考关系。
实战建议:什么时候应该用子任务,什么时候该用独立任务
更适合子任务的场景
- 明显属于同一交付件的拆解
- 希望保留父项总览和子项执行细节
- 不需要在项目主列表里全部平铺出来
更适合独立任务的场景
- 任务可能会跨项目迁移
- 负责人、优先级、节奏都独立
- 需要单独做看板管理与统计
- 将来很可能脱离父项单独存在
也就是说,子任务适合“内部分解”,独立任务适合“独立经营”。
最后一句
Odoo 子任务难理解,不是因为字段复杂,而是因为它同时在回答三个问题:
- 这是谁的子项?
- 它属于哪个项目?
- 它该不该在项目主视图中独立显示?
把这三层分开看,private task、父子关系和跨项目行为就都顺了。
DISCUSSION
评论区