先说结论
在 Odoo 里,工单“有了开始和结束时间”并不等于“排产没问题”。
系统还会继续问三件事:
- 它是不是排在前序工单之前了
- 它是不是和同一工位上的其他工单时间重叠了
- 当前排程是否需要通过 Replan 重新落到资源日历上
所以很多用户看到甘特图里工单已经有时间,却仍然出现黄色/红色提示,本质不是界面抽风,而是 Odoo 在区分:
- 有时间
- 时间合理
- 时间可执行
一句话说透:
Odoo 的工单排程不只是在填日期,而是在验证“前后顺序”和“同资源不重叠”这两个制造约束。
_compute_json_popover() 为什么信息量这么大
在 addons/mrp/models/mrp_workorder.py 里,工单看板和甘特上的提示信息主要来自 _compute_json_popover()。
它不是简单做个 tooltip,而是在汇总几个不同来源的风险:
- 前序工单的
date_start/date_finished - 自己当前是
blocked还是ready - 当前工单是否已经晚于现在
_get_conflicted_workorder_ids()查出来的同工位重叠冲突
这意味着 Odoo 的 popover 不是“显示附加信息”,而是在做一层排程质量诊断。
前序工单为什么会出现“Scheduled before the previous work order”
很多人会误以为:
- 只要工单依赖关系已经建好
- 系统自然会保证时间先后绝对正确
但源码并没有这么天真。
在 _compute_json_popover() 里,Odoo 会比较:
- 当前工单
date_start - 前序工单最早开始、最晚结束时间
如果发现当前工单被排到了前序工单之前,就直接给出危险提示。
这说明依赖关系本身只是结构约束,时间表仍然可能被人工拖拽、二开写入或重排后变得不合理。
所以 Odoo 还要再做一次时间上的现实校验。
同工位冲突为什么不是 Python 循环查,而是直接上 SQL OVERLAPS
_get_conflicted_workorder_ids() 很值得看。
它直接执行 SQL,对满足以下条件的工单做重叠判断:
- 都在
blocked/ready - 不是同一条工单
- 属于同一个
workcenter_id - 两个时间区间
OVERLAPS
这有两个很重要的含义:
1)冲突定义非常明确
Odoo 关心的不是“看起来忙不忙”,而是同一工位上两段时间区间是否真实重叠。
2)这个检查是资源层约束,不是工艺层约束
即使你的前后工序逻辑没问题,只要同工位时间撞上,系统仍然认为排程有冲突。
这就是为什么有些现场会说:
- 工艺顺序没错
- 但甘特图还是报警
因为报的不是工艺错误,而是资源占用错误。
Replan 到底重排了什么
action_replan() 的实现很直接:
- 它不是只重排当前工单
- 而是对关联制造单调用
production._plan_workorders(replan=True)
这点非常关键。
意味着 Replan 不是一个局部“把我这条工单挪一挪”的按钮,而是:
以制造单为单位,重新把 ready / blocked 的工单放回整体排程逻辑里。
所以现场点一次 Replan,看到多道工序时间一起变化,不是副作用,而是设计本意。
为什么 related date 字段还要专门写 compute / set
工单的 date_start / date_finished 实际上关联到 leave_id 上的资源日历占用。
源码里还特别解释:
- 如果甘特拖拽同时改开始/结束
- ORM 对 related 字段可能分多次 write
- 会导致日期约束误触发
所以 Odoo 才写了 _compute_dates() / _set_dates() 这一套,确保时间更新能成对落地。
这提醒我们:
工单时间不是普通表单字段,它本质上是资源日历上的占位记录。
也正因为如此,排程问题往往要同时看:
- 工单本身
leave_id- 工位日历
- 是否重排成功
新手最容易误解的点
1)有开始和结束时间,不代表没有冲突
时间存在,只说明“排过”;不说明“排对了”。
2)依赖关系存在,不代表时间先后一定合理
人工拖拽、批量修改、定制写入都可能把时间搞反。
3)Replan 不是只动当前一条工单
它会把同制造单下相关 ready / blocked 工单一起纳入重排。
4)工单时间不是纯展示字段
它背后对应资源日历 leave,占的是工位资源能力。
实施和开发注意点
一,培训时别把甘特图讲成“排上时间就好了”
必须让计划员理解两层约束:
- 工艺先后约束
- 同工位资源占用约束
只理解第一层,看到红色警告就会一头雾水。
二,二开别直接粗暴写 date_start / date_finished
如果不了解 leave_id 和重排逻辑,只改表面字段,很容易制造出:
- 时间看起来改了
- 资源占位没同步
- popover 一直报警
三,排错建议顺序
- 看当前工单是否
blocked/ready - 看
blocked_by_workorder_ids的开始和结束时间 - 跑
_get_conflicted_workorder_ids()这一类冲突判断思路,确认是否同工位重叠 - 再判断是否需要通过 Replan 让整张 MO 重新排程
最后总结
Odoo 的工单排程不是“写入两个日期字段”这么简单。
它至少同时在校验三层东西:
- 前序工单是否先完成
- 同工位资源是否时间重叠
- 当前时间是否真的已落到资源日历上
所以工单有时间却仍然报冲突,并不奇怪。
一句话记住:
在 Odoo 里,排产成功不是“有时间”,而是“顺序对、资源不撞、日历落得住”。
DISCUSSION
评论区