先说结论
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 里,调度逻辑的关键步骤是:
- 先构造本批次 procurements
- 在
with self.env.cr.savepoint()里调用stock.rule.run() - 如果捕获到
ProcurementException - 就把失败 procurement 还原回对应 orderpoint
- 把失败 orderpoint 从当前批次里减掉
- 剩余 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
评论区