补货采购

Odoo 补货为什么没按你指定的供应商走:Orderpoint 供应商覆盖、在途数量与采购触发边界讲透

很多团队以为补货规则只决定“补多少”,但在 Odoo 里,orderpoint 还会悄悄决定“向谁买、多少算已经在补、什么时候别再重复下单”。本文把补货触发采购时最关键的采购侧边界讲清楚。

采购
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

很多人把补货规则理解成一个很朴素的东西:

  • 库存低了
  • 系统补一点
  • 然后自动生成采购

但真实的 Odoo 采购侧逻辑更复杂。

对采购来说,stock.warehouse.orderpoint 不只是“触发器”,它还会影响:

  • 这次补货默认向谁买
  • 这次补货要不要强制用某条供应商价目
  • 还有多少采购量已经在路上,不该重复下单
  • 这次补货的采购日期和提前期该怎么推

所以很多“补货没生效”或“补货买错供应商”的问题,本质上并不是 scheduler 本身的问题,而是:

orderpoint 已经把采购侧上下文改掉了。


这篇为什么不是“补货规则与 scheduler”旧题重写

站里已有文章讲:

  • 为什么 reordering rules 会触发自动补货
  • scheduler 如何把库存阈值推进成采购或制造动作

那篇更像在讲“补货系统怎么跑起来”。

这篇专门盯采购侧边界:

  • 当补货落到 Buy 路径时,orderpoint 如何参与供应商选择
  • Odoo 如何统计“已经在采购路上的量”
  • 为什么明明库存低了,却不一定继续下更多 PO

也就是说,这篇是 补货触发采购后的采购语义,不是库存阈值总览。


源码入口:stock.warehouse.orderpoint 不只是库存参数表

/home/ubuntu/odoo-temp/addons/purchase_stock/models/stock.py 里,Odoo 给 orderpoint 扩展了几组非常关键的字段:

  • supplier_id:指定某条 product.supplierinfo
  • effective_vendor_id:当前补货真实会使用的供应商
  • vendor_ids:产品上的可用供应商列表
  • supplier_id_placeholder:如果你没显式指定,系统推断出的默认供应商

这已经说明一个很重要的事实:

在补货语境里,供应商不是产品静态属性,而是 orderpoint 级别的执行上下文。


为什么设置了 orderpoint 的供应商,就会改掉默认采购路线感受

源码里 supplier_id 有一个反向逻辑 _inverse_supplier_id()

  • 如果当前 orderpoint 还没 route
  • 但你设置了供应商
  • 系统会自动给它挂上 buy route

这背后的业务含义非常直接:

  • 你一旦在补货规则上明确指定了采购供应商
  • Odoo 就会把这条补货理解成“应该走 Buy”

所以很多人以为自己只是“补充了一个建议供应商”,但系统实际接收到的信号更强:

  • 这条补货是采购驱动,不只是库存提示

这也是为什么现场里常出现:

  • 改了供应商之后,补货表现突然更像采购流程而不是普通库存提醒

默认供应商不是拍脑袋选的,而是 _get_default_supplier() 现算的

在 orderpoint 上,如果你没有显式写 supplier_id,系统会调用 _get_default_supplier()

_get_default_supplier() 又不是随便拿产品第一家供应商,而是交给默认规则 _get_matching_supplier() 去算。

也就是说,它仍然会结合:

  • 当前公司
  • qty_to_order
  • 补货 UoM
  • 供应商生效条件

这就解释了一个常见误区:

同一个产品,在产品页看到的第一供应商,不一定是 orderpoint 补货时真正会用的那家。

补货语境下,系统关心的是“这次补货谁最匹配”,而不是“主数据列表谁排第一眼”。


effective_vendor_id 为什么很关键

effective_vendor_id 的计算很简单,但非常实用:

  • supplier_id 就用它
  • 没有就用 _get_default_supplier()

这说明 Odoo 很清楚地区分了两层语义:

1. 你手工指定的供应商

这是一种强约束。

2. 系统推断的默认供应商

这是一种运行时结果。

很多实施问题就卡在这里。

用户以为:

  • “这个产品默认就是 A 供应商”

但补货画面上真正显示的可能是:

  • effective_vendor_id = B

