先说结论
在 Odoo 的标准制造扩展里,过期批次并不总是“发现即禁止”。
更准确地说,系统会在你准备完工时检查原料批次,如果发现有 lot 带着 product_expiry_alert,就弹出一个确认向导,让人明确知道:你正在用过期物料完成这张制造单。
所以它的设计重点不是替你做全部质量判断,而是把责任从“系统默默放行”变成“用户显式确认”。
源码里的关键链路
mrp_product_expiry 扩展了 mrp.production.pre_button_mark_done()。
在真正执行 super 之前,Odoo 会先走 _check_expired_lots():
- 扫描
move_raw_ids.move_line_ids - 找出
lot_id.product_expiry_alert为真的批次 - 如果有,就返回一个确认向导动作
- 用户确认后,再通过上下文
skip_expired继续放行
这说明它不是在完工后补警告,而是在完工入口设置一道检查闸门。
为什么不是直接 hard block
因为制造现场经常存在灰度场景:
- 物料刚过预警线,但经 QA 判定仍可用
- 某些批次是“建议不用”,不是法律意义的绝对禁用
- 现场需要主管背书后临时放行
如果系统一刀切全阻塞,会让业务只能靠改主数据或绕系统来解决。Odoo 这里选择的是更现实的折中:
先把风险显式抬出来,再让组织自己承担放行责任。
这件事真正管住了什么
1. 防默默完工
最糟糕的不是用了临期 / 过期料,而是用了以后没人知道。这个向导至少把“风险使用”变成显式动作。
2. 防执行层与质量层脱节
生产人员点完工时,系统会提醒:这不是普通数量操作,而是带质量风险的收尾动作。
3. 给审计留下解释点
从治理角度看,“有人确认后继续”通常比“系统完全没提示就过了”更可接受。
最常见的误区
误区 1:看到弹窗就说明系统已经禁止
不对。很多时候它只是要求确认,而不是绝对拦截。
误区 2:没弹窗就说明批次没问题
也不一定。前提是相关 lot 上真的维护了过期预警状态,且 move line 正确绑定 lot。
误区 3:这是库存模块的逻辑
触发点在制造完工入口,不是普通库存移动共用的统一拦截。
误区 4:确认后就万事大吉
确认只表示业务上允许继续,不代表质量责任被系统接管了。
排错顺序
如果你以为该拦没拦、或不该拦却被拦,按这个顺序查:
- 原料 move line 是否真的带了
lot_id - lot 上是否有
product_expiry_alert - 当前动作是不是从 MO 完工入口触发
- 是否已有
skip_expired上下文导致重复不再弹窗 - 最后再看自定义模块是否把标准确认流改成硬阻塞或完全跳过
一句话记忆法
Odoo 在制造过期批次问题上的标准姿势,不是替你拍板,而是逼你在完工前公开拍板。
DISCUSSION
评论区