Push Rule 调试

Odoo Push Rule 为什么总像“做完才出现下一步”:_push_apply、透明规则与链式库内流转讲透

很多人学了 route 和 rule,还是会在 push rule 上反复迷路:为什么有的下一步 move 是事后冒出来的,有的只是改目标库位,有的又会把下游链路接过去。本文从 _push_apply 出发,把 push rule 的真实触发时机和调试顺序讲透。

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

很多人会配 Odoo route,也知道 push rule 跟 pull rule 不一样,但一到现场排错还是会糊:

  • 为什么这一步 done 了,下一步 move 才突然出现?
  • 为什么有时系统不新建 move,只是把目的库位改了?
  • 为什么某些下游依赖链被转接过去,有些却断掉?

这些问题如果只用“push 是到货驱动”一句话去解释,远远不够。真正要看懂,得进源码里的 _push_apply()stock.rule._run_push()

push rule 的根问题不是“补货”,而是“到站以后怎么继续走”

pull rule 的核心问题是:

下游有需求时,上游要怎么被拉出来?

push rule 的核心问题则是:

某个 move 已经把货送到一个位置后,接下来是否还应该立刻继续往后推进?

所以 push rule 的思维时点天然更靠后。

它不是围绕“需求缺口”组织的, 而是围绕“货到了某个目的地以后,是否触发下一跳”组织的。

这也是为什么很多人会产生一种主观感受:

Push rule 看起来总像“做完才开始工作”。

这个感觉并没有错。

_push_apply() 才是读懂 push 的真正入口

如果你只看 stock.rule._run_push(),容易把它误解成“只是一条创建 move 的工厂函数”。

真正有意思的,是 stock.move._push_apply() 做的事:

  1. 以当前 move 的 location_dest_id 为落点去找匹配的 push rule;
  2. 规则命中后,判断是 transparent 还是需要 manual operation;
  3. 必要时创建新 move;
  4. 处理 move_dest_ids 的转接或断链;
  5. 对新 move 做 confirm,让链路继续长出来。

也就是说,push 不只是“造下一张单”,它还会重排依赖关系

为什么有时不新建 move,只是改目标库位

这就是很多人第一次读到 auto == 'transparent' 时会愣住的地方。

transparent push rule 的语义不是“新加一步人工作业”,而更像:

这段中间节点对业务来说不值得单独落成一张新 move,直接把当前 move 的目标改到下一跳即可。

源码里它会:

  • 记住旧目标库位;
  • 把当前 move 的 location_dest_id 改成规则的目标;
  • 若已有 move line,还同步改 line 的目标库位;
  • 如果目标发生变化,再递归看后面是否还有下一条 push。

所以 transparent 的重点不是“没规则”,而是规则生效方式是改写当前动作,而不是增殖下一张动作

这对多步仓设计很关键。

因为并不是每个物流节点都值得单独变成一张拣货单。有些节点只需要保留路由语义,不需要保留独立作业单据。

manual operation push 才是大家最熟悉的“生成下一步 move”

如果 rule 不是 transparent,源码会走复制 move 的路径。

这里的关键不是“复制一张一样的 move”,而是:

  • 新 move 的来源,改成当前 move 的目的地;
  • 新 move 的目标,改成 push rule 指定的下一站;
  • 新 move 会带自己的 picking type、warehouse、rule_id;
  • 在某些场景下,还会重写 procure_method

所以新 move 并不是旧 move 的镜像,而是“把当前落点变成下一跳起点”的链式延伸。

这正是 push rule 能表达库内连续搬运的核心。

为什么 move_dest_ids 会被重新接线

这是 push 调试里最容易被忽略、但最有价值的细节。

当一个 move 因 push 规则产生了新 move 后,源码不会简单让原 move 和所有旧 downstream 关系继续混在一起。

它会根据场景决定:

  • 哪些后续依赖应该转接到新 move;
  • 哪些关系应该与原 move 断开;
  • 哪些 move 需要从 MTO 依赖里被 break 掉。

这说明 push rule 不只是追加流程,而是在维护因果图

  • 如果货已经不是“直接从当前 move 到最终依赖”,那依赖关系也该重新挂;
  • 如果中间多了一跳,真正的供应者也应该变成这张新 move。

因此现场上你看到“明明是一条库内规则,怎么把下游链路也改了”的时候,不是系统多事,而是它在努力保持依赖图真实。

push rule 和 pull rule 的根本差异,不止触发时机

当然,触发时机是第一层:

  • pull:需求先来;
  • push:货先到站。

但更深的一层是:

  • pull 更像“为满足需求,向上游要一笔供给”;
  • push 更像“当前这笔供给到了这里,还必须继续往后走”。

这两种机制对依赖图的改写方式完全不同。

所以你不能用 pull 的眼镜去看 push。

尤其不要把 push 理解成“另一种 procurement”。它更像库存动作完成后的流向接力器。

为什么 return move 默认要避开 push 再次触发

源码里还有个非常实战的保护:

如果当前 move 是 return 相关,系统会尽量避免让 push rule 再把它重新推进一遍,防止链路重复生成。

这个保护很重要,因为退货本来就是一种“逆向修正动作”。 如果它再按正向 push 规则继续自动推,就很容易制造双倍链路或错误中转。

所以你在排退货 / 逆向物流时,看见 push 不像普通出入库那样触发,往往是有意的。

实战调试顺序

遇到“为什么 push 没生效 / 生效方式不对”时,建议按这个顺序看:

  1. 当前 move 的 location_dest_id 到底是不是规则命中的落点;
  2. 规则是 transparent 还是 manual operation;
  3. move 当前是不是 return 场景;
  4. 规则是否有 push_domain 限制,导致匹配后又被排除;
  5. 新 move 的 picking type、warehouse、procure_method 是否符合预期;
  6. move_dest_ids 有没有被重新转接到新 move;
  7. 新 move confirm 后,是否又继续触发了下一层 push。

这套顺序比单纯去看“有没有新建拣货单”要有效得多。

最后的结论

Push rule 真正解决的问题不是“怎么补货”,而是:

一笔货到了这个节点后,系统要不要继续替你把下一跳组织出来。

因此它天然带着三层语义:

  • 触发时点在 move 落点之后;
  • 规则可能只是改写当前 move,也可能新建下一张 move;
  • 依赖链并不会原封不动,而是要按新的物流现实重新接线。

理解了这三层,你再看 Odoo 的库内链式流转、跨区中转、透明节点和人工步骤,就不会再把 push 当成“晚一点执行的 pull”了。

DISCUSSION

评论区

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