Pull Rule

Odoo Pull Rule 为什么总像“上一张单据凭空出现”:目标库位找规则、来源库位建 move 与上游补货链讲透

很多人第一次看 Pull Rule,会觉得系统像“倒着长单据”:明明只是下游库位缺货,怎么上一段调拨、采购或制造动作就自己冒出来了?从 stock_rule.py 看,Pull Rule 的关键不是“货往哪走”,而是“哪里产生需求、系统就去哪里找能把货拉过来的规则”。

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

很多人第一次理解 Odoo route 时,会把 Push Rule 和 Pull Rule 都看成“库存移动规则”,然后觉得它们只是触发时机不同:

  • Push:货到了就往后推;
  • Pull:缺货了就从前面拉。

这个理解不算错,但还不够。

如果你真正看 /home/ubuntu/odoo-temp/addons/stock/models/stock_rule.py,会发现 Pull Rule 最核心的思维不是“货现在在哪”,而是:

哪个目标库位出现了需求,系统就围绕这个目标库位去寻找一条能把货拉过来的补货规则。

这也是为什么它总给人一种强烈的“倒着长单据”感:

  • 先看到下游要货;
  • 然后上游 move、采购、制造动作才被补出来。

一、Pull Rule 不是在“扫描现有单据”,而是在处理 procurement

run() 的入口非常重要。

它处理的不是某张现成调拨单,而是一组 procurement,也就是:

  • 某产品;
  • 某数量;
  • 某目标 location;
  • 某计划日期;
  • 某上下文 values。

也就是说,系统先拿到的是一句抽象需求:

  • “这个产品,需要在这个库位、这个时间点可用。”

然后才决定:

  • 是从库存拿;
  • 还是触发上游规则;
  • 还是根本找不到规则直接报错。

所以 Pull Rule 的视角天然是需求驱动,不是“已有物流单据驱动”。

二、为什么 _get_rule() 是按目标库位往上找,不是按来源库位往下找

很多人脑中的查找方式是:

  • 货应该从哪里来?
  • 那我去那个来源库位找规则。

_get_rule(product_id, location_id, values) 做的恰恰相反。

它拿到的是当前需求所在的 location_id,然后:

  1. 从这个目标库位开始;
  2. 沿 location 父层级一路往上;
  3. 结合 route、packaging、product routes、warehouse routes;
  4. 在这些候选里选最合适的一条 rule。

这非常关键。

因为 Pull Rule 表达的是:

“如果某个目标位置需要货,应该用哪条制度把货补过来?”

所以优先锚定的是目标位置,不是来源位置。

这也是很多调试误判的根源:

  • 你一直盯来源库位配没配规则;
  • 但系统实际是在目标库位这一侧找“谁负责满足这里的需求”。

三、规则选择并不是“找到一条就算”,而是 route、warehouse、location 三层一起决定

_search_rule_for_warehouses()_search_rule() 展示了 Odoo 选 rule 的真实复杂度。

候选来源通常包括:

  • procurement 自己带进来的 route_ids
  • 包装类型带的 route
  • 产品和品类上的 route
  • warehouse 上的 route

同时还会按:

  • location_dest_id
  • warehouse_id
  • route_id

做分组和排序,顺序上又受:

  • route_sequence
  • rule.sequence

影响。

翻成人话就是:

Pull Rule 不是“产品打了哪个 route 就永远按那个走”,而是在“目标库位 + 仓库 + route 来源 + 排序优先级”这几个维度上共同决策。

所以现场出现“同产品在不同仓、不同位置、不同补货上下文下走法不同”,其实很正常。

四、_run_pull() 为什么总让人觉得“上一张 move 是系统补出来的”

_run_pull() 的动作很直接:

  1. 先校验 rule 是否有 location_src_id
  2. 再按 procurement + rule 生成 move values;
  3. 以 sudo 创建 stock.move
  4. 最后调用 moves._action_confirm()

这套动作看起来平平无奇,但恰恰是 Pull Rule 那种“倒着长链”的来源。

因为下游只是在说:

  • 我要在目标位置有货。

然后 _run_pull() 就根据 rule 反推出:

  • 那应该从哪个来源位置建一张 move,把货往这里拉。

于是用户在界面上会感受到:

  • 明明只是销售单确认了;
  • 或者补货建议点了确认;
  • 怎么突然多了一张仓间调拨,甚至再往上又多了采购 / 制造。

其实不是“凭空生成”,而是需求经由 rule 被逐层物化成 move

五、_get_stock_move_values() 才是 Pull Rule 把抽象需求落地成物流动作的地方

