很多团队会把“制造标签打印”理解成一个功能点:完工后打一张标签,结束。
但 Odoo 源码里,这件事其实被拆成了至少三类不同场景:
- 制造单完成后自动打印;
- 生成 lot / serial 时立刻打印;
- 用户临时决定打印什么标签、用什么版式。
如果不把这三条链路分清,你就会经常遇到一种错觉:明明都是打印标签,为什么这次自动打了,那次却弹窗口让你选,换成 ZPL 又是一套行为?
真正的核心方法:_get_autoprint_done_report_actions()
在 addons/mrp/models/mrp_production.py 里,制造单完工后的自动打印主入口是 _get_autoprint_done_report_actions()。
它不是“返回一个打印动作”那么简单,而是返回一个 动作列表。这已经说明设计思路:完工后可能要连续打印多种不同文档。
源码里按条件拆了几组动作:
- 生产单据本身(production order)
- 成品标签(finished product labels)
- 接收报告(reception report)
- 接收标签(reception labels)
- lot 标签(done MRP lot labels)
也就是说,Odoo 的制造打印不是一个 report toggle,而是一个按 picking type 配置驱动的打印队列。
为什么有时打印成品标签,有时打印 lot 标签
这是很多实施现场最容易混淆的点。
成品标签:面向“成品 move / 产品本身”
当 picking_type_id.auto_print_done_mrp_product_labels 打开时,系统会按 mrp_product_label_to_print 决定输出格式:
pdf用action_report_finished_productzpl用label_manufacture_template
这类标签更像“这批做出来的产品要贴什么商品标签”。
lot 标签:面向“已经落到 move line / lot 的追溯对象”
当用户拥有 stock.group_production_lot,且 auto_print_done_mrp_lot 开启时,系统会从 move_finished_ids.move_line_ids.lot_id 中收集 lot,再决定打印 PDF 还是 ZPL。
这条链路强调的是追溯单元,而不是商品展示单元。
换句话说:
- product label 更偏“我要给成品贴业务标签”;
- lot label 更偏“我要给这次产出的追溯对象贴身份标签”。
两者看起来都叫标签,但对象层级不同,所以配置和打印动作也分开。
为什么“生成批次号”后的打印又是另一套方法
_autoprint_generated_lot() 和 _autoprint_mass_generated_lots() 处理的不是“完工之后”的场景,而是“批次 / 序列号刚被生成”的场景。
这里的时间点往前挪了:
- 不是等 MO done;
- 而是在 lot_producing_ids 生成之后,就可以立即打印。
这非常符合现场操作:有些工厂是先打追溯标签,再贴到工件、周转箱或工单卡上,后续才继续报工。
源码里同样支持 PDF / ZPL 两种格式,但它依赖的是另一组配置:generated_mrp_lot_label_to_print。这说明 Odoo 认为“生成时打印”和“完工时打印”是两个独立决策,而不是一个总开关。
action_open_label_layout() 与 action_open_label_type() 在解决什么问题
自动打印解决的是默认流程,但制造现场经常还有临时打印需求。这时,Odoo 提供两个手动入口。
action_open_label_layout():先选版式,再打产品标签
这个 action 会打开 product.label.layout,并把:
default_product_idsdefault_move_idsdefault_move_quantity = 'move'
塞进上下文。
意思是:你不是抽象地为产品打标签,而是基于这批成品 move 来打印,数量也跟 move 走。
action_open_label_type():先决定打印 lot 还是产品
如果当前用户有 lot 权限、并且成品 move line 上已经有 lot_id,系统会优先弹 picking.label.type,让用户先选“打哪种标签”;否则就直接回退到 action_open_label_layout()。
这个设计很妙:
- 没 lot 的时候,不必多问,直接走产品标签;
- 有 lot 的时候,先让用户决定打印对象层级。
它不是多此一举,而是在避免“系统替你假设你到底想打印产品标签还是 lot 标签”。
为什么源码里到处在区分 PDF 和 ZPL
因为 Odoo 这里不是把“格式”当成视图皮肤,而是把它当作不同输出通道:
- PDF 更适合办公室、通用打印机和人工补打;
- ZPL 更适合标签机、现场自动贴标和批量流水线。
因此源码不是一个 report 模板里切 CSS,而是直接映射到不同 report action。实施时如果只测 PDF 不测 ZPL,通常上线后现场就会踩坑。
实施和定制时最容易忽略的三个点
1. 不要把所有打印都绑在 MO done 上
如果你的现场需要“先有码、后报工”,应优先理解 generated lot 的打印链路,而不是硬改完工动作。
2. picking type 配置是打印策略的真正开关
很多人只盯制造单按钮,忽略了真正影响行为的是 picking type 上的 auto print 配置以及对应格式字段。没配对,再多定制前端按钮也没用。
3. 标签对象层级要先说清楚
项目讨论里如果只说“打印标签”,大概率会埋雷。你至少要问清:
- 打产品标签还是 lot 标签?
- 完工时自动打还是生成批次时就打?
- PDF 还是 ZPL?
- 数量跟产品、move,还是跟 lot 走?
这些问题在源码里已经被拆开,实施方案也应该跟着拆开。
一句话总结
Odoo 制造标签之所以看起来“有时自动打、有时又让你选”,不是因为逻辑混乱,而是因为它把 打印时机、打印对象、输出格式 三件事认真拆开了。理解这三层后,制造标签就不再是一个模糊功能,而是一条可配置、可扩展的现场交付链路。
DISCUSSION
评论区