先说结论
很多人理解 Odoo 项目采购时,只看到项目右上角多了一个采购单按钮,于是会以为这只是 UI 增强。
但如果你读 /home/ubuntu/odoo-temp/addons/project_purchase/models/project_project.py 和 purchase_order_line.py,会发现官方真正做的是:
- 给采购单提供项目入口
- 自动把项目的分析账户分摊到采购行
- 在项目利润面板里把采购成本单独汇总成一类成本
- 再把供应商发票、待开票金额一起算进项目经营视角
所以项目采购的核心不是“能从项目打开采购单”,而是:
项目被当成采购成本归集和利润分析的业务容器。
第一层:项目采购按钮为什么只是表象
project.project 在 project_purchase 里新增了:
purchase_orders_countaction_open_project_purchase_orders()- 对应 stat button
这会让用户感觉功能重点是“项目里能点开采购单”。
但这个按钮真正体现的是:
- 项目不再只是任务容器
- 它开始承担采购成本归属入口
- 采购数据被允许从项目视角回看
也就是说,按钮只是视图层结果,真正的业务改造发生在成本归集逻辑里。
第二层:为什么项目能抓到两类采购单
源码在计算 purchase_orders_count 时,并不是只查:
purchase.order.project_id = 当前项目
它还会再查一类:
- 采购行
analytic_distribution里包含当前项目分析账户
这一步非常关键。
因为真实业务里,采购并不一定显式挂在项目头上。很多成本是通过分析分摊归属到项目的:
- 一个采购单服务多个项目
- 一个采购单不从项目页面创建
- 但某一行成本仍属于这个项目
所以官方的判断标准其实是双轨的:
- 采购单头直接绑定项目
- 采购行分析分摊命中项目分析账户
这比“项目字段等于我”更接近成本核算现实。
第三层:采购行为什么会自动继承项目分析分摊
在 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
评论区