VBA SignTb 字段赋值对照文档

对照 VBA print.vb 子程序「将打印内容添加到Access数据库」与 Python CertificatePrintService.record_print_log, 逐一检查 51 个 Br(1..51) 字段的赋值逻辑是否一致。

VBA 源文件: data/print.vb Python 服务: src/certflow/services/certificate_print_service.py Python 模型: src/certflow/models/print_log.py VBA 子程序入口: 第 2211 行 Sub 将打印内容添加到Access数据库(...)


VBA Br 数组与 Access SignTb 字段映射总览

Br#

SignTb 字段名

类型

说明

Br(1)

JiHuaRiQi

Date

计划日期

Br(2)

DingHuoDanWei

Text

订货单位

Br(3)

XiangMuMingCheng

Text

项目名称

Br(4)

ChanPinMingCheng

Text

产品名称

Br(5)

ChanPinXingHao

Text

产品型号

Br(6)

DN

Text

公称通径

Br(7)

PN

Text

公称压力

Br(8)

ShiYongJieZhi

Text

适用介质

Br(9)

ShiYongWenDu

Text

适用温度

Br(10)

ChuChangRiQiYear

Int

出厂日期年

Br(11)

ChuChangRiQiMonth

Int

出厂日期月

Br(12)

SN

Text

出厂编号(短编码)

Br(13)

KKS

Text

KKS编码(完整编码)

Br(14)

Sign_XingHao

Bool

型号刻印标记

Br(15)

Sign_DN

Bool

口径刻印标记

Br(16)

Sign_PN

Bool

压力刻印标记

Br(17)

Sign_JieZhi

Bool

介质刻印标记

Br(18)

Sign_WenDu

Bool

温度刻印标记

Br(19)

Sign_Year

Bool

出厂年刻印标记

Br(20)

Sign_Month

Bool

出厂月刻印标记

Br(21)

Sign_SN

Bool

出厂编号刻印标记

Br(22)

Sign_KKS

Bool

KKS刻印标记

Br(23)

Sign_ALL

Bool

全部刻印完毕标记

Br(24)

hgz_ShuLiang

Int

合格证数量

Br(25)

hgz_YangShi

Int

合格证版型

Br(26)

hgz_PdfPic

Int

合格证图片样式

Br(27)

hgz_BianMaModel

Int

合格证编码模式

Br(28)

hgz_xlMuBanName

Text(Memo)

合格证模板名称

Br(29)

hgz_PrintTime

Date

合格证打印时间

Br(30)

PrinterName

Text

合格证打印机名(存整数代码)

Br(31)

Sign_PrintTime

Date

铭牌打印时间(未使用,空值)

Br(32)

Sign_ModelFullName

Text

铭牌路径(或重复条目数)

Br(33)

Sign_PrintCount

Int

铭牌标记条目数

Br(34)

不存在

VBA 跳过 Br(34),INSERT SQL 中用 i+2 偏移

Br(35)

FaTiCaiZhi

Text

阀体材质

Br(36)

FaGanCaiZhi

Text

阀杆材质

Br(37)

QiBiJianCaiZhi

Text

启闭件材质

Br(38)

JiShuYaoQiu

Text(Memo)

技术要求

Br(39)

SheBeiWeiHao

Text

设备位号

Br(40)

ZiXiangMu

Text

子项目

Br(41)

SNFromXSB

Text

销售产品编号

Br(42)

ZhongLei

Text

销售种类

Br(43)

ZhongLiang

Text

销售重量要求

Br(44)

XuHao

Text

销售序号标识

Br(45)

YaoHuoDanHaoFromXSB

Text

营销部要货单号

Br(46)

Sign_FTCZ

Bool

阀体材质刻印标记

Br(47)

Sign_FGCZ

Bool

阀杆材质刻印标记

Br(48)

Sign_QBJCZ

Bool

启闭件材质刻印标记

Br(49)

DcOrPlPrint

Text

打印模式 ("0"=单次, "1"=批量)

Br(50)

MaxJinShuiTou

Text

最大净水头

Br(51)

MaxShengYaShuiTou

Text

最大升压水头


逐字段对照详情

Br(1) — JiHuaRiQi(计划日期)

属性

VBA

Python

来源

ar(2, 13)(Excel B13 单元格)

cert_data.get("plan_date", "")

逻辑

IIf(Len(Trim(ar(2,13)))>0, CDate(ar(2,13)), CDate("1"))

直接取 cert_data["plan_date"]

默认值

CDate("1") = 1899-12-30

"" (空字符串)

类型

Date

DateTime (String → DateTime)

状态

⚠️ 有差异 — VBA 空值默认 1899-12-30,Python 默认空字符串。但实际使用中 plan_date 极少为空。

Br(2) — DingHuoDanWei(订货单位)

属性

VBA

Python

来源

ar(2, 14)(Excel B14 单元格)

cert_data.get("customer", "")

逻辑

IIf(Len(Trim(ar(2,14)))>0, ar(2,14), "【空白订货单位】")

cert_data.get("customer", "") or ""

默认值

"【空白订货单位】"

"" (空字符串)

状态

逻辑一致 — 都有空值保护。默认值不同但不影响铭牌系统读取。

Br(3) — XiangMuMingCheng(项目名称)

属性

VBA

Python

来源

Arr_All(1, 30)(全局数组)

cert_data.get("project_name", "")

逻辑

先判断 Arr_All 是否为空,再判断 Trim 长度

cert_data.get("project_name", "") or ""

默认值

"【空数组_空白项目名称】""【空白项目名称】"

"" (空字符串)

状态

逻辑一致 — 都有空值保护。

Br(4) — ChanPinMingCheng(产品名称)

属性

VBA

Python

来源

ar(3, 2)(Excel B4 单元格)

cert_data.get("product_name", "")

逻辑

IIf(Len(Trim(ar(3,2)))>0, ar(3,2), "【空白产品名称】")

cert_data.get("product_name", "") or ""

默认值

"【空白产品名称】"

"" (空字符串)

状态

逻辑一致

Br(5) — ChanPinXingHao(产品型号)

属性

VBA

Python

来源

ar(4, 2)(Excel B5 单元格)

cert_data.get("product_model", "")

逻辑

IIf(Len(Trim(ar(4,2)))>0, ar(4,2), "【空白产品型号】")

cert_data.get("product_model", "") or ""

默认值

"【空白产品型号】"

"" (空字符串)

状态

逻辑一致

Br(6) — DN(公称通径)

属性

VBA

Python

来源

ar(5, 2)ar(5, 3)(Excel B6/C6 单元格)

cert_data.get("dn", "")

逻辑

Trim(IIf(Len(Trim(ar(5,2)))=0, IIf(Len(Trim(ar(5,3)))=0, "", ar(5,3)), ar(5,2)))

cert_data.get("dn", "") or ""

额外处理

去掉末尾 "MM";如果型号含 "LB" 则保留英制格式

默认值

"空白公称通径"

"" (空字符串)

状态

⚠️ 有差异 — VBA 有英制/公制判断(优先 B6,空则取 C6),Python 直接取值。VBA 会去掉末尾 "MM"。如果铭牌系统用 LB 制阀门,DN 格式可能不一致。

Br(7) — PN(公称压力)

属性

VBA

Python

来源

ar(6, 2)ar(6, 3)(Excel B7/C7 单元格)

cert_data.get("pn", "")

逻辑

Trim(IIf(Len(Trim(ar(6,2)))=0, IIf(Len(Trim(ar(6,3)))=0, "", Trim(ar(6,3))&"Lb"), ar(6,2)))

cert_data.get("pn", "") or ""

额外处理

去掉末尾 "MPA";如果型号含 "LB" 则追加 "Lb" 后缀

默认值

"空白公称压力"

"" (空字符串)

状态

⚠️ 有差异 — 同 DN,VBA 有英制/公制判断和单位后缀清理。

Br(8) — ShiYongJieZhi(适用介质)

属性

VBA

Python

来源

ar(8, 2)(Excel B9 单元格)

cert_data.get("medium", "")

逻辑

IIf(Len(Trim(ar(8,2)))>0, ar(8,2), "【空白适用介质】")

cert_data.get("medium", "") or ""

状态

逻辑一致

Br(9) — ShiYongWenDu(适用温度)

属性

VBA

Python

来源

ar(7, 2)(Excel B8 单元格)

cert_data.get("temperature", "")

逻辑

Replace(ar(7,2), "≤", "") → 去掉 "≤",然后 Replace("℃", "") → 去掉 "℃",再去空格

