采购到收货链路

采购单确认后,Odoo 如何把 RFQ 变成收货单和补货动作

看懂 purchase.order.button_confirm、purchase_stock._create_picking 和 _run_buy,理解采购如何驱动收货与补货。

Odoo 开发 库存 采购
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 的采购确认,也不是“按钮一按就什么都干完了”。

它其实分成两件不同的事:

  1. 采购业务状态推进:RFQ 变成 PO,或者进入待审批状态
  2. 库存履约对象生成:创建收货单、库存 move,并继续确认下游链路

这就是为什么采购模块看起来是在“确认订单”,库存模块却已经开始“准备收货”。


第一层:purchase.order.button_confirm()

purchase/models/purchase_order.py 里,确认逻辑本身并不负责直接创建收货单。

它主要做这些事:

  • 检查订单能不能确认
  • 校验 analytic distribution
  • 把供应商写回产品
  • 根据审批规则决定是进入 purchase 还是 to approve

这说明一个关键事实:

采购确认首先是业务状态,不是库存对象。

也就是说,纯 purchase 模块只是把采购关系说清楚;真正把它接到库存链路上的是 purchase_stock


第二层:purchase_stock.PurchaseOrder.button_approve()

purchase_stock 继承了 button_approve(),并在父类完成状态推进后补了一句:

self._create_picking()

这句话就是收货链路的起点。

所以对有库存收货需求的采购单来说,审批不是终点,而是履约开始


_create_picking() 做了什么

这段代码的核心思想是:

  • 如果已经有未完成收货单,就复用它
  • 如果还没有,就新建一个 incoming picking
  • 然后把采购行转换成 stock move
  • 再 confirm、assign,并继续确认被 push 规则影响到的后续 picking

代码结构非常清楚:

pickings = order.picking_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
if not pickings:
    res = order._prepare_picking()
    picking = StockPicking.with_user(SUPERUSER_ID).create(res)
moves = order.order_line._create_stock_moves(picking)
moves._action_confirm()
moves._action_assign()
forward_pickings = self.env['stock.picking']._get_impacted_pickings(moves)
(pickings | forward_pickings).action_confirm()

对新手来说,这里最容易误解的一点是:

采购单不是直接“变成”收货单,而是采购单驱动了收货单。

这两个对象仍然是分开的,只是 traceability 把它们连起来了。


_run_buy() 负责的不是“收货”,而是“补货决策”

采购链路还有另一半:stock.rule._run_buy()

这段逻辑的工作重点不是收货,而是:

  • 找到匹配的供应商
  • 决定是否创建新的 PO
  • 或者把 procurement 合并进已有的 PO 行
  • 再根据供应商交期回写 date_planned

如果找不到供应商,它还会走一条降级逻辑:

  • 把下游 move 切回 make-to-stock
  • 取消可传播的链路
  • 通知责任人去补供应商资料

这表示 _run_buy() 做的是“采购决策”,不是“仓库收货动作”。


为什么采购确认和收货单不能混为一谈

这个边界非常重要。

采购确认回答的是:

这笔采购业务是否成立?

收货单回答的是:

这批货是否真的到仓了?

所以你会看到:

  • button_confirm() 管业务状态
  • _create_picking() 管收货对象
  • move._action_assign() 管预留
  • picking.action_confirm() 管下游链路

它们是一条链,但不是同一个动作。


实战里怎么判断问题出在哪

如果采购单确认了,但你没看到收货单,优先查:

  1. 采购行产品是不是需要库存履约
  2. purchase_stock 是否真的装上了
  3. 订单状态是不是已经到 purchase
  4. picking_ids 是否已有未完成单据
  5. 供应商、路线和仓库设置是否让规则走了别的路径

如果你调试的是补货问题,别只盯着采购订单本身,最好一路追到 stock.rule._run_buy()


一句话总结

采购确认不是“结束”,而是“开始把采购关系落到仓库链路上”。

button_confirm() 决定业务状态,_create_picking() 生成收货单,_run_buy() 负责补货决策,三者拼起来才是完整的采购履约链。

DISCUSSION

评论区

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