先说结论
Odoo 里的 Unbuild Order 不是“把生产单反着执行一遍”这么粗暴。
它真正要表达的是:
把一个已经存在的成品数量拆回组件,并尽量沿着原制造或 BOM 语义恢复合理的库存流向。
所以它是拆解与回流,不只是撤销。
为什么它不是撤销按钮
如果只是撤销,系统根本不需要单独的 mrp.unbuild 模型。
但源码里它有:
- 独立的 unbuild 单
- 和
mo_id的关联 - 产出 move 与消耗 move
- lot / serial 处理
- 数量不足警告
这说明它不是删除历史,而是在创建一条新的反向库存业务事实。
mo_id 为什么很重要
当 unbuild 关联到已完成的 MO 时,源码会优先依据:
- 已完成成品 move
- 已完成原料 move
- 实际产出数量
来推算拆解比例。
这背后的逻辑是:
如果你知道这批成品原来怎么生产出来,就尽量按那次真实生产结果回推。
这比单纯按 BOM 静态比例更接近现实。
如果没有 MO,会发生什么
源码里也支持根据 BOM 直接做 unbuild。
这时系统会:
- 按 BOM
explode()结果去找组件 - 按当前拆解数量去换算组件回流数量
- 还会考虑 byproduct 语义
也就是说,没有 MO 时它退回到“按结构规则拆解”,有 MO 时则更偏“按真实来源回推”。
为什么 lot / serial 特别麻烦
在 mrp_unbuild.py 里,官方专门处理:
- 已经拆过的 lot
- serial 的可追溯回流
- 同一 MO 多次 unbuild 的边界
这说明拆解最难的点之一不是数量,而是:
你拆回来的到底是不是那一批真实来源组件。
所以 traceability 在这里比“数量算术”更关键。
为什么会有库存不足警告
源码里如果可拆数量不够,会打开 stock.warn.insufficient.qty.unbuild。
这说明 Odoo 不把 unbuild 当成纯理论动作,而是要求:
- 你手上真的有可拆的成品数量
- 才能去做这次拆解回流
所以它本质上还是库存业务,而不是配方推演。
一句话记忆法
Unbuild 不是撤销生产,而是把成品按 MO 或 BOM 语义拆回组件,并尽量保住真实追溯关系。
DISCUSSION
评论区