cert_data.get("temperature", "") or ""

默认值

"【空白适用温度】"

"" (空字符串)

状态

⚠️ 有差异 — VBA 会清理 符号,Python 不会。例如 Excel 中存 ≤120℃,VBA 输出 120,Python 输出 ≤120℃如果铭牌系统依赖纯数字温度值,可能不兼容。

Br(10) — ChuChangRiQiYear(出厂日期年)

属性

VBA

Python

来源

ar(11, 2)(Excel B12 单元格)

cert_data.get("manufacture_year", 0)

逻辑

CInt(Left(ar(11,2), 4)) 取前4位转整数

直接取 cert_data

默认值

1900(如果非数字)

0

状态

⚠️ 有差异 — VBA 默认 1900,Python 默认 0。且 VBA 从 Excel 日期字段取前4位,Python 依赖调用方传入。当前 batch_print 中硬编码为 0,没有从 Certificate 中解析出厂日期。

Br(11) — ChuChangRiQiMonth(出厂日期月)

属性

VBA

Python

来源

ar(11, 2)(Excel B12 单元格)

cert_data.get("manufacture_month", 0)

逻辑

CByte(Right(ar(11,2), 2)) 取后2位转字节

直接取 cert_data

默认值

1(如果非数字)

0

状态

⚠️ 有差异 — 同 Br(10)。当前 batch_print 中硬编码为 0。

Br(12) — SN(出厂编号,短编码)

这是最关键的字段之一,VBA 逻辑复杂。

属性

VBA

Python

来源

Excel B13 或 B12 单元格(取决于编码模式)

_derive_sn_and_kks(certificate_no) 从 certificate_no 推导

逻辑

见下方详细分析

见下方

VBA 逻辑(按 hgz_BianMaModel 分支):

Select Case A_Byte_hgzBianMaModel:
  Case 5, 6:  # 双编码模式
    Select Case ar(2, 8):  # 判断 B12/B13 哪个是 KKS、哪个是 SN
      "B12单元格为KKS编码,B13单元格为产品编号":
        SN = ar(13, 2)  # B13
      "B12单元格为产品编号,B13单元格为KKS编码":
        SN = ar(12, 2)  # B12
  Case 4:  # 正则编码模式
    SN = ar(12, 2)  # 取 B12
    如果以 V 开头: SN = V + Right(编码, Len-5)  # 去掉中间4位年月
  Case Else:  # 普通模式 (0,1,2,3)
    SN = ar(12, 2)  # 取 B12
    如果以 V 开头: SN = V + Right(编码, Len-5)  # 去掉中间4位年月

Python 逻辑 (_derive_sn_and_kks):

def _derive_sn_and_kks(full_code: str) -> tuple[str, str]:
    if not full_code or not full_code.startswith("V"):
        return full_code or "", full_code or ""
    if len(full_code) > 5:
        sn = full_code[0] + full_code[5:]  # V + 去掉年月的部分
    else:
        sn = full_code
    kks = full_code
    return sn, kks

状态: ⚠️ 有差异 — 两处差异:

  1. Python 不处理编码模式 5/6 的 KKS/SN 互换:当前始终将 certificate_no 按固定规则(SN=短编码, KKS=长编码)处理,不会根据 number_model(即 hgz_BianMaModel)做分支。

  2. CertificateController.batch_print_certificates 中 SN 和 KKS 都直接设为 certificate_no,完全没有调用 _derive_sn_and_kks

Br(13) — KKS(KKS编码,完整编码)

属性

VBA

Python

来源

Excel B12 或 B13(取决于编码模式)

_derive_sn_and_kks(certificate_no)

逻辑

取原始值,最多12位:IIf(Len(ar(12,2))>11, Left(ar(12,2),12), ar(12,2))

直接取 full_code

截断

最多 12 位

无截断

状态

⚠️ 有差异 — Python 不截断 KKS 到 12 位,也不处理编码模式 5/6 的互换。

Br(14) ~ Br(23) — 刻印标记(10个 Boolean)

字段

VBA 值

Python 值

状态

Br(14) Sign_XingHao

False

默认 False(ORM default)

✅ 一致

Br(15) Sign_DN

False

默认 False

✅ 一致

Br(16) Sign_PN

False

默认 False

✅ 一致

Br(17) Sign_JieZhi

False

默认 False

✅ 一致

Br(18) Sign_WenDu

False

默认 False

✅ 一致

Br(19) Sign_Year

False

默认 False

✅ 一致

Br(20) Sign_Month

False

默认 False

✅ 一致

Br(21) Sign_SN

False

默认 False

✅ 一致

Br(22) Sign_KKS

False

默认 False

✅ 一致

Br(23) Sign_ALL

False

默认 False

✅ 一致

结论: 全部一致。这些字段在打印时初始化为 False,由铭牌刻印系统后续更新。

Br(24) — hgz_ShuLiang(合格证数量)

属性

VBA

Python

来源

ar(2, 18)(Excel R3 单元格)

cert_data.get("copies", 1)

逻辑

IIf(Len(Trim(ar(2,18)))>0, ar(2,18), 0)

默认值 1

默认值

0

1

状态

⚠️ 有差异 — VBA 默认 0,Python 默认 1。实际使用中此值应为正整数,差异不大。

Br(25) — hgz_YangShi(合格证版型)

属性

VBA

Python

来源

子程序参数 A_Byte_hgzYangShi(Byte)

cert_data.get("template_style", 0)

含义

1=全中文, 2=中英文, 3=全英文

batch_print 中硬编码为 1

状态

基本一致 — 都是整数,但 VBA 从调用方传入,Python 默认 0 且 batch_print 中硬编码 1。

Br(26) — hgz_PdfPic(合格证图片样式)

属性

VBA

Python

来源

子程序参数 A_Byte_hgzPic(Byte)

cert_data.get("pic_style", 0)

状态

一致 — 默认 0。

Br(27) — hgz_BianMaModel(合格证编码模式)

属性

VBA

Python

来源

子程序参数 A_Byte_hgzBianMaModel(Byte)

cert_data.get("number_model", 0)

含义

0=普通, 4=正则, 5/6=双编码模式

batch_print 中硬编码为 0

状态

⚠️ 潜在差异 — 此字段影响 Br(12) SN 和 Br(13) KKS 的赋值逻辑(见上方)。当前 Python 硬编码 0,如果实际使用模式 5/6,SN/KKS 会不正确。

Br(28) — hgz_xlMuBanName(合格证模板名称)

属性

VBA

Python

来源

ar(13, 7)(Excel G14 单元格)

cert_data.get("template_name", "")

处理

将换行符 Chr(10) 替换为

默认值

"【未知Excel模板名称】"

""

状态

⚠️ 有差异 — Python 不替换换行符为

Br(29) — hgz_PrintTime(合格证打印时间)

属性

VBA

Python

来源

CDate(Now())

datetime.now()

状态

一致 — 都取当前时间。

Br(30) — PrinterName(合格证打印机名)

属性

VBA

Python

来源

ActivePrinter → 映射为整数代码

printer_name 参数(字符串)

映射表

0=默认, 1=Foxit PDF, 2=EPSON LQ-635KII, 3=网络打印机, 4=HP LaserJet M225, 5=Microsoft XPS, 6=DocuCom PDF, 7=HP LaserJet M225(销售)

直接存打印机名称字符串

状态

⚠️ 有差异 — VBA 存整数代码,Python 存字符串。如果铭牌系统根据此字段值做逻辑判断,可能不兼容。 需要确认铭牌系统是否依赖此字段。

Br(31) — Sign_PrintTime(铭牌打印时间)

属性

VBA

Python

来源

未赋值(注释写"未使用")

ORM 未显式赋值,默认为 None

状态

一致 — 都是空值。

Br(32) — Sign_ModelFullName(铭牌路径)

属性

VBA

Python

来源

如果为空则填入 "重复条目数:XX"

ORM 未显式赋值,默认为 None

逻辑

IIf(A_Sign_ModelFullName="", "重复条目数:" & Format(条目数, "#00"), A_Sign_ModelFullName)

状态

⚠️ 有差异 — VBA 有特殊逻辑:当铭牌路径为空时,先查询 SN 重复数,然后填入重复条目数统计。Python 直接为 None。这个重复条目数对铭牌系统可能有意义(标识同编号重复打印次数)。

Br(33) — Sign_PrintCount(铭牌标记条目数)

属性

VBA

Python

来源

0

Sign_PrintCount=0(在 record_print_log 中硬编码)

状态

一致

⚠️ Br(34) — 不存在