这个方法里最值得抓的几个字段是:

  • location_id = self.location_src_id.id
  • location_final_id = location_dest_id.id
  • move_dest_ids
  • procure_method
  • picking_type_id
  • date / date_deadline
  • procurement_values

这说明 Pull Rule 不是只说“从 A 到 B”,它还在同时确定:

  • 这段 move 属于哪种 operation type;
  • 它和下游 move 怎么串(move_dest_ids);
  • 计划时间怎么倒推出去;
  • 后续还需要哪些上下文继续传递。

特别是 move_dest_ids 很关键。

它让上游 move 和下游需求形成明确连接。

所以 Pull Rule 的核心不只是建一张上游单,而是:

把“我为什么要建这张上游单”也一起编码进链路里。

这就是为什么很多 traceability、smart button、依赖取消传播能顺着需求链继续工作。

六、为什么 date 会往前倒推

_get_stock_move_values() 里会把 values['date_planned'] 再减去 rule 的 delay,得到 move 的计划日期。

这说明 Pull Rule 不只是决定“从哪拉”,还决定“要提前多久开始拉”。

所以实施里很常见的一种错觉是:

  • 我明明要 3 月 30 日交货,怎么仓库动作被排到更早?

因为 Pull Rule 的视角是:

  • 目标位置在 3 月 30 日必须有货;
  • 那来源位置的动作就得提前若干天发生。

这不是日期错了,而是 supply chain 倒排本来就该这样。

七、procure_method 为什么会影响 Pull Rule 是“继续往上找”还是“先吃库存”

虽然 _run_pull() 默认会把 mts_else_mto 先转成 make_to_stock 进入 move,但整体补货语义仍由 procure_method 控制:

  • make_to_stock:先从来源库存满足;
  • make_to_order:忽略现有可用量,继续向上触发规则;
  • mts_else_mto:能吃库存就吃,不够再往上补。

这就是为什么两条看起来很像的 route,最终补货体感完全不同:

  • 一条会优先消耗源库现货;
  • 另一条会继续长出采购或制造;
  • 还有一条是“不够的部分才继续往上追”。

所以 Pull Rule 不是单独运作的,它和 procure_method 一起决定了“需求回拉的深度”。

八、为什么创建 move 时要 sudo()

_run_pull() 里创建 move 明确用了 sudo().with_company(company_id).create(...)

注释已经点明:

  • 当前触发采购链的人,不一定有库存对象创建权限;
  • 比如销售动作触发了 MTO 补货。

这意味着 Pull Rule 其实承担的是一种系统级补货职责,而不是单个业务用户手工发起的普通库存操作。

所以很多时候你不能用“当前用户在库存里有没有这个权限”来理解它为什么还能继续长单。

它表达的是:

  • 这个业务动作有权触发补货制度;
  • 具体补货文档由系统以更高权限生成。

九、最容易踩坑的几个调试顺序

1)报“找不到规则”时,先看目标库位和 route,不要先看来源库位

因为 _get_rule() 是围绕目标位置找“谁来满足这里的需求”。

2)出现“上一张 move 怎么自己长出来了”时,先看 move_dest_ids 和 procurement 来源

多数情况下不是凭空长,而是下游需求把上游动作拉出来了。

3)日期看起来提前了,不一定是 bug,先看 rule.delay

Pull Rule 天然会为目标可用时间做倒排。

4)为什么有时继续触发采购,有时只从源库拿货,先看 procure_method

不要把所有 Pull Rule 都当成纯 MTO。

5)为什么不同仓同产品表现不一致,先看 warehouse routes 与 route sequence

规则选择不是只看产品。

总结

Pull Rule 最值得记住的一句话是:

它不是“货从哪里发出”的说明书,而是“某个目标位置一旦缺货,系统该怎样沿着规则把上游补货动作倒着长出来”的制度。

所以它看起来总像“上一张单据凭空出现”,本质上是因为你在界面上先看到了需求端,而 Odoo 在后台又把满足这个需求所需的上游动作补齐了。

把这个逻辑想明白之后,很多库存疑难都会顺:

  • 为什么仓间调拨自己冒出来;
  • 为什么采购 / 制造会被继续触发;
  • 为什么计划日期总往前;
  • 为什么 route 明明配了却没命中;
  • 为什么调试时应该从目标库位往回查,而不是从来源库位往前猜。

这才是 Pull Rule 真正强大的地方:

它让 Odoo 不只是“记录物流动作”,而是能把需求、规则、时间和上游补货链串成一条可执行的制度链。

DISCUSSION

评论区

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