补货调度

Odoo 补货调度为什么不是“一个 orderpoint 报错整批就废了”:savepoint、失败剔除与 warning activity 讲透

很多人一看到 replenishment scheduler 报错,就直觉认为这一轮补货全军覆没。但在 stock_orderpoint.py 里,Odoo 实际采用的是“savepoint + 聚合异常 + 失败项剔除 + 剩余继续”的处理方式。本文聚焦 orderpoint 调度本身,解释为什么 warning activity 往往代表部分失败而不是整批失败。

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

先说结论

Odoo 的 replenishment scheduler 并不是“一个 orderpoint 出错,整批补货全部作废”。

stock_orderpoint.py 里,官方更像是在做一件很务实的事:

  • 把一批 orderpoint 先转成 procurements
  • 用 savepoint 包住本轮执行
  • 如果部分 procurement 失败,就把失败项挑出来
  • 剩下还能跑的继续跑
  • 对失败项再留下 warning activity,方便后续人工处理

所以你在界面上看到 warning,并不自动等于:

这一轮 scheduler 什么都没做成。

很多时候,它真正表达的是:

这轮补货里有一部分失败了,但另外一部分已经继续成功处理。


为什么这个差异很重要

如果把 scheduler 错误一律理解成“整批失败”,运维动作就会走偏:

  • 误判库存异常范围
  • 重复手工触发补货
  • 对已经成功生成的采购/调拨/制造单再次补单
  • 把 warning 当成系统全停,而不是局部补货点需要修复

对实施团队来说,这种误判非常常见。

因为界面上最终留下的是 warning,用户天然会以为“这一轮都没成功”。

但源码不是这样设计的。


stock.rule.run() 先把失败组织成批量异常

orderpoint 调度的上游,是 stock.rule.run()

它会:

  • 给 procurement 补默认值
  • 找匹配 rule
  • 按 action 分组执行
  • 把找不到 rule 或执行失败的项累积进 ProcurementException

关键点在于:

ProcurementException 里装的不是一条字符串,而是一组失败 procurement 与错误消息的配对。

这意味着传回 orderpoint 调度层的,本来就不是“单条异常”,而是:

这一批里哪些补货需求失败了。


orderpoint 调度的核心不是“捕获异常”,而是“失败隔离”

stock_orderpoint.py 里,调度逻辑的关键步骤是:

  1. 先构造本批次 procurements
  2. with self.env.cr.savepoint() 里调用 stock.rule.run()
  3. 如果捕获到 ProcurementException
  4. 就把失败 procurement 还原回对应 orderpoint
  5. 把失败 orderpoint 从当前批次里减掉
  6. 剩余 orderpoint 继续跑下一轮

这套设计非常值得运维人员记住。

它表达的是一个明确态度:

坏掉的补货点应该被隔离出来,而不是拖死整批计划。


savepoint 在这里解决的到底是什么问题

很多人知道 savepoint,但不知道它在 Odoo 调度里的实际价值。

放在这个场景里,它的意义是:

  • 某批 procurements 里有问题时,可以回滚这一小段尝试
  • 但不用把更外层整个调度事务一起炸掉
  • 于是系统有机会识别“谁失败了”,再继续处理剩下可行项

所以 savepoint 在这里不是性能技巧,而是:

支持局部失败、局部继续的事务边界。


为什么会留下 warning activity

源码在汇总失败 orderpoint 后,还会到产品模板上检查是否已有相同 warning activity。

如果没有,就新建一条 warning activity。

这背后的设计很现实:

  • 补货失败不一定要立刻中断所有工作流
  • 但必须留下一个可追踪、可分派的后续处理入口

所以 warning activity 的角色不是“再报一次错”,而是:

  • 让负责人知道哪个产品/补货点需要人工介入
  • 避免同一个错误被无休止重复创建提醒

这也是为什么你看到 activity 时,更应该把它理解成运维待办,而不是单纯的日志回显。


实战里该怎么区分“部分失败”和“整批失败”

我更建议按下面顺序判断:

1. 先看是否有新建出的业务单据

比如采购单、内部调拨、制造单是否已有部分生成。

2. 再看 warning activity 对应的是哪些产品/模板

如果 warning 只落在部分产品上,通常就不是整批失败。

3. 再查失败项共性

重点看:

  • route 缺失
  • vendor 缺失
  • BOM 缺失
  • location 配置不完整
  • 多公司上下文不匹配

4. 最后再决定是否需要重新触发 scheduler

不要因为有 warning 就直接整批重跑,否则很容易把已经成功的补货又触发一遍。


最容易出现的误区

误区 1:warning = 整批失败

不对。warning 很多时候只是局部失败留下的后续处理标记。

误区 2:有异常就说明 savepoint 没起作用

恰恰相反。能把失败项隔离出来并继续处理其他项,正是 savepoint 在发挥作用。

误区 3:重跑 scheduler 一定安全

如果前一轮已经成功处理了部分 orderpoint,盲目整批重跑可能造成重复单据或重复补货判断。


最后的判断句

在 Odoo 里,orderpoint scheduler 的本质不是“要么全成功,要么全失败”,而是:

把一批补货点放进可回滚的小事务里,识别失败项、剔除失败项,并让其余可执行项继续推进。

理解了这一点,你就会明白:

  • warning activity 是失败隔离后的待办痕迹
  • scheduler 报错不一定代表整批停止
  • 真正稳的运维动作,不是看见 warning 就重跑,而是先判断这一轮到底是“部分失败”还是“全局失败”

DISCUSSION

评论区

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