VBA 中 Br(34) 没有赋值,INSERT SQL 中从 Case 34 开始用 Br(i+2) 做偏移(第 2537-2544 行)。 Python 模型中也正确跳过了 Br(34),字段从 Br(35) 开始。

Br(35) — FaTiCaiZhi(阀体材质)

属性

VBA

Python

来源

子程序参数 S阀体材质

cert_data.get("body_material", "")

默认值

"【未录入阀体材质】"

""

状态

逻辑一致 — 都有空值保护。

Br(36) — FaGanCaiZhi(阀杆材质)

属性

VBA

Python

来源

子程序参数 S阀杆材质

cert_data.get("stem_material", "")

默认值

"【未录入阀杆材质】"

""

状态

逻辑一致

Br(37) — QiBiJianCaiZhi(启闭件材质)

属性

VBA

Python

来源

子程序参数 S启闭件材质

cert_data.get("disc_material", "")

默认值

"【未录入启闭件材质】"

""

状态

逻辑一致

Br(38) — JiShuYaoQiu(技术要求)

属性

VBA

Python

来源

Arr_All(1, 69)(全局数组第69列)

cert_data.get("tech_requirements", "")

默认值

"【空数组_空白技术要求】""【空白技术要求】"

""

状态

逻辑一致

Br(39) — SheBeiWeiHao(设备位号)

属性

VBA

Python

来源

Arr_All(1, 70)

cert_data.get("equipment_no", "")

默认值

"【空数组_空白设备位号】"

""

状态

逻辑一致

Br(40) — ZiXiangMu(子项目)

属性

VBA

Python

来源

Arr_All(1, 71)

cert_data.get("sub_project", "")

默认值

"【空数组_空白子项目】"

""

状态

逻辑一致

Br(41) — SNFromXSB(销售产品编号)

属性

VBA

Python

来源

Arr_All(1, 72)

cert_data.get("sales_sn", "")

默认值

"【空数组_空白销售部产品编号】"

""

状态

逻辑一致

Br(42) — ZhongLei(销售种类)

属性

VBA

Python

来源

Arr_All(1, 73)

cert_data.get("category", "")

默认值

"【空数组_空白销售部种类】"

""

状态

逻辑一致

Br(43) — ZhongLiang(销售重量要求)

属性

VBA

Python

来源

Arr_All(1, 74)

cert_data.get("weight", "")

默认值

"【空数组_空白销售部重量要求】"

""

状态

逻辑一致

Br(44) — XuHao(销售序号标识)

属性

VBA

Python

来源

Arr_All(1, 75)

cert_data.get("seq_no", "")

默认值

"【空数组_空白序号】"

""

状态

逻辑一致

Br(45) — YaoHuoDanHaoFromXSB(营销部要货单号)

属性

VBA

Python

来源

Arr_All(1, 18)

cert_data.get("requisition_no", "")

默认值

"【空数组_空白营销部要货单号】"

""

状态

逻辑一致

Br(46) — Sign_FTCZ(阀体材质刻印标记)

属性

VBA

Python

False

默认 False(ORM default)

状态

一致

Br(47) — Sign_FGCZ(阀杆材质刻印标记)

属性

VBA

Python

False

默认 False(ORM default)

状态

一致

Br(48) — Sign_QBJCZ(启闭件材质刻印标记)

属性

VBA

Python

False

默认 False(ORM default)

状态

一致

Br(49) — DcOrPlPrint(打印模式)

属性

VBA

Python

来源

子程序参数 S打印模式

cert_data.get("print_mode", "1")

逻辑

IIf(S打印模式="单次打印", "0", "1")

直接取,默认 "1"

类型

字符串 ("0"/"1")

字符串 ("0"/"1")

状态

一致 — 都是字符串类型。

Br(50) — MaxJinShuiTou(最大净水头)

属性

VBA

Python

来源

子程序参数 S最高净水头

cert_data.get("max_inlet_head", "")

默认值

"【空白】"

""

状态

逻辑一致

Br(51) — MaxShengYaShuiTou(最大升压水头)

属性

VBA

Python

来源

子程序参数 S最大升压水头

cert_data.get("max_boost_head", "")

默认值

"【空白】"

""

状态

逻辑一致


差异汇总

🔴 关键差异(影响铭牌系统读取)

#

字段

差异描述

建议

1

Br(12) SN

Python 不处理编码模式 5/6 的 KKS/SN 互换

_derive_sn_and_kks 中增加 number_model 参数,模式 5/6 时互换

2

Br(13) KKS

VBA 最多截断 12 位,Python 不截断

增加 full_code[:12] 截断

3

Br(30) PrinterName

VBA 存整数代码,Python 存字符串

确认铭牌系统是否依赖此字段做判断

🟡 中等差异(可能影响特定场景)

#

字段

差异描述

建议

4

Br(9) ShiYongWenDu

VBA 清理 / 符号,Python 不清理

在写入前做符号清理

5

Br(10) ChuChangRiQiYear

当前硬编码 0,未从 Certificate 解析

cert.issue_date 解析年月

6

Br(11) ChuChangRiQiMonth

当前硬编码 0,未从 Certificate 解析

同上

7

Br(32) Sign_ModelFullName

VBA 填入重复条目数,Python 为空

如果铭牌系统需要此数据则需补实现

8

Br(27) hgz_BianMaModel

硬编码 0,未反映实际编码模式

从 Certificate 获取实际编码模式

🟢 轻微差异(不影响铭牌系统)

#

字段

差异描述

9

Br(1) JiHuaRiQi

VBA 空值默认 1899-12-30

10

Br(6) DN

VBA 有英制/公制判断和 "MM" 后缀清理

11

Br(7) PN

VBA 有英制/公制判断和 "MPA"/"Lb" 后缀处理

12

Br(24) hgz_ShuLiang

VBA 默认 0,Python 默认 1

13

Br(28) hgz_xlMuBanName

VBA 替换换行符为

14

Br(2,3,4,5,8,35-45)

VBA 空值默认用 【空白xxx】 占位,Python 用空字符串


CertificateController.batch_print_certificates 额外问题

src/certflow/controllers/certificate_controller.py 中第 232-240 行直接创建 PrintLog 对象,完全绕过了 CertificatePrintService.record_print_log,导致:

  1. SN/KKS 未推导SNKKS 都直接设为 cert.certificate_no(长编码),没有短编码/长编码分离

  2. 缺少 51 个兼容字段的完整赋值:只设置了 5 个字段(certificate_id, SN, KKS, printer_name, hgz_ShuLiang, status, content_summary)

  3. 缺少打印时间:没有设置 hgz_PrintTime

建议:将此处的直接创建 PrintLog 改为调用 CertificatePrintService.record_print_log


扩展字段说明

以下 4 个字段是 SignTb 中不存在的扩展字段,仅存在于 Python SQLite print_logs 表:

字段

类型

用途

certificate_id

Integer

关联回 Certificate 批次表

status

String(20)

打印成功/失败状态 (success/failed)

error_message

Text

失败原因

content_summary

Text

可读摘要

注意:VBA 中还有 Br(32) 的特殊逻辑(存入重复条目数),这是铭牌系统判断同编号重复打印的依据。如果铭牌系统切换到 SQLite print_logs 表,需确认是否还需要此逻辑。



第二部分:VBA xlsx 回填子程序对照(含业务背景说明)

业务背景

VBA 时代的数据流经过多年演变,各子程序的定位如下:

子程序

原始用途

现状

① 保存到"打印历史"

打印日志,方便后期点击某条记录按格式重新排版

后来改用 Access SignTb 作为铭牌数据源,此表可以停止维护

② 保存到"数据库"

记录每个单元格的值和格式(对齐/字体/字号等),用于后期还原模板格式

VBA 通过复制整个单元格实现;Python PySide6 打印不需要格式信息,只需记录值

③ 保存到 Access SignTb

将打印信息写入 Access 数据库,作为铭牌刻印系统的数据源

铭牌系统读的是 32 位 Access 2010,Python 64 位无法直连,需导出或链表

④ 保存到"合格证清单"

将打印后的参数回填到合格证主表

Python 体系用 SQLite Certificate + SalePlan 替代,字段映射待确认

VBA 子程序总览(按调用顺序)

每次打印后,VBA 按顺序调用以下 4 个子程序:

