项目深度

Odoo 项目材料成本为什么不是“出库就记一笔”:项目拣货、分析分摊与材料成本进入 profitability 的真实链路

Odoo 项目和库存打通后,材料成本不是简单在库存单上挂个项目字段。官方会把项目带到拣货、分析分摊、分析行 category,再把材料成本送进项目利润面板。本文把 `project_stock` 与 `project_stock_account` 的链路讲透。

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

先说结论

很多人第一次看到 Odoo 项目和库存联动时,会把它理解成:

  • 出库单上多了个项目字段
  • 项目页能看到发货和收货
  • 材料成本于是就“归到项目里了”

但如果你去看 /home/ubuntu/odoo-temp/addons/project_stock/project_stock_account/,会发现官方做的是一整条成本链:

  • stock.picking 可以直接挂项目
  • 项目页面能按类型打开 deliveries / receipts / all pickings
  • 库存移动在启用分析成本时,会拿项目的分析分摊
  • 生成的分析行被标记为 picking_entry
  • 项目 profitability 再把这类分析行识别成 Materials

所以真正的设计不是“库存单属于哪个项目”,而是:

库存移动产生的成本,如何以可追踪、可汇总、可经营分析的方式进入项目。


第一层:项目上的库存按钮为什么只是入口,不是重点

project_stock/models/project_project.py 给项目加了几个动作:

  • action_open_deliveries()
  • action_open_receipts()
  • action_open_all_pickings()

从界面上看,这像是在项目里补了库存导航。

但这几个动作真正说明的是:

  • 项目可以成为库存业务的过滤视角
  • 不同类型拣货会继承项目上下文
  • 项目和库存之间建立了正式的业务关联,而不只是备注字段

换句话说,按钮是为了让用户能回看,真正重要的是后面的成本归集逻辑。


第二层:材料成本为什么不是看拣货单头,而是看 move 的分析行

project_stock/models/stock_picking.py 里,stock.picking 的确新增了:

  • project_id

但项目材料成本的关键不在 picking 头,而在 project_stock_account/models/stock_move.py

因为真正产生成本归属的是库存移动 stock.move

  • _get_analytic_distribution() 会优先拿 picking_id.project_id 的分析分摊
  • _prepare_analytic_line_values() 会把分析行名称和 category 一起准备好
  • _prepare_analytic_lines() 还会校验项目是否满足必填分析计划

这说明官方的思路是:

项目字段先挂在 picking 上,但真正落地到成本系统的是 move 级别的分析行。

这非常合理,因为会计和分析成本最终记的不是“有一张拣货单”,而是“有哪些库存移动形成了成本”。


第三层:为什么还要校验 mandatory analytic plans

_prepare_analytic_lines() 里,源码会检查:

  • 当前公司在 stock_picking 业务域下要求哪些 mandatory plans
  • 这些计划列在项目上是否都已填好

如果没填,就直接抛 ValidationError

这点特别有 Odoo 味道:

  • 不是先放行,等利润报表出错再补救
  • 而是在生成分析行时就拦住

因为一旦库存成本已过账、分析维度又缺失,后面项目盈利里看到的材料成本就会不完整甚至归错项目。

所以这里的校验本质上是在保护:

项目材料成本一旦入账,就必须具备可核算的分析维度。


第四层:为什么分析行 category 要专门标成 picking_entry

project_stock_account/models/account_analytic_line.py 给分析行 category 增加了一个新值:

  • picking_entry

很多人会忽略这个字段,但它其实是后面 profitability 识别材料成本的关键标签。

因为如果所有分析行都混在一起,项目盈利就很难区分:

  • 工时成本
  • 外包采购成本
  • 库存材料成本

picking_entry 恰好把“由库存移动生成的项目分析成本”单独打了标。

这就是一个典型的报表前置设计:

  • 先在记账/分析行层面把来源分清
  • 后面经营面板才能稳定按来源分类汇总

第五层:项目 profitability 为什么把它显示成 Materials

project_stock_account/models/project_project.py 里,官方扩展了盈利标签和成本抓取逻辑:

  • 把对应 section 命名为 Materials
  • 从 category 为 picking_entry 的分析行汇总成本
  • 可选地提供动作跳到相关分析行

这说明项目盈利页面对材料成本的理解是:

  • 它不是采购单成本
  • 也不是工时成本
  • 它是因为项目驱动了库存领用/调拨,从而形成的材料性成本

所以如果一个实施顾问跟客户说“项目利润里的材料成本来自库存移动”,这句话是源码级成立的,而不是业务猜测。


第六层:这套设计真正解决了什么问题

这套链路解决的是一个很现实的问题:

  • 项目做交付要用料
  • 用料往往发生在库存模块
  • 但项目经理最终想在项目盈利里看到材料花了多少钱

如果没有这条链,就会出现两种糟糕局面:

1. 库存做了,项目看不到

材料真实领用了,但项目利润还像没发生过一样。

2. 看得见,但不可核算

项目里能看到一个“材料金额”,却追不到底层 move、分析行和分析计划。

Odoo 这里走的是第三条路:

  • 库存流程照常做
  • 分析成本在 move 层形成
  • 项目盈利只做分类呈现与汇总

这让流程系统和经营系统能够对齐。


新手最容易误解的 4 件事

1. 以为项目材料成本就是拣货单上填了项目

不对,真正进入盈利的是库存移动生成的分析行。

2. 以为出库后材料成本会自动进项目,不需要分析配置

不对,mandatory analytic plans 缺失会直接拦截。

3. 以为材料成本和采购成本是一回事

不对,源码里是两个不同来源、不同 section。

4. 以为 profitability 里的 Materials 是手工统计值

不对,它是基于 picking_entry 类别分析行汇总出来的。


实战里最该注意什么

1. 项目想吃到材料成本,先确认库存操作类型是否启用了 analytic costs

否则 move 根本不会生成期望的分析分摊。

2. 客户一说“材料成本没进项目”,优先查三层

  • picking 是否挂项目
  • move 是否生成分析行
  • 分析行 category 是否正确进入 picking_entry

3. 多分析计划环境里,不要把项目分析字段配成“可选但其实业务必填”

因为库存成本一旦进错维度,后续利润面板再漂亮也只是错误汇总。


一句话记忆法

Odoo 项目材料成本不是从拣货单头直接进利润面板,而是先由挂项目的库存移动生成带 picking_entry 标签的分析行,再被 profitability 识别为 Materials 成本。

DISCUSSION

评论区

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