制造单拆并补

Odoo 制造单什么时候该拆、该并、该补单:Split、Merge 与 Backorder 边界讲透

很多团队把制造单 split、merge、backorder 当成一回事,结果越改越乱。官方源码其实给了三套不同语义:生产前拆批、计划层并单、完工时补单。

制造
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

在 Odoo 里,制造单的 Split、Merge、Backorder 不是三个按钮,而是三种完全不同的业务语义。

它们分别回答的是:

  • Split:一张单太大,要不要先拆成几批跑
  • Merge:几张同类单太碎,要不要并成一张排
  • Backorder:这次只做完一部分,剩下的以后再续

一句话记:

Split 解决“怎么开工”,Merge 解决“怎么计划”,Backorder 解决“怎么收尾”。

如果把这三个动作混着用,就很容易把 move、工单、追溯和上下游链路搅乱。


这三个机制分别在解决什么问题

Split:控制批次和排产粒度

当单张 MO 数量太大时,现场未必想让它一直以一张单的形式存在。

典型原因包括:

  • 工装 / 夹具一次只能跑固定批量
  • 不同班组要分担同一批生产
  • 同一产品要分散到不同日期或责任人

所以 split 的目标不是“补剩余”,而是:

在正式执行前,把一张生产任务拆成多张可排、可派、可控的小任务。

Merge:降低计划碎片化

反过来,如果系统已经长出了多张同产品、同 BOM、同操作类型的 MO,计划层可能更希望把它们并起来:

  • 减少切换次数
  • 合并备料和工位安排
  • 把相同生产任务做成一条更连续的执行链

所以 merge 的目标是:

把多张计划上等价的制造单合成一张新的主单。

Backorder:接受“这次没做完”

backorder 发生在完工边界。

它解决的不是“怎么排更好”,而是:

  • 本次实际完工量小于原计划量
  • 但已经要收当前这次执行成果
  • 剩余量需要保留到后续单据继续跑

所以 backorder 的语义是:

把一条正在执行中的生产链,按已完成和未完成部分正式切开。


Split 的核心链路:先拆明细,再生成新 MO

addons/mrp/wizard/mrp_production_split.py 里可以看到,split 向导不是让你随便点一下就结束。

它会先准备:

  • max_batch_size
  • num_splits
  • production_detailed_vals_ids

也就是说,Odoo 先把“要拆成哪些数量、给谁、排哪天”显式建模出来,再调用 action_split()

真正落地时,向导会调:

  • production._split_productions(...)

然后把每条拆出来的生产单再回填:

  • user_id
  • date_start

这说明 split 不是纯数量动作,它还顺带回答两个执行问题:

  • 谁负责
  • 什么时候开始

这也是为什么它更接近“拆批排产”,而不只是“数学拆分”。


Merge 的核心链路:新建一张主单,再迁移上下游关系

很多人以为 merge 就是把其中一张单数量加总,然后删掉其他几张。

不是。

mrp.production.action_merge() 的实现明显更谨慎:

1. 先校验同类单

系统要求被合并的单在关键维度上兼容,比如:

  • 同产品
  • 同 BOM
  • 同 operation type / picking type 语义

2. 新建一张全新的制造单

源码会创建一个新的 mrp.production,数量是所有原单数量之和。

这很关键。

因为 merge 的本质不是“选一个旧单做宿主”,而是:

创建一张新的中心单,把旧单的计划身份收编进去。

源码会:

  • 更新 linked picking 的 production_group_id
  • 把原有父子 MO 关系挂回新 production group
  • 重新挂 finished move 的 move_dest_ids
  • 把下游 move 的 created_production_id 改到新单

这说明 merge 真正难的地方不在数量相加,而在:

  • 谁是新的来源单
  • 谁还指向它
  • 三步制造下前置 / 后置搬运链要不要继续成立

4. 最后取消原单

原单不是简单删除,而是走取消链并写日志说明“已合并到哪张新单”。

所以 merge 是一次有历史、有追溯、有上下游重挂的计划重组。


Backorder 的核心链路:基于实际产出切开执行链

Backorder 最核心还是 _split_productions(),但触发背景完全不同。

它通常发生在 button_mark_done() 前后:

  • 当前准备完工的数量是 qty_producing
  • 原单目标数量是 product_qty
  • 两者不相等,就可能进入补单逻辑

这时 Odoo 的关注点不再是“怎么排得更漂亮”,而是:

  • 已消耗原料怎么保留
  • 已完工成品怎么留在当前单
  • 剩余量怎么挂到补单继续
  • workorder 已做和未做怎么衔接

所以 Backorder 比 Split 更像“执行态切单”,不是“计划态拆批”。


新手最容易混淆的边界

误区 1:把 Split 当作 Backorder 的前置版

不是。

Split 发生在更偏计划/排产层,强调:

  • 拆批
  • 分日期
  • 分负责人

Backorder 则强调:

  • 本次实际只做了多少
  • 剩余量以后再做

一个是开工前优化粒度,一个是完工时承认剩余

误区 2:把 Merge 理解成“几张单合并显示”

不是。

Merge 会新建主单,并重挂很多上下游关系。你如果有自定义字段、外部系统关联或者报工扩展,不能只看 MO 表头。

误区 3:忽视三步制造里的库位和搬运链

test_warehouse_multistep_manufacturing.py 里能看到,三步制造下:

  • 组件先从 Stock 到 Pre-Production
  • 成品完工后从 Post-Production 回 Stock
  • move_orig_ids / move_dest_ids 是有链的

所以 split / merge / backorder 不是只影响 MO,它们还会影响前后两端搬运 move 的父子关系。

误区 4:以为批次大小只影响 UI

split 向导里 max_batch_sizenum_splits 不是装饰字段。

它们表达的是:

  • 一张 MO 最多以什么批量执行
  • 系统应该自动分成几批

这直接影响排产粒度,而不只是界面显示。


开发时最该注意什么

1. 先分清你在改“计划”还是改“执行”

这是所有自定义前最该问的一句。

  • 要拆批排产:看 split
  • 要并计划:看 merge
  • 要处理未完工剩余:看 backorder

不要一个按钮里全做。

这三条链都会导致:

  • 新建 MO
  • 复制 move
  • 重挂 move_dest_ids / group links
  • 取消旧单

如果你的字段挂在:

  • mrp.production
  • stock.move
  • stock.move.line
  • mrp.workorder

就必须逐条验证继承策略。

3. 三步制造尤其要测 source/dest location

官方测试专门覆盖了 merge 后默认 source location 是否正确。

这说明在多步制造下,错误的 location 默认值会直接让后续 picking 串错链。

4. 不要只测单步制造

很多自定义在一库位单步制造里看起来正常,一到:

  • pbm
  • pbm_sam

就暴露出 origin、reservation、move link 全部不一致的问题。


最后总结

Split、Merge、Backorder 三者的真正区别,不在按钮,而在时间点和语义:

  • Split:生产前,把大单拆成可执行批次
  • Merge:计划层,把碎单合成新的中心单
  • Backorder:完工时,把已完成和未完成正式切开

如果你把这三种场景区分开,再去看 Odoo 源码,会发现官方设计一直很一致:

不是在“改一张单”,而是在维护一条制造链在不同阶段的正确形态。

理解了这一点,制造单拆并补的很多混乱用法,基本都能收住。

DISCUSSION

评论区

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