打印完成
  ├── 1. 保存合格证内容到打印历史工作表  (→ "打印历史" 工作表)  [已可废弃]
  ├── 2. 将打印内容添加到数据库          (→ "数据库" 工作表)    [只需值,无需格式]
  ├── 3. 将打印内容添加到Access数据库     (→ SignDB.mdb SignTb) [铭牌数据源]
  └── 4. 保存数据到合格证清单            (→ "合格证_2026" 工作表) [待确认映射]

2.1 ① Sub 保存合格证内容到打印历史工作表

定位: N 台打印为一条记录,工作日志性质。 VBA 源位置: print.vb 第 2754-2859 行 Python 对应: CertificatePrintService.record_print_log → SQLite print_logs

结论: ✅ Python 已覆盖核心功能 — SQLite print_logs 表替代了 xlsx "打印历史"。如果后期 SQLite 数据量偏大,可拆分为多个库(一库一表)。

VBA "打印历史" 工作表列结构(22列):

VBA 字段

来源

Python SQLite 对应

A (1)

序号

上一行行号

id 自增

B (2)

合格证编号

mk.Cells(13, 7)

certificate_no / SN

C-D (3-4)

产品名称/型号

mk.Cells(3,2), mk.Cells(4,2)

ChanPinMingCheng / ChanPinXingHao

E (5)

公称通径

mk.Range("B5")

DN

F (6)

公称压力

mk.Range("B6")

PN

G-L (7-12)

温度/介质/标准号/工号/日期/编号

B7-B12

✅ 分散在 print_logs 兼容字段

M-N (13-14)

通径补充/压力补充

C5/C6

❌ 未实现

O (15)

编号区间

参数

❌ 未实现

P (16)

打印时间

参数

hgz_PrintTime

Q (17)

工作簿路径

ThisWorkbook.FullName

❌ 未实现(无意义)

R (18)

打印机名称+类别

ActivePrinter + 打印类别

⚠️ 仅存打印机名

S (19)

模板名称

mk.Name

hgz_xlMuBanName

T (20)

打印数量

mk.Cells(3, 7)

hgz_ShuLiang

U (21)

KKS 编码

B12/B13

KKS

V (22)

本厂编号

B12/B13

SN


2.2 ② Sub 将打印内容添加到数据库(写入 xlsx "数据库" 工作表)

定位: VBA 通过复制整个单元格实现,不仅记录打印值,还记录对齐、字体、字号等格式信息。Python PySide6 窗体打印不需要格式信息,只需记录值即可。

VBA 源位置: print.vb 第 2561-2724 行 Python 对应: TemplateManager.writeback_to_xlsxtemplates.xlsx「数据库」表

结论: ✅ 仅需存值,格式信息可省略。Python writeback_to_xlsx 已覆盖核心业务字段(产品名/型号/DN/PN/温度/介质/标准号/工号/日期/编号)。

不需要实现的部分(VBA 特有的格式信息,PySide6 场景下无意义):

  • 行高/列宽/合并标记 → PySide6 用 templates.xlsx 模板,不需要还原格式

  • 工作簿路径 → 无意义

  • KKS 备注 → 已通过 print_logs 记录


2.3 ④ Sub 保存数据到合格证清单

VBA 源位置: print.vb 第 2985-3125 行 目标工作表: 合格证_2026(或 合格证_2022Python 对应: — 完全未实现

VBA 逻辑: 每次打印后将温度/介质/标准号/材质等回写到"合格证_2026"主表。

Python 体系现状: ⚠️ 待确认 — 需要先搞清楚两个问题:

  1. 销售计划表 → 合格证表的字段映射关系:合格证表哪些字段来自 SalePlan 导入?哪些需要打印后回填?哪些字段空着没用?

  2. Certificate 表的结构:Certificate 已有 working_tempworking_mediumtest_standardproduct_specprint_time 等字段,如果这些字段在 Certificate 创建时已从模板写入,则可能不需要额外回填。

目前已知的字段映射CertificatePrintService.create_certificates):

SalePlan 字段

Certificate 字段

备注

id

sale_plan_id

FK

unique_key

unique_key

唯一键

production_order_no

production_order_no

冗余

sales_order_no

sales_order_no

冗余

product_code

certificate_no

产品编号

cert_product_name / product_name

product_name

优先 cert_ 前缀

cert_product_model / product_model

product_model

优先 cert_ 前缀

cert_product_spec / product_spec

product_spec

优先 cert_ 前缀

quantity

quantity

数量

plan_date

plan_date

计划日期

customer

customer

订货单位

project_name

project_name

项目名称

test_standard

test_standard

试压标准

Certificate 表中可能缺少的字段(VBA 需要但 Certificate 未定义):

  • 阀体材质 (FaTiCaiZhi)

  • 阀杆材质 (FaGanCaiZhi)

  • 启闭件材质 (QiBiJianCaiZhi)

  • 口径/规格补充

下一步: 需确认 SalePlan 导入时哪些字段被填充到 Certificate,哪些字段是空的需要打印后回填。


第四部分:VBA 交互式参数补充对照

4.1 参数来源梳理

关键澄清

参数

来源

说明

working_temp (适用温度)

模板匹配

筛选同型号模板后选择某个模板,得到工作温度值。不来自 SalePlan

working_medium (适用介质)

模板匹配

同上,来自模板选择。不来自 SalePlan

test_standard (标准号)

模板匹配 或 SalePlan

apply_template_to_certificate 将模板的 check_standard 写入;create_certificates 也从 SalePlan.test_standard 赋值。两者都可覆盖。

product_spec (口径/DN)

模板匹配

模板的 dnCertificate.product_spec

PN (公称压力)

模板匹配

模板的 pnCertificate 无此字段! 所有 PN 输出硬编码为空字符串。

阀体/阀杆/启闭件材质

VBA InputBox

VBA 弹出输入框让用户填写,Python 完全未实现

4.2 VBA 的 InputBox 交互汇总

VBA 在打印主流程 Sub 批量打印流水合格证 中有大量交互式参数补充,共约 40+ 处 InputBox,按场景分类:

场景 A:英制压力模式(型号含 LB 或 2500)

InputBox

触发条件

提示文本

口径

始终弹出

请手工输入产品口径

英制压力

始终弹出

请手工输入产品英制压力 :LB 磅级压力

适用温度

始终弹出

请填写适用温度

VBA 英制判断逻辑(第 634 行):

If (UCase(.Cells(iR, 14 + OfSet)) Like "*LB*") Or (UCase(.Cells(iR, 14 + OfSet)) Like "*2500*") Then

型号包含 LB2500 → 英制。VBA 还会从型号中提取 LB 等级(150/300/600/1500/2500)作为 InputBox 默认值。

场景 B:公制压力模式(手动)

InputBox

触发条件

提示文本

口径

始终弹出

请手工输入产品口径

压力

始终弹出

请手工输入产品压力

适用温度

始终弹出

请填写适用温度

场景 C:公制压力模式(自动)

InputBox

触发条件

提示文本

口径

口径为空时

请输入口径值:

压力

压力为空时

请输入压力值:

适用温度

始终弹出

请填写适用温度

场景 D:通用参数(所有模式)

InputBox

提示文本

用途

适用介质

请手工输入产品适用介质

补充适用介质

检验工号

请手工输入英文工号...

中英文模板补充检验工号

出厂年月

请修改出厂年月,格式如2021.03

修正出厂日期

阀体材质

请输入保存到数据库的阀体材质文本

每次打印前弹出

阀杆材质

请输入保存到数据库的阀杆材质文本

每次打印前弹出

启闭件材质

请输入保存到数据库的启闭件材质文本

每次打印前弹出

场景 E:编码相关交互

InputBox

触发条件

提示文本

编码前缀

军工模式

请输入军工合格证编码的前缀字符...

起始编号

军工/line1 模式

请输入军工合格证编码的起始编号...

份数

军工/line1 模式

请输入要打印的军工合格证的份数...

编号后缀

军工/line1 模式

请输入军工合格证编码的编号后缀...

产品名称

新型号时

请输入正确的产品名称

场景 F:行号回退(数据未找到时)

InputBox

提示文本

行号

请输入数据要保存到的行号

4.3 VBA 英制/公制口径和压力的处理细节

在「数据库」工作表中

VBA 只是原样复制单元格(mk.Range("B5").Copy),不做转换:

  • B5 单元格 → 数据库第 5 列(公称通径 DN)

  • C5 单元格 → 数据库第 13 列(备用口径)

  • B6 单元格 → 数据库第 6 列(公称压力 PN)

  • C6 单元格 → 数据库第 14 列(备用压力)

B5/B6 存公制数据(DN mm / PN MPa),C5/C6 存英制数据(inch / LB 磅级数值)。