这时候不是系统自相矛盾,而是:

  • orderpoint 上下文、数量、日期或有效规则,已经把默认结果改写了。

为什么有时库存很低,系统却没有继续采购:关键在 _quantity_in_progress()

这条链非常值得讲透。

purchase_stock 里,orderpoint 扩展了 _quantity_in_progress(),它会调用产品层的 _get_quantity_in_progress() 去汇总当前已经在采购流程中的数量。

/home/ubuntu/odoo-temp/addons/purchase_stock/models/product.py 中,这个“在途数量”不是只看已确认采购单,而是会把满足条件的采购行按 location / warehouse 聚合。

它关注的是:

  • RFQ / PO 行状态
  • 采购行对应的 orderpoint
  • 目标位置 location_final_id
  • move 链是否已经指向某个目标位置

这说明 Odoo 的问题意识不是:

  • 库存低了就继续买

而是:

  • 库存低了,但已经有多少量在补、补到哪里、是给哪个位置补,这些量要不要先算进去?

所以现场里经常出现的“库存还没到仓,但系统不再下单”并不奇怪。

对 Odoo 来说,那部分量可能已经被视为:

  • 在采购中
  • 即将满足该 orderpoint 的补货需求

为什么 orderpoint 会影响采购合并边界

/home/ubuntu/odoo-temp/addons/purchase_stock/models/stock_rule.pypurchase_order_line.py 里,可以看到两个很关键的设计:

  • procurement merge 的 groupby 会把 orderpoint_id 考虑进去
  • _find_candidate() 在没有 move_dest_ids 的情况下,会优先只和同一 orderpoint_id 或空 orderpoint_id 的行合并

它想避免的问题是:

  • 不同补货规则本来是给不同位置、不同补货逻辑服务
  • 结果你把它们随便合并在一行里
  • 后面“谁的在途量算给谁”会变得说不清

换句话说:

orderpoint 不只是补货入口,也是在保护采购行和补货来源之间的可追踪性。


为什么“Set Supplier”动作会顺手把数量也改掉

product.supplierinfo.action_set_supplier() 里,还有一个非常业务化的动作:

  • 把选中的供应商写回 orderpoint
  • 如果当前 qty_to_order 小于该供应商的 min_qty
  • 就把补货数量直接抬到供应商起订量

这背后体现的原则很成熟:

  • 采购规则不只问“库存差多少”
  • 还要问“供应商愿不愿按这个量卖给你”

所以补货数量不是纯库存算法,它会被供应商商业条件修正。

这也是为什么很多团队以为系统“多买了”,其实是 Odoo 在帮你满足供应商最小起订量。


业务上最容易误解的 5 个点

1. 以为 orderpoint 只决定补货数量

实际上它也影响供应商、提前期、采购合并和在途量计算。

2. 以为产品默认供应商就等于补货供应商

orderpoint 可以覆盖,系统也可以动态重算。

3. 以为货还没到仓,就等于“不算已补”

在 Odoo 看来,采购中的量很多时候已经算在 in progress 里。

4. 以为供应商只是参考建议

显式 supplier_id 在补货语境里往往是强信号。

5. 以为补货量一定由最小库存差额直接决定

供应商 min_qty 和 UoM 可能会把采购量抬高。


开发和实施时最该注意什么

1. 排查补货异常时,不要只看 scheduler 日志

还要看:

  • orderpoint 上有没有 supplier_id
  • effective_vendor_id 是谁
  • _quantity_in_progress() 是否已把未到货采购量算进去了

2. 做定制补货逻辑时,别轻易破坏 orderpoint_id 追踪

否则你会很难解释:

  • 在途量到底算给哪个补货规则
  • 为什么某些 orderpoint 一直不再触发采购

3. 如果用户允许临时换供应商,界面上最好同时展示“指定供应商”和“生效供应商”

这能显著减少“系统没按我设定执行”的误会。

4. 多仓多位置场景一定要测 location_final_id 语义

因为在途量汇总是按位置口径来的,不是简单按产品总量来的。


一句话记忆法

Odoo 的 orderpoint 不只是“低库存触发器”,它同时决定向谁买、哪些量已经在补,以及这次采购该不该继续下。

DISCUSSION

评论区

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