整包语义

Odoo 整包操作为什么不是“把几条 move line 塞进箱子里”:package level、entire pack 和 result_package_id 真正分工

很多人把 package 理解成仓库界面里的一个箱子标签,但 Odoo 真正难的地方在于:什么时候只是把行放进包里,什么时候已经是“整包作业”语义。本文把 package level 讲透。

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

先说结论

在 Odoo 里,“给 move line 填上一个 result_package_id“系统认定这是一笔整包作业”,不是一回事。

前者更像是:

  • 这些明细最后装进了哪个包

后者表达的则是:

  • 这次作业能不能被系统当成一个完整 package 单元来处理、搬运、继续传递和下游识别

所以 package 不是简单的物流标签。真正难点在于:

Odoo 什么时候认为你是在处理“若干行”,什么时候认为你是在处理“一个完整包裹”。


为什么很多人会把 package 想浅了

因为在界面上,最显眼的动作是:

  • Put in Pack

看起来像是:

  • 选几条行
  • 点一下按钮
  • 系统生成一个 package

于是很容易误会成:

  • package 就是一个容器字段
  • result_package_id 就是“装箱后显示一下”

但源码往下看会发现,package 会进入:

  • quant 维度
  • reservation 维度
  • result_package_id / package_id 的源包与目标包语义
  • _check_entire_pack() 这类完整包判断
  • package level 相关聚合

这说明 Odoo 处理的不是“箱子外观”,而是包装层级上的库存结构


package_idresult_package_id 先别混

这是理解整包语义的第一步。

可以先用最朴素的话记:

  • package_id:货原本在哪个包里
  • result_package_id:这次操作后,货要进哪个包

也就是说:

  • 一个是来源包
  • 一个是结果包

这两个字段一旦混掉,你就会把很多场景看错,比如:

  • 拆包后重装
  • 同包转移
  • 从旧包挪进新包
  • 整包拣起又整包放下

Odoo 之所以能区分“包从哪里来、包最终变成什么”,靠的就是这两个方向性的字段,而不是单一一个 package 标记。


action_put_in_pack() 真正做的不是“生成个箱号”

stock.move.line.action_put_in_pack() 往下走时,会先做两件很关键的事:

  1. 通过 _get_lines_and_packages_to_pack() 选出哪些 move line 能打包
  2. 再用 _put_in_pack() 给这些行写入 result_package_id

看起来好像仍然只是“写字段”,但里面藏着两个很重要的判断:

第一,picked 优先

如果已经存在 picked 的 move line,系统会优先打包这些 picked 行,而忽略还没 picked 的行。

这背后的设计不是技术细节,而是业务保护:

打包动作优先服务于已经被确认正在作业的那一部分货,而不是把尚未真正拣起的行也一起假装打包。

第二,递归打包 package

如果一部分内容本身已经在 package 里,Odoo 不只是打包散行,还可能继续把 package 往更外层 package 里装。

也就是说,系统天然支持:

  • 行进包
  • 包再进更大包
  • 外层包继续形成层级

这已经不是简单“给箱子编号”,而是包装树结构。


entire pack 的难点:不是“同包”就等于“整包”

很多人会把“这些货都在一个 package 里”理解成“我正在做整包操作”。

但源码不是这么粗暴判断的。

_check_entire_pack() 与相关逻辑真正想确认的是:

  • 这次 picking 下,某个 package 的相关 move line 是否构成一个完整、可整体处理的包
  • 有没有只动了包里一部分货
  • 有没有同一个包在多个 transfer / 多种条件下被拆散

所以 entire pack 的重点不是“同名 package”,而是:

这个包是不是被这次动作完整而一致地接住了。

只要你对同一个包:

  • 只挑了一部分行
  • 目标不一致
  • picking 之间被拆开
  • 中途改写了 result_package_id

那它就可能不再被系统视为一个干净的整包单元。


为什么 package level 比 move line 更像“仓库操作语言”

move line 是明细层。

它表达的是:

  • 什么产品
  • 数量多少
  • 从哪到哪
  • lot / owner / package 是谁

但 package level 更像仓库执行语言,它表达的是:

  • 这一个包装单元被整体搬运
  • 这批作业可以按包聚合
  • 下游界面与逻辑可以不必先拆回逐行理解

所以当你在做整包拣货、整托移动、扫码整包过站时,真正重要的不是“我有多少行”,而是“系统能不能把这一坨行当成一个 package 级单元”。


为什么 result_package_id 还会影响后续 putaway

_action_assign() 结束后,move line 还会走 _apply_putaway_strategy()

_put_in_pack() 在某些情况下也会根据 package 去算默认目标库位。

这意味着 package 不只是“包装结果”,它还会继续影响:

  • 目标库位重定向
  • putaway 行为
  • 后续包级移动

换句话说:

包裹结构一旦成立,后面的库位决策也可能因此变化。

所以 package 不是出库最后一步的摆设,而是会继续向后传导影响的结构信息。


实战里最容易踩的 5 个坑

1. 把 package_idresult_package_id 当成同义词

一个是来源包,一个是结果包。

2. 以为 Put in Pack 只是界面装箱动作

它其实在建立包级结构。

3. 以为同一个包下的行天然就是 entire pack

只要只动了一部分,整包语义就可能破。

4. 以为 package 不影响后续库位或 reservation

实际上它会继续进入 quant 与策略层。

5. 只从 move line 看问题,不从 package level 看

这样很难解释扫码整包、整托搬运和多层包装的真实语义。


一句话记忆法

在 Odoo 里,package 不是“箱子标签”,而是库存结构;result_package_id 只是结果落点,而 package level / entire pack 才是在表达系统能不能把这次动作当成一个完整包装单元来处理。

理解这句话,你再看整包拣货、整包转移和扫码包装,就不会只盯着几条 move line 了。

DISCUSSION

评论区

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