在 Access SignTb 中(写入时转换)

' DN 口径:B5 优先,空则用 C5
A_Str_DN = Trim(IIf(Len(Trim(ar(5,2))) = 0, ar(5,3), ar(5,2)))

' PN 压力:B6 优先,空则用 C6 + "Lb" 后缀
A_Str_PN = Trim(IIf(Len(Trim(ar(6,2))) = 0, Trim(ar(6,3)) & "Lb", ar(6,2)))

' 清理单位后缀
If UCase(Right(A_Str_DN, 2)) = "MM" Then A_Str_DN = Left(A_Str_DN, Len(A_Str_DN) - 2)
If UCase(Right(A_Str_PN, 3)) = "MPA" Then A_Str_PN = Left(A_Str_PN, Len(A_Str_PN) - 3)

' 英制型号:确保压力以 "Lb" 结尾
If UCase(A_Str_CPXH) Like "*LB*" Then A_Str_PN = IIf(Right(A_Str_PN, 2) = "Lb", A_Str_PN, A_Str_PN & "Lb")

4.4 Python 现状 vs VBA 交互差距

VBA 功能

Python 现状

差距

英制 LB 判断 (*LB* / *2500*)

不会判断英制型号

英制 LB 等级提取 (150/300/600/1500/2500)

不能从型号中提取 LB 等级

DN 口径 B5/C5 回退逻辑

dn = cert_data.get("dn", "") — 单一来源

没有 C5 备用列

PN 压力 B6/C6 回退 + Lb 后缀

pn 硬编码为空字符串

完全丢失

DN 后缀清理 (去 MM)

不去掉单位后缀

PN 后缀清理 (去 MPA)

不去掉单位后缀

型号含 LB 时自动加 Lb 后缀

压力值可能缺少单位

适用温度 InputBox

仅从模板取

新型号时无法补充

适用介质 InputBox

仅从模板取

新型号时无法补充

检验工号 InputBox

仅从模板取

始终为空

阀体/阀杆/启闭件材质 InputBox

材质数据完全丢失

出厂年月修正 InputBox

无法修正

产品名称修正 InputBox

新型号无法修正

行号回退(数据未找到)

匹配失败时无回退方案

4.5 需要新增的参数对照表

根据你的说明,Python 后期应该增加以下几类参数对照表:

对照表

内容

用途

型号-压力对照

型号 → 默认压力等级 (MPa / LB)

新型号时自动推荐压力值,减少 InputBox

口径-字符串对照

口径值 → 显示文本(如 DN100 → "100" 或 "4inch")

英制/公制自动转换和显示

材质牌号-元素对照

材质牌号 → 化学成分元素值

自动填充阀体/阀杆/启闭件材质

型号-温度对照

型号 → 默认适用温度

新型号时自动推荐温度

型号-介质对照

型号 → 默认适用介质

新型号时自动推荐介质

注意:VBA 中的 确认口径()确认压力() 是两个自定义函数,定义在 function.vb 中。核心逻辑:

  1. CanShuSheet 的命名范围(口径替换数据表 / 型号压力转换表)查对照表

  2. 找到匹配 → 返回对照值;未找到 → 弹出 InputBox + 自动记录到对照表

  3. 新值自动写入对照表,下次同型号就能直接命中


第五部分:yaml 配置化重构方案

5.1 设计思路

VBA 中的对照表(口径替换、型号-压力、型号-标准号)和交互逻辑,非常适合用 yaml 配置化实现。参照项目已有的 config.yaml + ui.yaml 模式,新增 parameters.yaml

核心原则

  • 数据驱动:对照表数据存 yaml,不写死在 Python 代码中

  • 自学习:用户通过界面补充的新值,自动追加到 yaml(类似 VBA 的 CanShuSheet 自动记录)

  • 分层设计:配置层(对照表)+ 规则层(处理逻辑)+ UI 层(交互界面)

5.2 建议的 yaml 结构

# config/parameters.yaml
# ============================================================
# 打印参数配置 — 对照表 + 处理规则
# ============================================================

# ---- 英制判断规则 ----
imperial_rules:
  # 判断型号是否为英制的关键字
  keywords:
    - "LB"
    - "2500"      # 2500 本身也是 LB 级别
  # LB 等级提取(从型号字符串中匹配)
  lb_levels:
    - "150LB": 150
    - "300LB": 300
    - "600LB": 600
    - "800LB": 800
    - "900LB": 900
    - "1500LB": 1500
    - "2500LB": 2500
  # LB → MPa 转换(可选)
  lb_to_mpa:
    150: 2.0
    300: 5.0
    600: 10.0
    800: 13.0
    900: 15.0
    1500: 25.0
    2500: 42.0

# ---- DN/PN 单位清理规则 ----
unit_cleanup:
  dn:
    # 从口径字符串中移除的后缀
    strip_suffixes:
      - "MM"
      - "mm"
      - "mm"
  pn:
    strip_suffixes:
      - "MPA"
      - "MPa"
      - "mpa"

# ---- 口径替换对照表(VBA: 口径替换数据表)----
caliber_mapping:
  # 原始口径字符串 → 替换后的值
  # VBA CanShuSheet 命名范围 "口径替换数据表"
  mappings:
    "DN50": 50
    "DN65": 65
    "DN80": 80
    "DN100": 100
    "DN125": 125
    "DN150": 150
    "DN200": 200
    "DN250": 250
    "DN300": 300
    "DN350": 350
    "DN400": 400
    "DN450": 450
    "DN500": 500
    "DN600": 600
    "DN700": 700
    "DN800": 800
    "DN900": 900
    "DN1000": 1000
    "DN1200": 1200
    # 英制口径(从型号中提取的英寸值)
    '2"': 50
    '2-1/2"': 65
    '3"': 80
    '4"': 100
    '5"': 125
    '6"': 150
    '8"': 200
    '10"': 250
    '12"': 300
    '14"': 350
    '16"': 400
    '18"': 450
    '20"': 500
    '24"': 600
    '28"': 700
    '32"': 800
    '36"': 900
    '40"': 1000
    '48"': 1200
  # 特殊值
  special_values:
    - "-----"   # 表示不适用
    - "/"       # 表示无

# ---- 型号-压力对照表(VBA: 型号压力转换表)----
model_pressure_mapping:
  # 型号字符串 → 压力值
  # VBA CanShuSheet 命名范围 "型号压力转换表",列1=型号,列2=压力
  mappings:
    "D942X-10Q": "1.0MPa"
    "HH47X-25C": "2.5MPa"
    "VSSJA-2-10": "1.0MPa"
    "VSSJA-2-25": "2.5MPa"
    "Z41H-25C": "2.5MPa"
    "Z941H-25C": "2.5MPa"
    # ... 更多型号-压力对照
  # 英制压力特殊处理
  imperial_pressure_handling:
    # "150Lb" → 数值 2.0 (MPa)
    # "300Lb" → 数值 5.0 (MPa)
    # "600Lb" → 数值 10.0 (MPa)
    enabled: true

# ---- 型号-标准号对照表(VBA: 型号压力转换表 列3)----
model_standard_mapping:
  # 型号 → 试压标准号
  # VBA CanShuSheet 命名范围 "型号压力转换表",列1=型号,列3=标准号
  mappings:
    "Z41H-25C": "GB/T 13927-2008"
    "Z941H-25C": "GB/T 13927-2008"
    "D942X-10Q": "GB/T 13927-2008"
    # ... 更多型号-标准对照

# ---- 材质对照表 ----
material_mapping:
  # 阀体材质常见牌号
  body:
    - "WCB"
    - "CF8"
    - "CF8M"
    - "CF3"
    - "CF3M"
    - "LCB"
    - "WC6"
    - "WC9"
    - "HT200"
    - "QT450-10"
  # 阀杆材质
  stem:
    - "2Cr13"
    - "304"
    - "316"
    - "17-4PH"
    - "F53"
  # 启闭件材质
  disc:
    - "2Cr13"
    - "304"
    - "316"
    - "WCB+13Cr"
    - "CF8+STL"

# ---- 自动回填到 yaml 的配置(自学习)----
auto_learning:
  # 启用自动学习(用户补充的新值自动追加到对应映射表)
  enabled: true
  # 追加时是否去重
  deduplicate: true
  # 是否保留注释标记(标记为自动学习的条目)
  add_comment: true
  comment_marker: "# auto-learned"

5.3 与现有 yaml 的关系

