先说结论
Odoo 里的工位产能,不是单独一个“每小时做几件”的数字。
系统在算工单预计时长和排程时,至少会同时考虑:
- 工位或产品级的
capacity - 开机/收尾时间
time_start、time_stop - 工位效率
time_efficiency - 工艺本身的
time_cycle - 是否存在
alternative_workcenter_ids
所以如果你觉得“我明明填了产能,为什么排程还是怪”,往往不是 Odoo 没用这个字段,而是你把整套模型只理解成了一个数字。
一句话记:
Odoo 排工单看的是“每批要跑几轮、每轮多长、能不能换工位”,不是只看一条产能参数。
capacity 在源码里到底是什么意思
关键逻辑在 addons/mrp/models/mrp_workcenter.py 的 _get_capacity()。
这段实现不是简单返回一个工位默认值,而是先在 capacity_ids 里找最合适的记录:
- 优先匹配当前产品 + 当前产品主 UoM
- 其次匹配通用容量 + 当前单位
- 再其次匹配通用容量 + 产品主单位
如果找到了产品级容量,就优先用它;找不到,才退回工位默认的 setup/cleanup 和默认 capacity。
这说明 Odoo 的心智模型是:
- 同一个工位,对不同产品可以有不同并行产能
- 产能还可以和单位绑定
- 工位默认值只是兜底,不是唯一真相
这和很多工厂现场非常接近:同一条线做 A 产品和 B 产品,节拍本来就可能完全不同。
setup / cleanup 为什么会经常被忽略
很多人只盯 time_cycle,但 Odoo 在 _get_capacity() 和工单时长计算里,都会把:
time_starttime_stop
一起带入。
这两个字段的业务含义很直白:
- setup:开工准备、换模、预热
- cleanup:收尾、清场、换线整理
于是总时长不是单纯:
数量 × 单件时间
而更接近:
准备时间 + 收尾时间 + 批次数 × 每轮工作时间
这也是为什么小批量生产特别容易“感觉系统算多了”。
不是系统算多,而是你脑子里只算了节拍,没有把每次开工的固定成本算进去。
Odoo 怎么把产能变成工单时长
核心逻辑在 addons/mrp/models/mrp_workorder.py 的 _get_duration_expected()。
这段代码会先拿到:
- 当前工位 capacity
- setup / cleanup
time_efficiency- operation 的
time_cycle
然后做一个关键动作:
先算要跑多少轮 cycle
如果这次要做的数量是 qty_producing,工位 capacity 是每轮可并行做 5 件,那么系统不会算 20 件就跑 20 次,而是:
cycle_number = ceil(qty / capacity)
也就是:
capacity 在 Odoo 里更像“每轮能并行吞多少数量”,不是“每小时产量”。
再把效率折进去
最终时长里还会乘除 time_efficiency。
这意味着:
- 工位效率低,预计时长会拉长
- 同一个 operation 放到不同工位,预计时长会变
所以 capacity 解决的是“要跑几轮”,而 efficiency 解决的是“每轮实际要多久”。
这两个不是一回事。
替代工位到底怎么介入排程
另一个经常被低估的字段是 alternative_workcenter_ids。
在 mrp.workorder 里,Odoo 规划工单时不会只看原工位,而是把:
- 主工位
- 所有关联的替代工位
一起放进候选集合。
然后系统会逐个尝试:
- 用该工位重新估算预计时长
- 调用
_get_first_available_slot()找最早可用时间窗 - 比较哪个工位能更早做完
- 最后把工单排到“最优完成时间”的工位上
这说明替代工位不是前台备注,也不是手工切换列表。
它是排程算法的候选池。
所以你配置了替代工位却没感觉,常见原因并不是字段无效,而是:
- 替代工位没有 calendar
- 替代工位反而更慢
- 替代工位没有空档
- 替代工位 capacity / efficiency 设置不合理
为什么有时候换到替代工位后时长还会变
因为 Odoo 不是简单把同一工单“搬家”。
源码里如果改用替代工位,会重新计算:
- 该工位自己的 capacity
- 该工位自己的 setup / cleanup
- 该工位自己的
time_efficiency
所以替代工位不是只影响开始时间,也会影响总工时。
这很符合现场:
- A 工位更快但更忙
- B 工位空一点,但换线更慢
- C 工位效率一般,但现在有窗口
Odoo 想解决的是“把工单放到哪台机器上,整体更早完成”,不是只看哪台机器先空出来。
新手最容易误解的三个点
1)capacity 不是每小时产量报表字段
它更接近“单个 cycle 能并行生产多少”。
如果你把它当 KPI 报表口径去填,排程当然会怪。
2)替代工位不是人工调度备注
它会真的被排程逻辑拿来试算,并可能自动成为最终工位。
3)setup / cleanup 不填,排程一定偏乐观
很多团队把固定准备时间省掉,结果系统排出来的计划永远过密。不是算法错,是输入数据天生偏乐观。
实战配置建议
如果你们希望排程更贴近现场,建议这样建模:
对高频产品补产品级 capacity
不要只给工位一个总容量。关键产品应该补 capacity_ids,让 Odoo 知道不同产品在同工位上的节拍差异。
把 setup / cleanup 当真实时间维护
尤其是换模、预热、清洗明显的行业,这两个字段比很多人想象中更重要。
替代工位不要乱挂
只有“工艺上真能替代”的工位才应该挂进 alternative_workcenter_ids。否则系统会认真把不该替代的机器也纳入候选。
排错时该看什么
如果你发现工单排程不合理,建议按这个顺序看:
mrp.workcenter.capacity是否有产品级覆盖- setup / cleanup 是否维护成了 0
time_efficiency是否夸张- 替代工位是否没有 calendar 或者时间窗更差
- 该工单的
duration_expected是在主工位还是替代工位条件下算出来的
别只看甘特图结果,要回到 _get_capacity() 和 _get_duration_expected() 的输入参数去看。
最后总结
Odoo 的工位排程逻辑其实很朴素:
- capacity 决定一批要跑几轮
- setup / cleanup 决定固定成本
- efficiency 决定真实速度
- alternative workcenter 决定有没有更好的落点
所以“工位产能”在 Odoo 里从来不是单字段问题,而是一组会共同影响预计工时与排程结果的参数。
真正重要的不是把某个数字填上去,而是理解:
系统排的是一段可执行的制造时间窗,而不是一条好看的产能档案。
DISCUSSION
评论区