装箱流程

Odoo Put in Pack 为什么不是“选几行装个箱”:目的库位校验、picked 优先与发运包裹工作流讲透

站在操作员视角,Put in Pack 像是一个很轻的按钮;但源码里它会先检查 destination、决定是否弹 package type 向导、优先处理 picked 行、递归处理已有 package,甚至自动打印包裹标签。本文把这条发运包裹工作流讲透。

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

很多人第一次教仓库同事用 Odoo 时,都会把 Put in Pack 描述得很轻:

  • 选几行;
  • 点一下按钮;
  • 生成一个箱号。

这个说法对培训有帮助,但对排错帮助不大。

因为源码里的 action_put_in_pack() 真正在保护的,不只是“生成包裹”,而是整条发运包裹工作流

哪些行现在允许装箱、它们是否指向同一目的地、要不要先定义 package type、已拣货与未拣货谁优先、已有包是否继续嵌套,以及装箱后要不要立刻打标签。

所以 Put in Pack 不是一个 UI 小动作,而是仓内执行秩序的入口。

一句话先讲透

Odoo 的 Put in Pack 至少串了 5 层判断:

  1. 先看 destination 是否一致,不一致就不允许直接打包;
  2. 再看 operation type 是否要求先选 package / package type;
  3. 选取待装箱对象时优先 picked 行;
  4. 需要时不只装 move line,也会继续把已有 package 往外层 package 里装;
  5. 装箱完成后还能按 operation type 自动打印 package label。

这就是为什么“只是打个包”背后会冒出向导、弹窗和标签动作。

第一步为什么先查 destination

_pre_put_in_pack_hook() 里第一件事不是建 package,而是 _check_destinations()

如果待装箱行的 location_dest_id 不一致,系统不会假装看不见,而是直接弹“Choose destination location”向导。

这层保护很重要,因为在仓库现实里:

  • 一个运输箱里的货,通常应该去同一条后续路径;
  • 如果目的地都不同,还强行先装成一个包,后面要么拆箱,要么让 traceability 混乱。

所以 Odoo 在装箱前先保证目的地语义一致,不是多此一举,而是在阻止错误包装结构。

为什么有时一按按钮先弹 package type 向导

很多团队会疑惑:

  • 同样都是 Put in Pack;
  • 为什么有时直接生成包裹;
  • 有时却先弹让你选 package type。

答案在 _should_display_put_in_pack_wizard()

  • 如果 operation type 开启了 set_package_type
  • 且你这次没显式传 package / package type / package name;
  • 系统就会要求先补 package 定义。

也就是说,Odoo 允许企业把装箱从“临时生成箱号”升级为“带包装规格约束的标准流程”。

这对以下场景尤其有价值:

  • 不同承运商要求不同包材;
  • 包装规格会影响后续标签模板;
  • 需要区分信封、纸箱、托盘等不同作业容器。

为什么系统优先装 picked 行

_get_lines_and_packages_to_pack() 有个很容易被忽略的设计:

  • 只要存在 picked 的 move line,系统就优先处理 picked 行;
  • 未 picked 的行会暂时被忽略。

这背后的意思很明确:

打包优先服务于已经进入实际作业状态的货,而不是把“计划里要拣的货”和“手上已经拣到的货”混装。

对现场执行来说,这能明显减少两类错误:

  • 纸箱已经打出来了,但实际还有一半货没拣到;
  • 系统层面看似装箱完成,现场却仍然在补货。

为什么 Put in Pack 不只处理散行,还会递归处理已有 package

同一个 action_put_in_pack() 流程里,系统会把对象拆成两类:

  • 还没有 result_package_id 的 move line;
  • 已经在 package 里的内容,进一步提取其 outermost_package_id

这意味着 Odoo 支持的不是单纯“散货入箱”,而是连续包装:

  • 先把商品装进小箱;
  • 再把多个小箱装进大箱;
  • 最后按最外层运输包继续流转。

这条链路和普通 package 字段介绍不同,它强调的是操作流程连续性,而不是单个字段含义。

为什么装箱后有些旧 package 关系会被清掉

stock.package.action_put_in_pack() 和 move line 的写入 / unlink 逻辑里,都有一个重要清理动作:

  • 如果某些 package 的 package_dest_id 还在;
  • 但它已经不再关联任何 active picking;
  • 系统会把这段失效的目标包关系清掉。

这层设计解决的是“包装树残影”问题。

否则你一旦反复重装、拆包、重分流,系统里会留下很多看似还挂在外层箱上的旧关系,后续追踪会越来越假。

自动打印标签为什么挂在 Put in Pack 后面

_post_put_in_pack_hook() 里,如果 operation type 开启了:

  • auto_print_package_label

系统会根据 package_label_to_print 决定输出 PDF 还是 ZPL。

这说明 Odoo 认定装箱和标签不是两件松散动作,而是一条紧邻链路:

  • 货一旦真正装入运输包;
  • 包裹身份就应该被立即固化成标签。

这对现场最直接的好处是:

  • 避免先装箱、后面忘打标签;
  • 降低箱号与实体箱脱节的概率;
  • 把包装动作和可追踪载体绑定在同一个时刻。

实战里最该先排查什么

只要遇到 Put in Pack 行为异常,我建议先别急着看包裹最终结果,先按这个顺序拆:

  1. 待装箱行的 destination 是否一致;
  2. operation type 是否要求 set_package_type
  3. 当前有无 picked 行,系统是不是因此只取了部分行;
  4. 这次是在装散行,还是在把现有 package 往外层继续装;
  5. 标签自动打印是否由 operation type 配置触发。

这样基本能把“为什么弹窗”“为什么少装了几行”“为什么直接出标签”这些问题拆开。

最后的结论

Put in Pack 在 Odoo 里从来不是一个“给箱子起名字”的按钮。

它真正负责的是:

  • 包前校验目的地;
  • 决定是否要求包装规格;
  • 以 picked 事实驱动装箱;
  • 维护包中包链路;
  • 在装箱完成的瞬间输出发运标签。

所以你如果想把装箱流程做稳,关注点不该只放在 result_package_id,而要放在整条从拣货到发运包裹固化的工作流上。

DISCUSSION

评论区

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