先说结论
在 Odoo 里,“生成制造单”只是结果,不是机制本身。
真正重要的问题是:
- 需求是谁发起的
- BOM 是谁决定的
- 计划日期怎么算的
- 这张 MO 属于预测补货,还是属于订单拉动
一句话记:
MTO 更像“有单就拉”,Orderpoint 更像“缺量就补”,而 Manufacture 路线负责把这两类需求翻译成 MO。
如果只盯着“系统最后建没建制造单”,就很容易把 Odoo 的制造补货逻辑看扁。
为什么这个问题在实施里总出错
现场常见困惑是:
- 为什么两个产品都走 Manufacture,却一张 MO 跟销售单绑得很紧,另一张像系统自己补出来的
- 为什么补货规则里选了 BOM,生成单据用的就是那个;而有些场景又像系统自己找默认 BOM
- 为什么有时 scheduler 跑完看到 draft MO,过一会儿才 confirmed
这些问题背后,其实是三套对象在一起工作:
stock.warehouse.orderpointstock.rulemrp.production
再加上 mrp.bom 对 lead time、BOM 选择和补货单位的影响。
Orderpoint 在制造补货里到底多做了什么
addons/mrp/models/stock_orderpoint.py 不是简单给补货规则多挂了一个 BOM 字段。
它实际上补上了制造补货最关键的上下文。
1. orderpoint 可以显式指定 BOM
字段包括:
bom_ideffective_bom_idbom_id_placeholder
意思是:
- 你可以在补货规则里直接指定要用哪张 BOM
- 如果没指定,系统再去找默认匹配 BOM
这解决的是多 BOM 产品最容易出错的问题:
不是“这个产品能制造”就够了,而是“这次补货到底按哪张 BOM 制造”。
2. orderpoint 会把 BOM 带进 procurement values
源码里 _prepare_procurement_values() 明确把:
bom_id
塞进了后续采购/制造链。
这一步很关键。
因为如果这层上下文丢了,后面 stock rule 只能重新猜 BOM,结果就可能和补货规则页面看到的不一致。
3. orderpoint 会影响 days_to_order
MRP 扩展了 _compute_days_to_order(),当补货规则走 manufacture 时,会优先看 BOM 的:
days_to_prepare_mo
也就是说,制造补货不是只看最小/最大库存,还会把“组件提前准备时间”纳入计划。
Manufacture route 怎么把需求翻译成 MO
真正把需求变成制造单的核心,在 addons/mrp/models/stock_rule.py。
_get_matching_bom():先看上下文,再看默认
BOM 选择顺序很有代表性:
- 如果 values 里已经有
bom_id,优先用它 - 否则如果
orderpoint_id上有 BOM,优先用补货规则上的 - 再不行才用系统默认
_bom_find()
这说明官方非常明确:
制造补货的 BOM 选择优先级,先尊重业务上下文,再退回系统默认。
_prepare_mo_vals():把需求语义写进 MO
准备 MO 时,系统会写入:
orderpoint_idbom_iddate_startdate_deadlinereference_idsmove_dest_ids
所以生成出来的 MO,不只是一个产品+数量对象,而是带着来源语义的执行节点。
如果它来自 orderpoint,MO 上就能看见 orderpoint_id;如果它来自订单拉动,下游 move 关系又会体现另一套语义。
_make_mo_get_domain():有些需求会尝试并到已有 MO
这段逻辑很容易被忽视。
当有 orderpoint_id 时,系统会根据:
date_deadlinedate_start- BOM
- 产品
- picking type
去找是否存在可复用的 draft / confirmed MO。
这也是为什么某些制造补货看起来像“没有新建单,而是并到旧单里”。
不是系统玄学,而是 stock rule 在尝试复用合适的未计划 MO。
MTO 和 Orderpoint 的制造语义到底差在哪
MTO:需求跟着具体下游对象走
MTO 的核心不是“必须制造”,而是:
- 有明确需求对象
- 不走库存缓冲
- 由下游需求直接拉起上游补给
在制造场景里,这通常意味着 MO 更强地带着销售 / 下游 move 语义。
Orderpoint:需求来自库存目标
Orderpoint 的核心则是:
- 为了把库存补到目标区间
- 需求不是某张具体订单一对一拉起
- 更偏预测、库存政策和计划补货
所以两者都可能走 Manufacture route,但心智模型完全不同:
- MTO:谁要,我给谁做
- Orderpoint:库存缺了,我先补到位
为什么 scheduler 不一定立刻给你最终状态
stock_orderpoint._post_process_scheduler() 里有一个很重要的设计:
先让所有 orderpoint 跑完 procurement,再统一把对应 draft MO 确认为 confirmed。
这样做是为了避免:
- 某张刚确认的 MO 又立刻触发新的补货冲突
- 多条补货规则互相抢上下文
所以你看到 scheduler 跑完后某些 MO 的确认时机不是瞬时同步,其实正是官方在避免补货链互相打架。
新手最容易踩的 5 个坑
坑 1:以为开了 Manufacture route 就万事大吉
不够。
没有正常 BOM,或者 BOM 选择上下文不对,Manufacture route 也只会把问题更快暴露出来。
坑 2:忽略 orderpoint 上的 BOM 指定
多 BOM 产品下,不显式指定 BOM,经常会导致“补货规则页面想要 A,系统最后制造成 B”。
坑 3:把 phantom kit 和制造补货混用
源码里明确限制:phantom kit 产品不能再配 reordering rule。
因为 kit 的语义是展开,不是制造入库。
坑 4:只看 produce_delay,不看 days_to_prepare_mo
很多团队只看制造提前期,却忘了组件供应提前期也会被 BOM 带进计划链。
坑 5:自定义时把 orderpoint_id、bom_id 覆盖掉
这类 bug 特别隐蔽。
表面看 MO 建出来了,实际上上下文丢了,后续补货通知、BOM 追溯和复用已有 MO 的逻辑都会失真。
开发时最该注意什么
1. 保住 procurement 上下文
最重要的不是“我也能建 MO”,而是你自定义后:
bom_idorderpoint_idreference_idsmove_dest_ids
有没有被正确保留。
2. 多 BOM 产品一定要测默认路径和显式路径
至少要测两类:
- 没指定 BOM 时系统怎么选
- 指定 BOM 时是否真能贯穿到最终 MO
3. 调 lead time 时别只改一层
制造计划日期不仅受 produce_delay 影响,还受:
- rules delay
- 非单步制造下的 pre-production 规则
days_to_prepare_mo
共同影响。
4. 看补货链时不要只盯 MO
还要同时看:
- orderpoint
- stock rule
- 下游 move / reference
- scheduler 后处理
这样你才能分清这张单到底是“订单拉的”,还是“库存补的”。
最后总结
制造补货的核心,不是谁会创建 MO,而是谁把需求语义、BOM 选择和计划时间带进了 MO。
在 Odoo 里:
- Orderpoint 负责把“库存政策”变成制造上下文
- MTO 负责把“具体需求”变成拉动信号
- Manufacture route 负责把这些信号落成制造单
真正理解这三层,你才不会把所有“自动建 MO”都看成同一种机制。
DISCUSSION
评论区