certflow.services.certificate_print_service module

合格证打印服务(编排层)

编排完整的合格证打印业务流程,组合编号、打印引擎、日志等子模块。

拆分为: - config/print_config.py: 配置加载(canonical,原 cert_print_config.py 已迁入) - cert_numbering.py: 编号与 Certificate 写入 - cert_print_engine.py: GDI/HTML 打印引擎 - certificate_print_service.py (本文件): 编排层(参数补充、批量打印、日志、模板)

class certflow.services.certificate_print_service.CertificatePrintService(session, output_dir='output/certificates', printer_name='')[源代码]

基类:object

合格证打印服务(编排层)

组合编号、打印引擎、日志等子模块,提供完整的打印业务流程。

编号模式(委托 CertNumberingService):
  • auto_number: 自动编号(正则解析 production_order_no / supply_type,最常用)

  • manual_number: 手动指定前缀和起始序号

  • copy_number: 复制已有编号(单件直接复制)

打印(委托 CertPrintEngine):
  • print_single: 单台 GDI 精确打印

  • batch_print: 批量展开编号 → 逐台打印 → 记录日志

  • print_html_fallback: HTML 降级打印

持久化(委托 CertNumberingService):
  • create_certificates: 将已编号的 SalePlan 写入 Certificate 表

  • export_to_access: 导出到 Access 数据库

  • export_sign_tb_csv: 导出 CSV

日志与历史:
  • record_print_log: 记录打印日志到 SQLite print_logs 表

  • get_print_history: 查询打印历史

参数:
  • session (Session)

  • output_dir (str)

  • printer_name (str)

session

SQLAlchemy 数据库会话对象

output_dir

输出目录路径

printer_name

默认打印机名称

示例

>>> service = CertificatePrintService(session)
>>> # 自动编号
>>> count = service.auto_number([1, 2, 3], prefix_override="2506")
>>> # 写入 Certificate
>>> cert_count = service.create_certificates([1, 2, 3])
>>> # 逐台打印
>>> service.batch_print(certificate_id=1, printer_name="EPSON LQ-635KII")
auto_number(ids, prefix_override=None)[源代码]

自动编号(委托 CertNumberingService)

通过正则解析 production_order_no / supply_type 自动生成前缀并编号。

参数:
  • ids (list[int]) -- SalePlan.id 列表

  • prefix_override (str | None) -- 自定义前缀覆盖(如 "2506"),None 则按规则推导

返回:

成功编号的记录数

返回类型:

int

manual_number(ids, prefix, start, quantity, suffix='')[源代码]

手动编号(委托 CertNumberingService)

参数:
  • ids (list[int]) -- SalePlan.id 列表

  • prefix (str) -- 编号前缀

  • start (int) -- 起始序号

  • quantity (int) -- 每台数量

  • suffix (str) -- 编号后缀

返回:

成功编号的记录数

返回类型:

int

copy_number(ids, source_code)[源代码]

复制编号(委托 CertNumberingService)

将已有编号直接复制到目标记录(单件直接复制)。

参数:
  • ids (list[int]) -- SalePlan.id 列表

  • source_code (str) -- 源编号(作为复制模板)

返回:

成功编号的记录数

返回类型:

int

create_certificates(ids, printer_name='', print_status='已打印')[源代码]

将已编号的记录写入 Certificate 表(委托 CertNumberingService)

参数:
  • ids (list[int]) -- SalePlan.id 列表

  • printer_name (str) -- 写入的打印机名称

  • print_status (str) -- 写入的打印状态,默认 "已打印"

返回:

成功创建的 Certificate 记录数

返回类型:

int

export_to_access(ids, mdb_path=None)[源代码]

导出到 Access 数据库(委托 CertNumberingService)

参数:
  • ids (list[int]) -- Certificate.id 列表

  • mdb_path (str | None) -- 目标 .mdb 文件路径,None 则使用默认路径

返回:

成功导出的记录数

返回类型:

int

export_sign_tb_csv(output_path=None, limit=1000)[源代码]

导出 CSV(委托 CertNumberingService)

参数:
  • output_path (str | None) -- 输出 CSV 文件路径,None 则使用默认路径

  • limit (int) -- 最大导出条数

返回:

生成的 CSV 文件路径;失败返回 None

返回类型:

str | None

generate_print_certificates(ids, prefix_override=None, print_status='待打印')[源代码]

为选中的销售计划生成合格证(去重安全)。

跳过已生成 Certificate 的计划(避免证书重复生成与唯一约束冲突), 对剩余计划自动编号并写入 Certificate 表。编号与写库均委托 ``CertNumberingService``(数据访问层),本方法仅做编排与去重判定。

参数:
  • ids (list[int]) -- 销售计划 ID 列表

  • prefix_override (str | None) -- 编号前缀覆盖(None 则按规则推导)

  • print_status (str) -- 写入的打印状态

返回:

(编号数, 合格证生成数)

返回类型:

tuple[int, int]

print_single(data, serial_number, printer_name=None, print_fields=None)[源代码]

打印单台合格证(委托 CertPrintEngine)

