策略边界

Odoo 明明配了 FIFO/FEFO,为什么系统还是没按你想的那批货出:removal strategy “失效感”的真相

很多团队已经配置了 FIFO、FEFO、LIFO 甚至 least packages,但出库时还是觉得系统“没按规则来”。问题往往不在于策略没生效,而在于你把策略影响范围想大了。

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

先说结论

很多人配置了 FIFO、FEFO、LIFO 或 least packages 之后,还是会抱怨:

  • 为什么系统没有先出我预期那批货?
  • 为什么看起来像随机选?
  • 为什么策略明明开了却像没生效?

大多数时候,removal strategy 并没有失效

真正的问题是:

你以为 removal strategy 决定“所有库存选择”,但源码里它只在“已经满足其他边界条件的候选 quant 之间”决定优先顺序。

也就是说,策略只管排序,不管替你突破 lot、package、owner、strict、tracking、location 这些前置边界。


removal strategy 的权力没有你想的那么大

stock.quant._gather() 的逻辑非常值得看。

它先做的是:

  1. 根据 product / location / lot / package / owner / strict 生成 domain
  2. 再决定 removal strategy
  3. 最后按策略顺序 search / sort quant

这说明顺序是:

  • 先过滤候选集
  • 再在候选集里排序

所以如果你期待的那批 quant 一开始就不在候选集中,那么:

  • FIFO 不会把它变出来
  • FEFO 也不会越权把它拿进来
  • least packages 更不会绕开 owner / package / location 约束

这就是很多“策略失效感”的根本原因。


第一种误会:你在看全仓,系统在看当前 domain

_get_gather_domain() 会先决定本次到底允许哪些 quant 参与比较。

这里最重要的几个边界是:

  • location_id
  • lot_id
  • package_id
  • owner_id
  • strict
  • 有效期相关的 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

评论区

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