项目深度

Odoo 项目采购为什么不只是“项目里能看到采购单”:采购单、分析分摊与利润面板成本汇总到底怎么接上

Odoo 的项目采购不是给项目多挂一个采购入口这么简单。源码里它会把采购单、采购行分析分摊、供应商发票与项目利润面板串到一起。本文把 `project_purchase` 的设计讲透。

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

先说结论

很多人理解 Odoo 项目采购时,只看到项目右上角多了一个采购单按钮,于是会以为这只是 UI 增强。

但如果你读 /home/ubuntu/odoo-temp/addons/project_purchase/models/project_project.pypurchase_order_line.py,会发现官方真正做的是:

  • 给采购单提供项目入口
  • 自动把项目的分析账户分摊到采购行
  • 在项目利润面板里把采购成本单独汇总成一类成本
  • 再把供应商发票、待开票金额一起算进项目经营视角

所以项目采购的核心不是“能从项目打开采购单”,而是:

项目被当成采购成本归集和利润分析的业务容器。


第一层:项目采购按钮为什么只是表象

project.projectproject_purchase 里新增了:

  • purchase_orders_count
  • action_open_project_purchase_orders()
  • 对应 stat button

这会让用户感觉功能重点是“项目里能点开采购单”。

但这个按钮真正体现的是:

  • 项目不再只是任务容器
  • 它开始承担采购成本归属入口
  • 采购数据被允许从项目视角回看

也就是说,按钮只是视图层结果,真正的业务改造发生在成本归集逻辑里。


第二层:为什么项目能抓到两类采购单

源码在计算 purchase_orders_count 时,并不是只查:

  • purchase.order.project_id = 当前项目

它还会再查一类:

  • 采购行 analytic_distribution 里包含当前项目分析账户

这一步非常关键。

因为真实业务里,采购并不一定显式挂在项目头上。很多成本是通过分析分摊归属到项目的:

  • 一个采购单服务多个项目
  • 一个采购单不从项目页面创建
  • 但某一行成本仍属于这个项目

所以官方的判断标准其实是双轨的:

  1. 采购单头直接绑定项目
  2. 采购行分析分摊命中项目分析账户

这比“项目字段等于我”更接近成本核算现实。


第三层:采购行为什么会自动继承项目分析分摊

project_purchase/models/purchase_order_line.py 里,_compute_analytic_distribution() 会在标准逻辑后继续补一层:

  • 如果采购行已有分摊,就把项目缺失的根计划账户拼进去
  • 如果采购行没有分摊,就直接使用项目的 _get_analytic_distribution()

这个设计很值得注意。

它不是粗暴覆盖采购行已有分摊,而是优先保留已有分摊,再补齐项目分析维度。这说明官方想解决的是:

采购行可能已经有自己的分析语义,但项目相关的分析主计划不能丢。

这也是为什么项目采购能自然进入后面的利润分析——因为从采购行层面开始,它就已经被打上了项目可识别的分析分摊。


第四层:利润面板为什么把采购单单独做成一个 section

project_purchase 会扩展项目盈利逻辑:

  • _get_profitability_labels()
  • _get_profitability_sequence_per_invoice_type()
  • _get_profitability_items()

它会新增一个成本分区:

  • purchase_order

而且区分:

  • 已开票成本 billed
  • 待开票成本 to_bill

这就不是“给项目看采购明细”那么简单了,而是把采购正式纳入项目经营分析。

在这个模型里,采购的意义变成:

  • 已确认采购但未入票,是待确认成本
  • 已形成供应商发票,是已实现成本
  • 有退款时,还会反向冲回

所以项目利润面板里看到的采购,不是采购流程截图,而是经营口径下的采购成本状态


第五层:为什么它要避免和已有发票行重复计算

源码里有一段很像会计思维的细节:

  • 先记录已纳入的 invoice line ids
  • 再排除已被采购行发票覆盖的数据
  • 最后补抓其余供应商发票成本

这一步是为了避免项目成本被重复统计。

因为一个采购成本可能既表现为:

  • 采购订单行
  • 又表现为相关供应商发票行

如果两边都全量加,总成本就会膨胀。

所以官方在项目盈利这里不是“能抓多少抓多少”,而是很谨慎地定义:

采购流与发票流都要看,但必须避免重复入账。


第六层:项目采购背后的真正设计边界

从这套源码能看出,官方对项目采购的边界理解很清楚:

1. 项目不是采购主对象

采购单主对象仍然是 purchase.order / purchase.order.line

2. 项目是采购成本的归属与分析视角

采购并不依附于项目存在,但项目需要能识别哪些采购成本属于自己。

3. 项目盈利看的是经营结果,不是单一业务单据

所以它会同时关心采购单、供应商发票和分析分摊,而不是只守着采购单头字段。


新手最容易误解的 4 件事

1. 以为项目采购就是项目里能创建采购单

不对,真正重点是采购成本如何被识别并汇总到项目。

2. 以为只有 purchase.order.project_id 才算项目采购

不对,采购行分析分摊也会被纳入。

3. 以为项目利润里的采购成本只看已开票金额

不对,源码会同时区分 billed 和 to_bill。

4. 以为采购单和供应商发票成本可以直接全部相加

不对,官方专门做了去重处理。


实战里最该注意什么

1. 如果客户要看“项目采购金额”,先问清是流程口径还是利润口径

  • 流程口径:看采购单
  • 利润口径:要看 billed / to_bill / invoice 归集

两者不是一回事。

2. 自定义采购分摊时,不要破坏项目分析账户的注入逻辑

否则后面项目侧可能根本认不出这些成本属于谁。

3. 多项目共用采购单时,优先验证 analytic_distribution,而不是只盯单头项目字段

因为真正落成本的是行级分摊,不一定是表头字段。


一句话记忆法

Odoo 项目采购不是给项目挂一个采购入口,而是通过采购行分析分摊、供应商发票和 profitability section,把采购成本真正归集到项目经营视角里。

DISCUSSION

评论区

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