先说结论
在 Odoo 里,工位的 Blocked、停机时间、productive time、OEE,并不是三套彼此独立的制造功能。
它们大多来自同一个底层对象:
mrp.workcenter.productivity
也就是工位时间日志。
一句话记:
Odoo 不是先有 OEE 报表、再有停机分析;而是先记录工位时间,再把这些时间重新解释成 blocked、productive 和 OEE。
工位为什么会变成 Blocked
关键逻辑在 addons/mrp/models/mrp_workcenter.py 的 _compute_working_state()。
源码的判断很直接:
- 先找这个工位有没有“尚未结束”的 productivity line
- 如果没有,工位状态是
normal - 如果有,而且
loss_type属于productive或performance,工位被视为正在使用 - 否则,工位被视为
blocked
这背后其实很有意思。
Odoo 不会因为你点了某个“停机按钮”就神秘变红,而是因为:
- 系统发现当前存在一条未关闭的时间日志
- 这条日志被归类为非 productive / performance
- 所以它把这段时间解释为“工位被阻塞”
也就是说,Blocked 不是一个孤立状态,它是时间日志分类后的结果。
productivity loss 到底在表达什么
在 mrp.workcenter.productivity.loss.type 与相关模型里,Odoo把工位时间分成几种 loss type,例如:
productiveperformanceavailabilityquality
对很多团队来说,这几个名字容易看成“报表标签”。
但在实现层面,它们其实是在决定:
- 当前工位显示为正常使用,还是 blocked
- 这段时间会计入 productive_time 还是 blocked_time
- OEE 分母和分子怎么累计
所以 loss type 不是事后分析标签,而是运行时状态解释器。
blocked_time 和 productive_time 是怎么来的
在同一个 mrp_workcenter.py 文件里,Odoo 分别计算:
_compute_blocked_time()_compute_productive_time()
两者都是去读最近一个月内已结束的 productivity lines,再按 loss_type 汇总时长。
差别在于:
blocked_time
会把 非 productive 的已结束时间都累计进去。
所以 blocked_time 在业务上更接近:
- 停机
- 卡住
- 等待
- 质量/可用性等非产出时间
productive_time
只统计 loss_type = 'productive' 的时间。
也就是说,Odoo 这里对“真正产出时间”是有一个非常收敛的定义的,不是所有工单占用时间都算 productive。
OEE 为什么不是单独填报出来的
_compute_oee() 的逻辑其实很朴素:
- 读近一个月的工位时间日志
- 按
loss_type分组求和 - productive 的时长当分子
- productive + blocked 的总时长当分母
- 最后算百分比
换成大白话就是:
Odoo 的 OEE 不是另一套报表来源,而是拿同一批工位时间日志重新算出来的结果。
所以如果底层时间日志没记好,OEE 一定不可信;它不是一个能“自动修正原始数据质量”的魔法指标。
为什么很多团队觉得 OEE “不准”
原因通常不在公式,而在记录口径。
最常见的问题有:
1)没有认真维护 productivity loss 原因
如果大家随手记,或者全部都记成同一类,最终 blocked / productive 的边界就会失真。
2)时间日志没有及时关闭
源码里工位当前状态就是看有没有未结束的 line。如果日志长期不关,工位状态和统计都会漂移。
3)把 performance 当 productive 理解
Odoo 在 working_state 判断里把 performance 也当“工位正在被使用”,但在 OEE 聚合时,productive_time 只认 productive。这正是很多人直觉和系统口径不一致的地方。
也就是说:
- 从运行状态看,performance 不是 blocked
- 但从 OEE 的 productive_time 口径看,它又不等于真正产出时间
这就是为什么只看字段名很容易误判。
这套设计到底想解决什么问题
Odoo 并不是想把工厂复杂现实全部装进一个指标。
它在做的是一件更务实的事:
- 先把工位的每段时间记录下来
- 再按照 loss type 解释“这段时间意味着什么”
- 最后得出当前状态、停机统计、产出统计和 OEE
这样设计的好处是统一。
不然你会得到三套互相打架的数据:
- 看板上一个状态
- 报表里另一个停机时间
- OEE 再来第三套口径
而 Odoo 尽量让它们共用同一底座。
新手最容易误解的几个点
1)Blocked 不是工位字段手工写死的
它是未结束时间日志 + loss type 共同推导出来的。
2)productive_time 不是“所有工单占用时间”
它是被明确标记成 productive 的那部分时间。
3)OEE 不是独立录入口径
它依赖底层 productivity lines 的质量。
实战里怎么落地
如果你们想让工位状态和 OEE 真有运营价值,建议优先做三件事:
- 把 productivity loss 分类定义清楚,不要让车间随意理解
- 让开始、暂停、恢复、结束动作尽量形成稳定流程,避免时间日志悬空
- 出现 OEE 异常时,先查原始 productivity line,再查报表,不要反过来
这样你会发现很多“指标不准”问题,其实是记录治理问题,不是系统公式问题。
最后总结
Odoo 对工位状态和 OEE 的设计并不花哨,但很扎实:
- 当前工位是否 blocked,看未结束时间日志怎么分类
- 最近一个月 blocked_time / productive_time,看已结束时间日志怎么聚合
- OEE,则是基于同一批日志继续计算出来的比率
所以真正该管理的,不是报表页面上的几个数字,而是:
工位时间到底被怎样记录、怎样分类、怎样结束。
把这个底层逻辑抓住,Blocked、停机分析和 OEE 才会真正连成一套可用的制造语言。
DISCUSSION
评论区