项目深度

Odoo 服务产品为什么不会“自动长任务”这么简单:从 service_tracking 到销售订单行生成项目/任务的整条链路

很多人以为 Odoo 服务产品确认销售订单后只是“自动建一个项目或任务”。其实真正决定结果的是 service_tracking、service_policy、模板复用、分析账户和销售订单行复用规则的组合。本文把这条生成链完整讲透。

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

先说结论

很多人第一次接触 Odoo 项目销售联动时,会把它理解成一句话:

  • 服务产品确认销售订单
  • 系统自动生成项目或任务

但如果你去看 /home/ubuntu/odoo-temp/addons/sale_project/models/product_template.py/home/ubuntu/odoo-temp/addons/sale_project/models/sale_order_line.py,就会发现这不是一个“自动化小功能”,而是一套产品配置驱动的交付生成引擎

系统至少要先回答这些问题:

  • 这个服务产品到底要生成项目、任务,还是两者都生成
  • 生成时复用全局项目,还是按订单新建项目
  • 是否套用项目模板或任务模板
  • 新项目挂哪个分析账户
  • 多条销售订单行是在同一个项目里落任务,还是各自拆项目
  • 里程碑计费场景要不要顺手把 milestone 一起接上

所以你看到的“自动生成任务”,其实只是这条链最后的可见结果。


第一层:真正的入口不是销售订单,而是产品上的 service_tracking

product_template.py 里,sale_project 给服务产品扩展了几个关键 tracking 选项:

  • task_global_project
  • task_in_project
  • project_only

这三个选项分别表达三种完全不同的交付建模方式。

1. task_global_project

含义是:

  • 不新建项目
  • 直接在既有项目里生成任务

这适合长期运作的共享项目,比如“客户支持池”“持续顾问项目”“统一运维项目”。

2. task_in_project

含义是:

  • 先按订单生成项目
  • 再为当前销售订单行生成任务

这适合“每张订单形成一个项目,但每个服务项还要拆成独立任务”的交付模式。

3. project_only

含义是:

  • 只生成项目
  • 不自动生成任务

这适合项目经理后续自己按模板或计划手工拆任务的场景。

换句话说,Odoo 不是在问“要不要自动建任务”,而是在问:

销售出去的服务,最终应该落成什么粒度的交付对象。


第二层:service_tracking 不是孤立配置,它会被约束校验

product_template.py 里有一段非常关键的约束:

  • service_tracking == no 时,不能再挂 project_idproject_template_id
  • task_global_project 时,不能再选 project_template_id
  • task_in_project / project_only 时,不能再选全局 project_id

这背后的意思很重要。

Odoo 不允许你同时表达两套相互冲突的语义:

  • 既说“我要在已有项目里落任务”
  • 又说“请按模板新建一个项目”

从源码角度看,这类限制并不只是为了表单体验,而是为了确保后续 _timesheet_service_generation() 不会面对自相矛盾的输入。

所以产品配置在 Odoo 里不是“建议”,而是后续生成链路的硬前提。


第三层:真正负责生成的是销售订单行,而不是产品自己

到了 sale_order_line.py,核心方法是 _timesheet_service_generation()

这一步很值得注意:

  • 产品定义的是“应该怎么生成”
  • 但真正执行生成动作的是销售订单行

为什么要这样设计?

因为交付不是抽象产品在履约,而是具体这条销售订单行在履约。只有销售订单行才同时知道:

  • 属于哪张订单
  • 当前客户是谁
  • 数量是多少
  • 是否已确认
  • 这条行是不是已经生成过项目/任务

这也是为什么 project_idtask_id 最终都挂回 sale.order.line,而不是只停留在产品模板层。


第四层:同一张订单里的多条服务行,不一定各建各的项目

很多实现者最容易误解的,是 task_in_project / project_only 的“复用逻辑”。

源码里,Odoo 会先搜:

  • 同一订单下有没有已经由别的服务行生成过项目
  • 如果产品使用项目模板,还会看“同模板项目”是否已经生成

于是它不是简单粗暴地“一条销售行一个项目”,而是:

  • 无模板时,同一订单往往复用同一个已生成项目
  • 有模板时,会按“订单 + 模板”维度复用项目

这说明 Odoo 的思路是:

项目的生成粒度,取决于交付建模,不取决于你有几条销售订单行。

所以当一张订单同时卖出多种服务时,最后结果可能是:

  • 一个项目里多张任务
  • 多个基于不同模板的项目
  • 只生成一个项目不自动拆任务

这完全取决于产品 tracking 配置。


第五层:分析账户不是补充品,而是项目生成时一起落地的地基

_timesheet_create_project_prepare_values() 里,新项目会拿到一个 account_id,来源优先级大致是:

  1. 上下文传入的 project_account_id
  2. 订单上的 project_account_id
  3. 如果都没有,就现场创建分析账户

这一步非常关键。

