先说结论
在 Odoo 企业版里,条码制造不是“扫码够数就完工”。
只要这张制造单还有待做的质量检查,系统就会主动把条码页里的 Validate / Produce 按钮收起来,逼着你先走 Quality Checks。
这不是前端小把戏,而是一条完整链路:
quality_mrp先在 MO 上生成并计算待处理检查stock_barcode_quality_mrp再把quality_check_todo暴露给条码前端- 条码模型根据这个字段决定是否显示最终验证按钮
- 只有检查通过后,制造单才允许继续
button_mark_done
一句话说透:
Odoo 在条码制造场景里把“完工动作”改造成了“先过质量闸门,再允许完工”。
这篇文章为什么和已有“制造质量检查”不是一回事
站内已经有一篇泛讲 quality_mrp / quality_mrp_workorder 的文章,重点是“质量检查如何嵌入制造步骤”。
但这篇要解决的是一个更细、也更容易让现场困惑的问题:
- 为什么在桌面表单里能看到质量按钮
- 到了条码枪 / 条码页却像“完成按钮失踪”
- 扫码数量明明已经够了,系统还是不让过
这个切口主要落在 stock_barcode_quality_mrp,也就是制造条码流里的质量阻塞逻辑,不是泛泛讲质检概念。
第一层:质量检查在 MO 上是怎么长出来的
真正的源头在 quality_mrp/models/stock_move.py。
stock.move._action_confirm() 在标准确认逻辑之后,会继续调用:
_create_quality_checks_for_mo()
这一步会按生产单把 move 分组,然后去搜对应的 quality.point,再生成 quality.check。
源码里至少覆盖了三类制造质检入口:
measure_on = 'product'measure_on = 'move_line'measure_on = 'operation'
也就是说,条码页里后来看到的“还没做完的质检”,不是前端临时推出来的,而是在 MO 确认和 move 建立阶段就已经准备好的。
这里有两个细节很关键:
1. 成品级质量点会挂到 production_id
生成检查值时,_create_quality_checks_for_mo() 会给检查补上:
production_id = production.id
这让后面的所有逻辑都知道:这张检查单属于哪张制造单。
2. 制造场景下不是所有质量类型都能乱配
quality_mrp/models/quality.py 里有约束:
- 如果
measure_on == 'move_line' - 且
picking_type是制造工序类型 - 系统会直接报错
也就是源码明确阻止某些“数量型质检 + 制造工序类型”的错误组合。
所以现场如果觉得“为什么我配完质量点却不按预期工作”,别先怪条码页,先回头看质量点定义是否真的符合制造场景约束。
第二层:quality_check_todo 才是条码页真正盯着的信号
在 quality_mrp/models/mrp_production.py 里,mrp.production 被加了几个关键字段:
check_idsquality_check_todoquality_check_fail
其中最重要的是:
quality_check_todo = fields.Boolean(compute='_compute_check')
_compute_check() 的逻辑不复杂,但非常关键:
- 只要有任意
check_ids.quality_state == 'none',todo = True - 只要有任意
check_ids.quality_state == 'fail',fail = True
最后结果写回:
production.quality_check_todo = todoproduction.quality_check_fail = fail
这意味着对条码前端来说,它根本不需要理解每一条检查单细节。
它只要盯住一个布尔量:
- 这张 MO 现在还有没有没做完的质量检查
有,就卡住。 没有,才放行。
这就是一个很典型的 Odoo 设计:
- 后端把复杂语义折叠成简单状态
- 前端只围绕状态做行为切换
第三层:条码页为什么能“知道”这张 MO 有待检项目
这一步在 stock_barcode_quality_mrp/models/mrp_production.py,代码非常短,但价值极大。
它重写了:
mrp.production._get_fields_stock_barcode()
然后往条码初始状态里追加:
quality_check_todo
别小看这行代码。没有它,条码前端根本拿不到这个字段。
也就是说,quality_mrp 负责定义质量状态,stock_barcode_quality_mrp 负责把这个状态送进条码客户端的首屏载荷。
很多人看企业版源码时会低估这种“小 patch”,但它往往正是业务行为切换的开关。
第四层:Validate 按钮为什么会消失
真正的“闸门”在 stock_barcode_quality_mrp/static/src/models/barcode_mrp_model.js。
这个模块 patch 了 BarcodeMRPModel,做了两件事:
1. 指定打开质量检查的方法
openQualityChecksMethod: "check_quality"
也就是条码端点 Quality Checks 时,不是自己拼界面,而是回调后端的:
mrp.production.check_quality()
而这个方法在 quality_mrp/models/mrp_production.py 里会筛出:
quality_state == 'none'的检查单
然后打开质量检查向导。
2. 更关键:直接改写 displayValidateButton
get displayValidateButton() {
return !(this.record && this.record.quality_check_todo) && super.displayValidateButton;
}
这段逻辑可以直接翻译成人话:
- 当前记录存在
- 且
quality_check_todo为真 - 那就不要显示 Validate 按钮
而基础条码模板 stock_barcode/static/src/components/main.xml 本来就是:
t-if="env.model.displayValidateButton"
所以不是按钮“灰掉”,不是按钮“点了报错”,而是按钮根本不渲染。
这就是为什么一线用户最常见的抱怨是:
- “我条码页上的完成按钮怎么没了?”
因为系统做的不是温和提醒,而是明确的 UI 闸门。
第五层:就算前端绕过去,后端也还会再拦一次
如果你以为这只是前端隐藏按钮,那就把 Odoo 想得太松了。
quality_mrp/models/mrp_production.py 里还有:
pre_button_mark_done()_check_qc_status()
_check_qc_status() 会检查:
check_ids里是否还存在quality_state == 'none'
如果还有未完成检查,pre_button_mark_done() 就会:
- 向导场景下弹 warning 通知
- 普通场景下直接抛
UserError("You still need to do the quality checks!")
所以这套机制是双保险:
- 前端先不让你点完成
- 后端再保证你就算点到了也过不去
这才是企业版“流程控制”该有的样子。
官方测试把这条链路写得很直白
stock_barcode_quality_mrp/tests/test_barcode_quality_mrp_flows.py 和对应 tour 很值得看。
测试流程基本是:
- 建一个有 BOM 的成品
- 建一个
measure_on = 'product'的质量点 - 打开
/odoo/barcode - 进入 Manufacturing
- 新建制造单
- 扫成品条码
love724 - 点击增加数量,让
qty_producing达到可完工状态 - 点击
Quality Checks - 在弹窗里点
do_pass - 回到条码页点击
.o_validate_page - 断言出现成功通知
- 最后确认
quality.check.quality_state == 'pass',且production_id.state == 'done'
这个测试说明了三个事实:
1. 条码制造里的质量检查不是旁路功能
它被写进官方 tour,说明这是标准完成路径的一部分。
2. 先做质量,再做最终验证,是有严格顺序的
测试不是先 validate 再补检查,而是相反。
3. 质量通过后,MO 才真正进入 done
也就是说,质检不是装饰数据,而是完工状态机的一段前置条件。
这套设计对车间现场意味着什么
如果你在现场推条码制造,这条链路会直接影响培训和 SOP。
1. 扫码员不能只学“扫料、扫成品、点完成”
他们还必须理解:
- 什么时候系统会要求先做质量检查
- 为什么按钮会暂时消失
- 质检通过后按钮为什么又回来了
2. 质量员和操作员的职责边界会更清晰
因为系统已经把“未检不可完工”固化成界面行为,不再只是口头纪律。
3. 自定义条码前端时,最容易把这条闸门误删
很多团队改条码页时只盯着按钮布局,忘了 displayValidateButton 这种 getter 背后藏着业务控制。
一旦你重写前端却没把 quality_check_todo 带上,或没保留这个条件判断,就会出现:
- 现场可以提前完工
- 但后端 later 才报错
- 用户体验变得很割裂
更糟的是,如果你连后端保护也改坏了,就真的把质检闸门拆掉了。
最常见的 5 个排错点
1. 条码页没有 Quality Checks,也没有 Validate
先看条码载荷里有没有 quality_check_todo,以及 openQualityChecksMethod 是否还指向 check_quality。
2. 明明配置了质量点,却从没触发
回头查 stock.move._create_quality_checks_for_mo() 是否真的搜到了对应 quality.point,尤其是:
measure_onpicking_type_ids- 产品 / 产品类别范围
3. 质量检查做完了,按钮还是不回来
先确认检查状态是否从 none 变成 pass / fail,再看条码页关闭向导后是否触发 refresh。
4. 用户能从别的入口把 MO 标记完成
重点查有没有自定义覆盖 pre_button_mark_done() 或 _check_qc_status()。
5. 失败后流程不符合预期
这篇主讲的是“条码端完工闸门”,但如果你希望失败后改库位、打告警、走隔离流程,还要继续往 quality_mrp 的 failure_location_id、quality.alert 和相关向导逻辑查。
我对这段源码的判断
我挺喜欢这段设计,因为它很克制。
stock_barcode_quality_mrp 自己几乎没发明新业务对象,只做了三件小事:
- 把
quality_check_todo暴露到条码前端 - 告诉条码端该调用哪个质量入口
- 用一个 getter 决定是否渲染 Validate 按钮
但就是这几个小 patch,把“质量先于完工”的企业规则稳稳塞进了现场执行流。
这比做一堆醒目的红字提醒更有效,因为用户根本没有跳过流程的主路径。
一句话总结
在 Odoo 企业版制造条码流里,
quality_check_todo不是提示字段,而是完工闸门;它让系统在 UI 和后端两层同时保证“先质检,后完工”。
DISCUSSION
评论区