打印与标签

打印机路由、ZPL 与 PDF 标签,以及把一次收货或一张完工生产订单变成货架标签的打印任务队列。

Carbon 可以在你过账收货、拆分批次或完成生产订单首道工序时自动打印实体标签。打印系统由打印任务队列、把地点和上下文映射到设备的打印机路由,以及热敏标签 ZPL 和文档 PDF 渲染器组成。

这套系统与发票、采购订单、报价单和生产订单流转卡等生成的 PDF 相互独立。那些文档以文件形式交付;打印则把输出发送到物理打印机。其默认输出是热敏 ZPL 标签。

两种渲染器,一个队列

打印任务按它所路由到的打印机选择两种格式之一渲染:

  • ZPL 是热敏标签打印机使用的文本命令语言,是部件标签和存储标签的默认格式。ZPL 标签需要配置了 ZPL 尺寸的介质规格,否则无法渲染。
  • PDF 是真正的文档,由生成发票和流转卡的同一套 React-PDF 引擎渲染。看板卡仅支持 PDF,因此发送到仅 ZPL 打印机的看板卡会失败,除非有模板覆盖。

存在三种单据类型(packages/printing/src/registry.ts:10):

字段类型
用于(序列号或批号)的标签。可从 Receipt、Shipment、Operation、Entity、Job、Split 或 StockTransfer 打印。
用于货架、库位和存储位置的标签。从 StorageUnit 打印。
用于看板库位的补货卡。从 Kanban 打印。

来源单据映射到值得打印的单据类型。过账发货会生成产品标签;扫描存储单元会生成存储标签。getDocumentTypesForSource 完成该查找(registry.ts:49)。

打印机路由

打印机路由对应一台物理打印机(printerRoute 表,20260326000000_print-manager.sql:37)。每条路由包含:

字段类型
你对打印机的称呼,同一地点内唯一,例如「Zebra 2x1」。
"ZPL (Thermal Label)" 或 "PDF (Document)"。CHECK 约束限定这两个值。
打印机装载的标签尺寸。ZPL 打印机只提供热敏(支持 ZPL)尺寸;PDF 打印机提供全部尺寸。
任务投递到的 ProxyBox HTTP 端点,例如 https://pbx-XXXX.pbxz.cloud/api/v1/print/…。
可选的密钥,投递时作为 X-API-Key 头发送。
BinderyPress 模板 ID。「留空使用内置」——留空则使用 Carbon 内置渲染器。
打印机所在地点(可选;路由也可以作用于整个公司)。

在设置 → 打印中管理路由(apps/erp/app/routes/x+/settings+/printing.tsx)。每条路由都有 Test(测试) 操作,直接向 printerUrl 发送一张小型 ZPL 测试标签,以便在接入工作流之前验证打印机可达。测试打印仅适用于配置了介质规格的 ZPL 打印机,成功时返回 "Test label sent to printer"。

NOTE

Carbon 通过 ProxyBox 访问打印机,这是车间网络上的 HTTP 端点。Carbon 从不直接连接打印机的原始端口。投递方式是把渲染后的字节 POST 到 printerUrl。如果路由还配置了 templateId 且部署有 BinderyPress 密钥,渲染会委托给 BinderyPress 而不是内置渲染器。

分配:在什么上下文用哪台打印机

路由是打印机;分配决定何时使用它。分配保存在公司的打印设置(companySettings.printing JSONB)中,每个地点一个块。在地点内,你为每个上下文分配打印机(packages/printing/src/assignments.ts:7):

字段类型
该地点的回退打印机。其他上下文没有自己的打印机时都继承它。
来源单据为 Shipment 时使用。
来源单据为 Receipt 时使用。
用于 StockTransfer 或 StorageUnit。
按工作中心,用于从生产订单工序打印的标签。未分配的工作中心直接继承地点默认值。

每个上下文行还带 Auto-print(自动打印) 开关。开启时,匹配的业务事件无需点击即可打印。getPrinterContextForSource 把来源单据映射到上下文,resolveContextAssignment 沿回退链解析:未分配的上下文使用地点默认打印机。

HEADS UP

对于没有显式条目的上下文,自动打印开关默认为开启,即使地点默认值的自动打印已关闭。如果希望某地点的发货工位保持静默,请显式设置其 shipping 行,而不要依赖 default 行。

打印机路由使用一小时的 Redis 缓存,键为 printing:{companyId}:{locationId}:{context}。保存路由或分配会使公司缓存的路由数据失效。

打印任务队列

每次打印(手动或自动)都会生成一行 printJob(20260326000000_print-manager.sql:80),并经过一组固定状态:

偏轨状态
已完成
已投递,或已渲染但没有打印机可发送(仅下载的任务)。

每个任务记录其 origin,为 auto(业务事件背后的自动打印)、manual(有人点击打印)或 reprint(从任务列表重打)之一。它还保留 sourceDocument、sourceDocumentId、可读 ID、contentType 和发送到的 printerUrl。

在设置 → 打印 → 打印任务(printing.jobs.tsx)中查看队列。表格实时流式更新,显示状态、说明、来源、类型、来源方式和时间。展开行可看到打印机 URL、尝试次数、时间戳和错误。你可以查看内容(ZPL 通过 Labelary 预览,PDF 通过内嵌框架)、重打(生成 origin: "reprint" 的新任务)或删除。

NOTE

打印任务按计划清理:已完成任务 30 天后,失败任务 90 天后。

自动打印行为

在受支持的业务事件之后,Carbon 解析地点的打印机配置和上下文,检查 autoPrint(未设置时默认开启),并排队一个任务。打印失败不会阻塞触发它的收货、发货或生产订单。

已接入的事件:

事件上下文打印内容
过账收货receiving已收跟踪实体的产品标签。
过账发货shipping产品标签,以及批次拆分的 Split 标签。
拆分库存调拨行inventory一张 Split 标签,以及原批次的 Entity 重打标签。
完成生产订单首道工序(MES)工作中心生产订单的产品标签。

要停止某个工位自动打印,请关闭该地点对应上下文行的自动打印。要改为手动触发打印,请使用相关界面上的打印按钮,或把来源单据发送到手动打印路由(/x/print),它会校验请求并排队同类任务。

HEADS UP

同一来源单据的自动打印在 30 秒内去重,Carbon 会丢弃第二个事件。手动打印和重打不去重。

打印与下载

当地点有打印机路由时,打印会打开打印机选择器(默认选中解析出的打印机),确认后排队任务。没有路由时,它会打开下载弹窗,把标签保存为 ZPL 或 PDF。

标签尺寸

介质尺寸是固定目录(packages/utils/src/labels.ts):label2x1、label4x2、公制 label100x50mm 和 label50x25mm,以及仅 PDF 的 avery5163。只有带 ZPL 尺寸的规格才能打印到热敏打印机;像 Avery 这样仅 PDF 的规格不行。

尺寸在两处设置。打印机路由固定自己的 mediaSizeId(该物理打印机装载的尺寸)。公司还有一个默认产品标签尺寸(companySettings.productLabelSize),在单据模板界面设置,在没有路由指定时供交互式标签下载路由使用。该默认值的位置参见公司设置,为内置标签模板设置样式的定制器参见文档。