config/
├── config.yaml          ← 主配置(include 其他 yaml)
│   !include ui.yaml
│   !include parameters.yaml    ← 新增
├── ui.yaml              ← UI 布局/样式
├── parameters.yaml      ← 新增:对照表 + 处理规则
├── columns.yaml         ← 列映射
├── query_fields.yaml    ← 查询字段
└── userconfig.yaml      ← 用户本地配置

5.4 Python 侧改造点

改造项

现状

yaml 配置化后

英制判断

imperial_rules.keywords

LB 等级提取

imperial_rules.lb_levels

DN 后缀清理

unit_cleanup.dn.strip_suffixes

PN 后缀清理

unit_cleanup.pn.strip_suffixes

口径转换

caliber_mapping.mappings,未命中弹出补充

型号→压力

model_pressure_mapping.mappings,未命中弹出补充

型号→标准号

model_standard_mapping.mappings,未命中弹出补充

材质选择

material_mapping 提供下拉选项

Certificate.pn 字段

不存在

模型需新增 pn 字段

C5/C6 英制列写入

不写

从对照表解析后写入 templates.xlsx

5.5 自学习机制

对标 VBA 的 CanShuSheet 自动记录行为:

用户打印新型号
  → 对照表查不到型号
  → PySide6 弹出参数补充对话框(替代 InputBox)
  → 用户填写口径/压力/标准号/材质
  → Python 自动追加到 parameters.yaml
  → 下次同型号直接命中,无需再次输入

5.6 实施优先级

优先级

配置项

原因

P0

imperial_rules + unit_cleanup

解决 DN/PN 最紧迫的处理差距

P0

Certificate 模型加 pn 字段

当前 PN 完全丢失

P1

caliber_mapping

口径替换对照,减少交互

P1

model_pressure_mapping

型号→压力对照,减少交互

P2

auto_learning

自学习机制,积累数据

P2

model_standard_mapping

型号→标准号对照

P3

material_mapping

材质下拉选项


第六部分:SQLite 配置表方案(综合 TODO)

6.1 为什么用 SQLite 代替 yaml 配置对照表?

第五部分的 yaml 方案适合小量静态配置(如英制规则、单位清理规则),但对于大量动态数据有明显缺陷:

维度

yaml 配置

SQLite 配置表

数据规模

适合几十条,上千条会臃肿

天然支持百万级

编辑方式

文本编辑器 / 程序追加

SQL 查询 + 未来可做 UI 编辑界面

查询效率

加载到内存遍历

SQL 索引查询,毫秒级

并发写入

需要文件锁

SQLite WAL 模式支持并发读

数据关系

扁平键值对

支持外键关联、多表 JOIN

模板规模

几千条模板不适合

✅ 建表 + 索引,秒查

材质牌号-化学成分

❌ 多对多关系无法表达

✅ 标准的多对多关联表

结论: 采用混合方案

  • yaml:英制规则、单位清理规则、LB 等级表(几十条)

  • SQLite:模板对照表、材质牌号对照表、化学成分表、口径映射表、自学习记录(几百~几千条)

6.2 新增 SQLite 配置表设计

以下 5 张新表均建在现有 certflow.db 中,利用现有的 DatabaseManager._migrate_missing_columns() 机制自动创建。

表 A: model_param_mappings — 型号参数对照表

对标 VBA CanShuSheet"型号压力转换表" 命名范围。

