先说结论
很多人配置了 FIFO、FEFO、LIFO 或 least packages 之后,还是会抱怨:
- 为什么系统没有先出我预期那批货?
- 为什么看起来像随机选?
- 为什么策略明明开了却像没生效?
大多数时候,removal strategy 并没有失效。
真正的问题是:
你以为 removal strategy 决定“所有库存选择”,但源码里它只在“已经满足其他边界条件的候选 quant 之间”决定优先顺序。
也就是说,策略只管排序,不管替你突破 lot、package、owner、strict、tracking、location 这些前置边界。
removal strategy 的权力没有你想的那么大
stock.quant._gather() 的逻辑非常值得看。
它先做的是:
- 根据 product / location / lot / package / owner / strict 生成 domain
- 再决定 removal strategy
- 最后按策略顺序 search / sort quant
这说明顺序是:
- 先过滤候选集
- 再在候选集里排序
所以如果你期待的那批 quant 一开始就不在候选集中,那么:
- FIFO 不会把它变出来
- FEFO 也不会越权把它拿进来
- least packages 更不会绕开 owner / package / location 约束
这就是很多“策略失效感”的根本原因。
第一种误会:你在看全仓,系统在看当前 domain
_get_gather_domain() 会先决定本次到底允许哪些 quant 参与比较。
这里最重要的几个边界是:
location_idlot_idpackage_idowner_idstrict- 有效期相关的
with_expiration
所以你脑子里想的是:
- 仓里明明还有更早的一批
系统想的是:
- 但那批不在这次合法 domain 里
于是你感受到的就是:
- FIFO / FEFO 好像没按预期工作
其实不是策略错了,而是你看的候选集和系统看的候选集根本不是同一批。
第二种误会:FEFO 也不是“永远先拿最早到期那批”
在源码里,带有效期过滤时会把 removal_date 纳入 domain。
也就是说,FEFO 先要解决的问题不是“谁最早到期”,而是:
- 哪些 quant 在本次条件下可参与
- 哪些 quant 满足有效期相关边界
之后它才在这些候选里按顺序决定谁优先。
所以如果某批看起来“更该先出”的 lot 因为别的边界没进候选集,你就会误以为 FEFO 失效。
其实它只是没有机会比较到那批货。
第三种误会:least packages 不是“永远少拿几包”这么简单
least_packages 在源码里甚至不是普通排序,而是会跑一段更复杂的 A* 风格搜索来找更合适的 package 组合。
这说明它的目标不是简单的:
- 按时间排一下
而是:
- 在候选 quant 中尽量找到更少 package 数的解
但注意,它仍然建立在“候选 quant 已经合法”的前提上。
所以它解决的是:
- 在合法候选里,怎么更像整包 / 少包操作
而不是:
- 为了凑更少包,允许越过别的库存边界
第四种误会:你看到的“不是 FIFO 顺序”,可能是 unreserve 反向行为
源码里在减少 move 数量时,有一个非常容易被忽略的注释:
- move lines 当初可能是为了尊重 removal strategy 按某种顺序建立的
- 所以 unreserve 时需要按相反顺序释放
这意味着:
你在后续修改、减量、拆单时看到的 move line 变化,不一定是新的 removal strategy 没生效,也可能是系统在反向撤销旧的保留顺序。
这点特别容易让人错把“回滚行为”当成“选货行为”。
第五种误会:策略负责“谁先出”,不是“最终每条 line 长什么样”
真正落地时,_action_assign() 还会继续经过:
- reserve quantity
- move line 生成
- putaway / package / tracking 细化
所以最终你看到的:
- 多条 move line
- 某个 lot 被拆开
- 某包没有完整拿走
不一定是在否定 removal strategy,而可能是执行层把策略结果展开成更细粒度的作业结果。
也就是说:
- removal strategy 决定优先候选
- 执行层决定具体落地形式
不要把这两层混在一起。
遇到“策略没生效”时,最该先查什么
1. 当前 move 的候选 quant domain 是什么
先别急着看 FIFO / FEFO,先看谁有资格参赛。
2. strict 有没有把范围缩小
很多“怎么没拿那批”的根因都在这里。
3. lot / owner / package 有没有把你预期那批排除掉
策略不会帮你突破这些边界。
4. 当前看到的是 reserve 结果,还是 unreserve / 改量后的回滚结果
这两者很容易被看混。
5. 是排序问题,还是执行展开问题
先分清“先选谁”和“最后怎么落 line”。
一句话记忆法
Odoo 的 removal strategy 决定的是“在合法候选 quant 里先拿谁”,不是“全仓随你挑谁”;如果你期待的那批货一开始就没进 domain,FIFO/FEFO 再正确也不会选到它。
理解这句话,你以后再排“策略失效”,就会先去看候选边界,而不是先怀疑系统没按 FIFO。
DISCUSSION
评论区