它意味着 Odoo 认为:

  • 项目一旦由销售履约生成
  • 就应该立刻具备可归集成本 / 工时 / 收入的分析基础

也就是说,项目生成从来不只是“创建一个 project.project 记录”,而是同时把后续:

  • timesheet
  • profitability
  • invoice analytic distribution

这些能力的底座一起搭起来。

这也是为什么很多项目类功能最后都会绕回分析账户。


第六层:任务标题、描述、工时预算,都是从销售订单行翻译过来的

_timesheet_create_task_prepare_values() 很值得读,因为它展示了 Odoo 怎么把销售语义翻译成任务语义。

它会处理这些事:

  • 把销售行数量转换成 allocated_hours
  • 从销售行描述中拆出任务标题和任务说明
  • 关联客户、项目、销售行、销售单
  • 默认不直接分配负责人

这里有两个很典型的设计判断。

1. 数量不只是报价数字,还会变成交付预算

如果产品不是 milestone 型服务,销售行数量会被换算成任务的 allocated_hours

也就是说,订单上的“卖了多少小时 / 天”,并不会停留在销售模块里,而是直接影响项目模块里的任务预算。

2. 任务文案不是凭空生成,而是尽量复用销售上下文

销售行第一行如果只是产品名,Odoo 会把它拿掉,剩余内容作为更具体的任务标题或描述。这其实是在做一件很实用的事:

让销售承诺尽量无损传到交付执行。


第七层:模板复制不是简单 copy,而是带交付语义的复制

如果产品上配置了:

  • project_template_id
  • task_template_id

那么生成就不会走最普通的 create,而会调用模板创建逻辑。

这意味着模板不是“方便你少点几下鼠标”,而是:

  • 固定交付结构
  • 继承项目经理、阶段、角色、任务树
  • 把销售确认后的新项目尽量拉到统一实施标准

尤其是项目模板场景,源码还会额外把复制出来的任务统一补上:

  • sale_line_id
  • sale_order_id
  • partner_id

这说明模板复制在 Odoo 眼里,首先是把旧的交付结构嫁接到新的销售履约上下文里


第八层:系统还会替你补默认阶段,避免项目生成后落到“Undefined”

_timesheet_create_project() 里还有个很容易被忽略但特别像产品化思维的动作:

如果新项目没有任何任务阶段,Odoo 会自动补一组基础阶段:

  • To Do
  • In Progress
  • Done
  • Cancelled

这一步很小,但说明官方很清楚:

  • “成功创建项目”不等于“这个项目已经可用”
  • 如果项目一落地就没有阶段,后续任务体验会立刻变差

所以源码在生成链里顺手把最基础的执行环境也补齐了。


第九层:里程碑服务不是晚点再处理,而是生成当下就一起挂上

_handle_milestones() 里,如果产品的 service_policydelivered_milestones,系统会:

  • 打开项目的 allow_milestones
  • 把已有里程碑挂到当前销售行
  • 或者直接新建一个 milestone
  • 如果是 task_in_project,还会把任务和 milestone 关联起来

这说明 milestone 不是销售确认后的独立后处理动作,而是项目生成链的内建分支

所以服务产品的 tracking 和 invoicing policy,其实一起决定了交付对象会怎么长出来。


新手最容易误解的 5 件事

1. 以为“自动建任务”是产品的单点功能

不对。产品只负责声明策略,真正落地的是销售订单行生成链。

2. 以为一条服务行一定对应一个项目

不对。Odoo 会按订单、模板和 tracking 规则决定是否复用已有项目。

3. 以为项目生成后再补分析账户也行

源码不是这么想的。项目生成时就尽量把分析账户一起准备好。

4. 以为任务预算和销售数量没什么关系

不对。服务数量会直接参与 allocated_hours 的换算。

5. 以为 milestone 计费只影响开票,不影响项目生成

不对。里程碑会在生成链中一起被接入项目和任务。


实战里最该注意什么

1. 配置服务产品时,先想清楚交付粒度

你卖出去的到底应该长成:

  • 全局项目里的任务
  • 每单一个项目 + 每行一个任务
  • 还是每单一个项目由项目经理再拆任务

这比“能不能自动建”更关键。

2. 自定义时别只改 create,先读 _timesheet_service_generation()

很多人喜欢在确认订单后自己补逻辑,但真正的项目复用、模板复用、分析账户复用,都已经在这里了。贸然绕开,最容易制造重复项目或错绑项目。

3. 如果销售和交付常常脱节,优先检查销售行描述和模板设计

因为 Odoo 已经在努力把销售行信息翻译成任务和项目。如果出来的任务总是不像样,往往不是项目模块的问题,而是销售输入本身太弱。


一句话记忆法

Odoo 服务产品的“自动生成项目/任务”不是一个按钮动作,而是 service_tracking + service_policy + 模板 + 分析账户 + 销售订单行复用规则 共同驱动的交付建模链。

DISCUSSION

评论区

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