CREATE TABLE model_param_mappings (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    product_model   VARCHAR(200) NOT NULL,     -- 产品型号(如 Z41H-25C)
    pressure_value  VARCHAR(50),               -- 公称压力(如 2.5MPa / 150Lb)
    test_standard   VARCHAR(200),              -- 试压标准号(如 GB/T 13927-2008)
    working_temp    VARCHAR(50),               -- 工作温度(如 ≤425℃)
    working_medium  VARCHAR(100),              -- 适用介质(如水、蒸汽)
    source          VARCHAR(20) DEFAULT 'manual',  -- 来源: manual / auto-learned / import
    usage_count     INTEGER DEFAULT 0,         -- 使用次数(热门度)
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 索引:型号精确查询
CREATE UNIQUE INDEX idx_mapping_model ON model_param_mappings(product_model);

数据来源:

  1. 从 VBA CanShuSheet 的 "型号压力转换表" 导出(几百条现有数据)

  2. 从现有 templates.xlsx「数据库」表导入

  3. 运行时自学习追加

表 B: caliber_mappings — 口径字符串映射表

对标 VBA CanShuSheet"口径替换数据表" 命名范围。

CREATE TABLE caliber_mappings (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    raw_text        VARCHAR(50) NOT NULL UNIQUE, -- 原始口径字符串(如 DN100 / 2" / 2-1/2")
    caliber_value   VARCHAR(20) NOT NULL,         -- 标准化口径值(如 100 / 50 / 65)
    is_imperial     BOOLEAN DEFAULT FALSE,        -- 是否英制口径
    source          VARCHAR(20) DEFAULT 'manual',
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_caliber_raw ON caliber_mappings(raw_text);

表 C: material_grades + material_compositions — 材质牌号与化学成分对照表

这是用户特别关注的:材质牌号与化学成分的对应关系。现有系统完全没有这个数据。

-- 材质牌号主表
CREATE TABLE material_grades (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    grade_code      VARCHAR(50) NOT NULL UNIQUE,  -- 牌号(如 WCB / CF8 / 2Cr13 / 304)
    grade_name      VARCHAR(100),                  -- 中文名称(如 碳素钢铸件)
    category        VARCHAR(20) NOT NULL,          -- 分类: body(阀体) / stem(阀杆) / disc(启闭件) / common(通用)
    standard_ref    VARCHAR(100),                  -- 参考标准(如 ASTM A216 / GB/T 12229)
    is_active       BOOLEAN DEFAULT TRUE,
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_grade_code ON material_grades(grade_code);
CREATE INDEX idx_grade_category ON material_grades(category);

-- 化学成分表(每个牌号对应多条元素记录)
CREATE TABLE material_compositions (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    grade_code      VARCHAR(50) NOT NULL,          -- 关联 material_grades.grade_code
    element         VARCHAR(20) NOT NULL,          -- 元素符号(如 C / Mn / Si / Cr / Ni / Mo)
    min_value       FLOAT,                         -- 最小值(%)
    max_value       FLOAT,                         -- 最大值(%)
    is_single_value BOOLEAN DEFAULT FALSE,         -- TRUE 表示单一值而非范围
    display_order   INTEGER DEFAULT 0,             -- 显示排序
    FOREIGN KEY (grade_code) REFERENCES material_grades(grade_code)
);

CREATE INDEX idx_comp_grade ON material_compositions(grade_code);
CREATE INDEX idx_comp_element ON material_compositions(element);

示例数据:

-- 牌号
INSERT INTO material_grades VALUES (1, 'WCB', '碳素钢铸件', 'body', 'ASTM A216', TRUE, NOW());
INSERT INTO material_grades VALUES (2, 'CF8', '304不锈钢铸件', 'body', 'ASTM A351', TRUE, NOW());
INSERT INTO material_grades VALUES (3, '2Cr13', '马氏体不锈钢', 'stem', 'GB/T 1220', TRUE, NOW());

-- 化学成分(WCB)
INSERT INTO material_compositions VALUES (1, 'WCB', 'C', 0.18, 0.25, FALSE, 1);
INSERT INTO material_compositions VALUES (2, 'WCB', 'Mn', 0.50, 0.80, FALSE, 2);
INSERT INTO material_compositions VALUES (3, 'WCB', 'Si', 0.30, 0.60, FALSE, 3);
INSERT INTO material_compositions VALUES (4, 'WCB', 'P', NULL, 0.04, FALSE, 4);
INSERT INTO material_compositions VALUES (5, 'WCB', 'S', NULL, 0.045, FALSE, 5);

用途:

  1. 打印时材质报告自动填充化学成分

  2. 用户选择阀体/阀杆材质时提供下拉选项

  3. 未来可在 UI 中编辑、新增、修改牌号和化学成分

表 D: param_auto_learn_log — 自学习记录表

对标 VBA CanShuSheet 的自动追加机制,但用结构化方式记录。

CREATE TABLE param_auto_learn_log (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    table_name      VARCHAR(50) NOT NULL,          -- 目标表: model_param_mappings / caliber_mappings
    field_name      VARCHAR(50) NOT NULL,          -- 字段名: pressure_value / caliber_value
    lookup_key      VARCHAR(200) NOT NULL,         -- 查询键(型号 / 口径字符串)
    user_value      VARCHAR(200) NOT NULL,         -- 用户输入的值
    certificate_no  VARCHAR(50),                   -- 关联的合格证编号
    learned_at      DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_learn_table_key ON param_auto_learn_log(table_name, lookup_key);

用途: 追踪每次自学习的记录,便于审计和回滚。

6.3 SQLite 模型定义位置

src/certflow/models/
├── sale_plan.py              ← 现有
├── certificate.py            ← 现有
├── print_log.py              ← 现有
├── auto_number.py            ← 现有
├── report_template.py        ← 现有
├── sale_plan_change.py       ← 现有
├── model_param_mapping.py    ← 新增:型号参数对照表
├── caliber_mapping.py        ← 新增:口径映射表
├── material_grade.py         ← 新增:材质牌号表
├── material_composition.py   ← 新增:化学成分表
└── param_auto_learn_log.py   ← 新增:自学习记录表

6.4 Python 服务层设计

# src/certflow/services/param_lookup_service.py

class ParamLookupService:
    """参数查找服务 — 对标 VBA 确认口径() / 确认压力() / 确认标准号()"""

    def lookup_caliber(self, raw_text: str) -> str | None:
        """从 caliber_mappings 表查找口径值"""
        ...

    def lookup_model_params(self, model: str) -> dict | None:
        """从 model_param_mappings 表查找型号的压力/标准号/温度/介质"""
        ...

    def save_or_update_mapping(self, table, lookup_key, field, value):
        """自学习:保存用户补充的新值到对应表"""
        ...

    def get_material_grades(self, category: str = None) -> list:
        """获取材质牌号列表(可按阀体/阀杆/启闭件分类)"""
        ...

    def get_material_composition(self, grade_code: str) -> list:
        """获取指定牌号的化学成分"""
        ...

6.5 交互流程

对标 VBA 的 InputBox 交互,Python 使用 PySide6 对话框:

打印服务调用 ParamLookupService.lookup_model_params("Z41H-25C")
  ├─ 命中 → 直接返回 {pressure: "2.5MPa", standard: "GB/T 13927-2008", temp: "≤425℃"}
  └─ 未命中 → 弹出参数补充对话框
      ├─ 预填: 型号 Z41H-25C, DN 从模板提取, 温度/介质从模板提取
      ├─ 用户补充: 公称压力 [2.5MPa ▼] 标准号 [GB/T 13927-2008 ▼]
      │             阀体材质 [WCB ▼] 阀杆材质 [2Cr13 ▼] 启闭件材质 [2Cr13 ▼]
      │             (下拉选项从 material_grades 表读取)
      ├─ 确认 → 自动写入 model_param_mappings + param_auto_learn_log
      └─ 下次同型号直接命中

6.6 与现有数据库的关系

certflow.db (现有 3.29 MB)
├── sale_plans              ← 现有 (53 字段)
├── certificates            ← 现有 (47 字段)
├── print_logs              ← 现有 (54 字段)
├── auto_number_rules       ← 现有
├── auto_number_counters    ← 现有
├── report_templates        ← 现有
├── sale_plan_changes       ← 现有
├── model_param_mappings    ← 🆕 新增 (型号参数对照)
├── caliber_mappings        ← 🆕 新增 (口径映射)
├── material_grades         ← 🆕 新增 (材质牌号)
├── material_compositions   ← 🆕 新增 (化学成分)
└── param_auto_learn_log    ← 🆕 新增 (自学习记录)

6.7 数据初始化策略

数据源

导入方式

预估条数

VBA CanShuSheet "型号压力转换表"

model_param_mappings

从 xlsx 导出 CSV → SQLite INSERT

~200-500

VBA CanShuSheet "口径替换数据表"

caliber_mappings

从 xlsx 导出 CSV → SQLite INSERT

~50-100

标准材质牌号数据库

material_grades + material_compositions

编写 seed 脚本批量导入

~50-100

运行时自学习

param_auto_learn_log

用户操作自动追加

逐步增长

6.8 yaml vs SQLite 分工(最终方案)

config/
├── config.yaml              ← 主配置
├── ui.yaml                  ← UI 布局/样式
├── imperial_rules.yaml      ← 🆕 英制规则(几十条,不常变)
│   ├── lb_keywords: [LB, 2500]
│   ├── lb_levels: {150LB: 150, 300LB: 300, ...}
│   ├── lb_to_mpa: {150: 2.0, 300: 5.0, ...}
│   └── unit_cleanup: {dn: [MM], pn: [MPA, Lb]}
├── columns.yaml             ← 列映射
├── query_fields.yaml        ← 查询字段
└── userconfig.yaml          ← 用户本地配置

certflow.db (SQLite)
├── model_param_mappings     ← 型号参数对照(几百条,频繁查询/更新)
├── caliber_mappings         ← 口径映射(几十条)
├── material_grades          ← 材质牌号(几十条)
├── material_compositions    ← 化学成分(几百条,多对多)
└── param_auto_learn_log     ← 自学习记录(逐步增长)

6.9 未来 UI 编辑界面(展望)

SQLite 配置表的一个重要优势是可以通过 UI 界面编辑,而无需手动写 SQL:

┌─────────────────────────────────────────────────────┐
│  系统设置 → 参数对照表管理                           │
│                                                      │
│  ┌─ 标签页 ───────────────────────────────────────┐ │
│  │ [型号参数] [口径映射] [材质牌号] [化学成分]     │ │
│  ├────────────────────────────────────────────────┤ │
│  │ 型号          │ 压力    │ 标准号        │ 操作  │ │
│  │ Z41H-25C      │ 2.5MPa  │ GB/T 13927   │ 编辑  │ │
│  │ Z941H-25C     │ 2.5MPa  │ GB/T 13927   │ 编辑  │ │
│  │ D942X-10Q     │ 1.0MPa  │ GB/T 13927   │ 编辑  │ │
│  │ ...           │ ...     │ ...          │ ...   │ │
│  ├────────────────────────────────────────────────┤ │
│  │ [+ 新增]  [导入 CSV]  [导出 CSV]  [批量删除]   │ │
│  └────────────────────────────────────────────────┘ │
│                                                      │
│  ┌─ 材质化学成分编辑弹窗 ──────────────────────────┐│
│  │ 牌号: WCB   分类: 阀体   标准: ASTM A216        ││
│  │                                                 ││
│  │ 元素   最小值(%)   最大值(%)                    ││
│  │ C      0.18       0.25        [删除]           ││
│  │ Mn     0.50       0.80        [删除]           ││
│  │ Si     0.30       0.60        [删除]           ││
│  │ P      -          0.04        [删除]           ││
│  │ S      -          0.045       [删除]           ││
│  │ [+ 添加元素]                                   ││
│  │                                                 ││
│  │ [保存]  [取消]                                  ││
│  └─────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────┘

6.10 实施 TODO 清单

#

任务

优先级

依赖

预估工时

1

创建 5 个 SQLAlchemy 模型文件

P0

2h

2

创建 param_lookup_service.py

P0

任务 1

4h

3

从 VBA xlsx 导出并导入 model_param_mappings

P0

任务 1

2h

4

从 VBA xlsx 导出并导入 caliber_mappings

P0

任务 1

1h

5

编写材质牌号 seed 脚本(50+ 牌号 + 化学成分)

P1

任务 1

4h

6

Certificate 模型添加 pn 字段

P0

0.5h

7

打印服务集成 ParamLookupService

P0

任务 2

4h

8

PySide6 参数补充对话框(替代 VBA InputBox)

P0

任务 2

6h

9

实现自学习自动写入机制

P1

任务 2

3h

10

imperial_rules.yaml 创建 + 英制处理逻辑

P0

3h

11

UI 参数对照表管理界面

P2

任务 1

8h

12

材质报告自动填充化学成分

P2

任务 5

4h

13

C5/C6 英制列回填到 xlsx「数据库」表

P2

任务 10

3h

总预估: P0(核心)= 22h, P1 = 7h, P2 = 15h, 合计约 44 小时


第三部分:整体 VBA → Python 迁移状态

VBA 完整打印流程

用户操作 → 查找活动的工作表并指定相关参数
         ↓
    批量打印流水合格证 (主流程, ~1800 行)
         ↓ 每次打印后
    ┌────┼────┬──────────────────────┐
    ↓    ↓    ↓                      ↓
  GDI   ①保存到  ②保存到"数据库"   ③保存到 Access
  打印   "打印历史" (xlsx 内部表)    SignDB.mdb
         ↓                         (铭牌刻印数据源)
    ④保存到"合格证_2026"清单
         ↓
    取消筛选 + 静默保存工作簿

Python 当前迁移覆盖度

VBA 子程序

VBA 行号

Python 对应

覆盖度

查找活动工作表

77-94

❌ 不需要(PySide6 UI 替代)

批量打印流水合格证

309-2150

CertificatePrintService.batch_print

⚠️ 60% — GDI 打印 + 编号展开已实现

① 保存到"打印历史"

2754-2859

record_print_log → SQLite print_logs

可废弃 — SQLite print_logs 已替代

② 保存到"数据库"

2561-2724

writeback_template_to_xlsx → templates.xlsx「数据库」

⚠️ 值不完整 — DN/PN 处理粗糙,缺 C5/C6 英制列

③ 保存到 Access SignTb

2211-2559

record_print_log → SQLite print_logs (51字段)

⚠️ 85% — 字段兼容但铭牌系统仍读 Access

④ 保存到合格证清单

2985-3125

0% — 需先确认字段映射

VBA 交互式参数补充

~40处 InputBox

0% — 完全缺失,见第四部分

Access 导出方案(铭牌刻印数据源)

现状: 铭牌刻印系统目前读取的是 32 位 Access 2010SignDB.mdb SignTb 表。Python 64 位环境下 pyodbc 无法连接 32 位 Access ODBC 驱动。

两种可行方案:

方案

描述

优点

缺点

A. SQLite 导出到 Access

打印后从 SQLite print_logs 导出为 .mdb/.accdb 文件

铭牌系统无需改动

需要 32 位 Access 引擎(可用独立 32 位 Python 进程或 Access COM 自动化)

B. SQLite 链表到 Access

在 Access 2010 中创建 ODBC 链接表指向 SQLite print_logs

数据实时同步,无需导出

需要 SQLite ODBC 驱动 + Access 中配置链接表

当前代码状态:

  • export_to_access 方法已标注为废弃certificate_print_service.py 第 244 行),内部尝试 pyodbc 但 64 位无法连接

  • certflow export-access CLI 命令存在但底层已废弃

  • pyproject.toml pyodbc 依赖(仅在 try-except 中延迟导入)

建议: 方案 B(链表)更优,避免每次打印后手动导出。需要安装 SQLite ODBC 驱动并在 Access 中配置。如果不可行,可用方案 A 通过 32 位 Python 子进程或 COM 自动化写入 .mdb。

未实现的关键功能

#

功能

重要性

说明

1

交互式参数补充

🔴 高

VBA 有 ~40 处 InputBox,新型号/未知参数时让用户补充口径/压力/温度/介质/材质。Python 完全缺失

2

PN 公称压力字段

🔴 高

Certificate 模型缺少 pn 字段,所有 PN 输出硬编码为空字符串。

3

DN/PN 英制处理

🔴 高

没有 LB 判断、LB 等级提取、C5/C6 回退、单位后缀清理(MM/MPA/Lb)。

4

Access 导出/链表

🔴 高

铭牌刻印系统依赖 Access SignTb,必须解决 64/32 位兼容问题。

5

材质字段

🟡 中

VBA InputBox 填写的阀体/阀杆/启闭件材质,Python 完全缺失。

6

SN/KKS 互换逻辑

🟡 中

编码模式 5/6 下 KKS/SN 位置互换,当前 Python 未处理。

7

参数对照表

🟡 中

VBA 的 确认口径()/确认压力() 函数功能需用对照表替代(型号-压力、口径-字符串、材质-元素)。

8

PrinterName 整数代码

🟢 低

VBA 存整数代码(0-7),Python 存字符串,铭牌系统可能依赖。

9

编号区间记录

🟢 低

VBA 写入范围(如 V2506121-140),Python print_logs 存逐台 SN。

迁移优先级建议

  1. P0 Certificate 模型修复: 添加 pn 字段(公称压力),确保模板的 PN 值能写入 Certificate

  2. P0 DN/PN 处理: 实现 LB 英制判断、等级提取、C5/C6 回退、单位后缀清理逻辑

  3. P0 交互式参数补充: 在 PySide6 打印对话框中增加参数确认/补充界面(口径/压力/温度/介质/材质)

  4. P1 Access 数据源: 确定方案 A(导出)或 B(链表),确保铭牌系统能读到打印数据

  5. P1 参数对照表: 建立型号-压力、口径-字符串、材质-元素对照表,减少手动输入

  6. P2 SN/KKS 修复: 编码模式 5/6 下的互换逻辑 + KKS 12 位截断

  7. P2 ② xlsx 回填完善: 写入 C5/C6 英制列到 templates.xlsx「数据库」表

  8. P3 ④ 合格证清单回填: 如果确认 Certificate 表已完整记录所有字段,则可省略


文档版本

  • 创建日期: 2026-06-06

  • 最后更新: 2026-06-06(v7:P0 实施 — DNService + ParamSupplementDialog + 打印流程集成)

  • VBA 源版本: data/print.vb(3235 行)

  • Python 版本: v2.0 重构后

本次修改记录 (v7)

#

文件

变更

1

services/dn_service.py

🆕 新建 — 对标 VBA 确认口径() 的完整 DN 处理服务

2

views/param_supplement_dialog.py

🆕 新建 — 对标 VBA InputBox 的 PySide6 参数补充对话框

3

models/caliber_mapping.py

🆕 新建 — 口径映射表 SQLAlchemy 模型

4

services/certificate_print_service.py

集成 DNService + 参数补充流程 + check_and_supplement_params()

5

views/query_view.py

_print_certificates 集成参数补充对话框

6

models/__init__.py

导出 CaliberMapping

7

views/__init__.py

导出 ParamSupplementDialog

本次修改记录 (v6)

#

文件

变更

1

models/certificate.py

添加 pn_value / pn_display / pn_unit 三个字段

2

services/pn_service.py

🆕 新建 — 对标 VBA 确认压力() 的完整 PN 处理服务

3

services/certificate_print_service.py

集成 PNService,6 处硬编码 pn="" 改为从 Certificate 读取

4

models/model_param_mapping.py

🆕 新建 — 型号参数对照表模型

5

models/param_auto_learn_log.py

🆕 新建 — 自学习记录表模型

6

models/__init__.py

导出新模型

7

config/imperial_rules.yaml

🆕 新建 — 英制规则配置

关键结论

VBA 子程序

处理策略

① 保存到"打印历史"

可废弃 — SQLite print_logs 已替代

② 保存到"数据库"

⚠️ 需完善 — DN/PN 处理粗糙,需补充 C5/C6 英制列 + 单位清理

③ 保存到 Access SignTb

⚠️ 需解决 — 字段兼容(85%),但需导出/链表到 Access 供铭牌系统读取

④ 保存到合格证清单

待确认 — 如果 Certificate 表完整记录了打印参数,可能不需要额外回填

VBA InputBox 交互

完全缺失 — ~40 处交互式参数补充在 Python 中完全未实现

VBA CanShuSheet 参数表

🆕 SQLite 化 — 用 5 张配置表替代 Excel 命名范围,支持 UI 编辑

配置方案最终选型

数据类型

存储方式

原因

英制规则 / 单位清理

yaml (imperial_rules.yaml)

少量静态规则,几十条

型号-压力-标准对照

SQLite (model_param_mappings)

几百条,频繁查询/更新

口径字符串映射

SQLite (caliber_mappings)

几十条,自学习增长

材质牌号 + 化学成分

SQLite (material_grades + material_compositions)

多对多关系,需 UI 编辑

核心数据流(已确认)

SalePlan 导入
  ├─ product_name / product_model / product_spec  →  Certificate
  ├─ customer / project_name / test_standard      →  Certificate
  └─ quantity / plan_date / production_order_no   →  Certificate

模板匹配 (templates.xlsx「数据库」表)
  ├─ dn            →  Certificate.product_spec     (覆盖)
  ├─ pn            →  Certificate.pn               (❌ 丢失!Certificate 无 pn 字段)
  ├─ temperature   →  Certificate.working_temp     (✅)
  ├─ medium        →  Certificate.working_medium   (✅)
  ├─ check_standard → Certificate.test_standard    (可能覆盖 SalePlan 值)
  ├─ inspector_id  →  Certificate.inspector_id     (❌ 丢失!Certificate 无此字段)
  └─ manufacture_date → Certificate.issue_date     (✅)

打印后写入 SQLite print_logs
  ├─ 51 个兼容字段(对应 Access SignTb)
  ├─ DN/PN 未做英制处理(LB 判断/单位清理缺失)
  └─ 阀体/阀杆/启闭件材质未写入(VBA 靠 InputBox 填写)