参数:
  • data (dict[str, Any]) -- 打印数据字典(含 product_name、dn、pn 等字段)

  • serial_number (str) -- 合格证编号(SN)

  • printer_name (str | None) -- 打印机名称,None 则使用服务默认打印机

  • print_fields (list[str] | None) -- 需要打印的字段名列表,None 表示打印全部字段

返回:

是否打印成功

返回类型:

bool

property last_os_job_id: int | None

最近一次打印提交的 OS 作业 ID(委托 CertPrintEngine 捕获)。

偏差 5 作业 ID 捕获链路末端:队列的 _default_print_func 经此属性 拿到 spooler 作业 ID,写入 PrintJob.os_job_id,供 OS 级取消使用。

print_html_fallback(data, serial_number)[源代码]

HTML 降级打印(委托 CertPrintEngine)

参数:
  • data (dict[str, Any]) -- 打印数据字典

  • serial_number (str) -- 合格证编号(SN)

返回:

生成的 HTML 文件路径;失败返回 None

返回类型:

str | None

check_and_supplement_params(cert, parent=None, supplement_callback=None)[源代码]

检查 Certificate 参数完整性,必要时弹出参数补充对话框

对标 VBA print.vb 中一连串的 InputBox 调用: - 阀体材质 InputBox(dd.vb:1066) - 阀杆材质 InputBox(dd.vb:1074) - 启闭件材质 InputBox(dd.vb:1082) - 检验工号 InputBox(dd.vb:894-906) - 出厂年月修正 InputBox(dd.vb:986,1009)

参数:
  • cert (Certificate) -- Certificate ORM 对象

  • parent (object | None) -- 对话框父级窗口,服务层不感知其具体类型,仅原样透传给 supplement_callback;由 Controller/View 层传入 QWidget

  • supplement_callback (Callable | None) -- 可选回调 (model, prefill, material_grades, parent) → (confirmed, skip_all, params),由 Controller/View 层注入

返回类型:

dict[str, Any]

batch_print(certificate_id, printer_name=None, progress_callback=None, skip_supplement=False, parent=None, supplement_callback=None)[源代码]

批量打印:参数补充 → 解析编号 → 展开逐台编号 → 打印 → 记录日志

对标 VBA print.vb 批量打印流程: 1. 检查参数完整性,缺失则弹出补充对话框(对标 InputBox) 2. 解析 Certificate 编号范围 3. 展开为逐台编号后依次打印 4. 每台记录一条 print_log

参数:
  • certificate_id (int) -- Certificate.id

  • printer_name (str | None) -- 打印机名称

  • progress_callback (Callable[[int, int], None] | None) -- 进度回调 fn(current, total)

  • skip_supplement (bool) -- 跳过参数补充对话框(批量自动化时使用)

  • parent (object | None) -- 父窗口(用于对话框模态),服务层不感知其具体类型,原样透传

  • supplement_callback (Callable | None) -- 参数补充对话框回调 (model, prefill, material_grades, parent) → (confirmed, skip_all, params)

返回:

int, "failed": int, "serials": [...], "message": str}

返回类型:

{"success"

record_print_log(certificate_id, certificate_no, cert_data=None, printer_name='', status='success', error_message='', content_summary='')[源代码]

记录打印日志到 SQLite print_logs 表(字段名与 Access SignTb 兼容)

每次打印操作(单台或批量中的每台)都写入一条记录。 字段命名与 Access SignTb 保持一致,方便铭牌刻印系统直接读取。

SN 和 KKS 赋值遵循 VBA 逻辑: - SN = 短编号(去掉中间4位年月),如 V520Y - KKS = 完整编码(含年月),如 V2604520Y - 编码模式5/6时 SN/KKS 互换 - KKS 截断到12位

字段清理对齐VBA(温度去≤/℃,PN去MPA后缀等)。

参数:
  • certificate_id (int) -- 关联合格证批次ID

  • certificate_no (str) -- 合格证完整编号(如 V2604520Y)

  • cert_data (dict[str, Any] | None) -- 证书完整数据字典

  • printer_name (str) -- 打印机名称

  • status (str) -- 打印状态 (success/failed)

  • error_message (str) -- 错误信息

  • content_summary (str) -- 打印内容摘要

返回:

PrintLog 对象或 None

返回类型:

PrintLog | None

get_print_history(limit=100, status_filter=None)[源代码]

查询打印历史(返回字段名与 Access SignTb 兼容)

参数:
  • limit (int) -- 最大返回条数

  • status_filter (str | None) -- 状态筛选 (success/failed),None 表示全部

返回:

打印历史列表,每条记录包含 SignTb 兼容字段

返回类型:

list[dict[str, Any]]

apply_template_to_certificate(cert_id, template)[源代码]

将 templates.xlsx 中的模板参数应用到 Certificate 记录

参数:
  • cert_id (int) -- Certificate.id

  • template (dict[str, Any]) -- 从 TemplateManager.query_by_model 返回的模板 dict

返回:

是否成功

返回类型:

bool

writeback_template_to_xlsx(cert_id, xlsx_path)[源代码]

打印完成后回填到 templates.xlsx「数据库」表

参数:
  • cert_id (int) -- Certificate.id

  • xlsx_path (str) -- templates.xlsx 文件路径

返回:

是否成功

返回类型:

bool