Add sign-off receipt report and refine packing list: single-line info bar, real-glyph row height, auto-paginated detail table

- Packing list: info bar changed to a single-line three-column layout (schedule no. | order no. | box no.); packing date moved to bottom-right on the same row as the total; date separator changed to slash
- Packing list: range column widened 20->30mm; quantity/position-no./remark start shifted right 10mm; remark column narrowed to compensate; total width unchanged
- Packing list: detail cells now use line_spacing=1.0 + valign=top; separators changed to thin solid lines at row bottom (+1mm line spacing); placeholder empty rows no longer step y
- Packing list: row height estimation now uses fpdf2's same simhei glyph measured width (replacing rough character-count estimate); _clean adds control-character cleanup
- New sign-off receipt report reports/sign_receipt: master+detail linked query, ReportBro auto-paginated table, PyMuPDF stamped footer (document no. / page no. / sign-off column)
- run.py: required params now read from each report's config.yaml; default output path auto-generated; added transform.stamp_footer post-processing hook
- .gitignore: ignore .workbuddy/
This commit is contained in:
Misaka_Company
2026-08-04 08:55:09 +08:00
parent 22b764ef19
commit df8f25f6da
11 changed files with 1503 additions and 70 deletions

View File

@@ -0,0 +1,98 @@
"""验证明细行「整行不跨页」:构造长名称明细,逐页检查每条记录是否完整落在同一页。
判定方法:每条记录的产品名称/型号中嵌入唯一 ASCII 标记——
产品名称:开头 "S{nn}" + 长文本 + 结尾 "E{nn}"
型号: 开头 "M{nn}" + 长文本 + 结尾 "N{nn}"
同一记录的开头标记与结尾标记必然落在不同的折行上。
若整行被跨页拆开,则开头标记在某页、结尾标记在另一页 → 被抓到。
ASCII 字形在 simhei 中存在,避免生僻字缺字形问题。)
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
from reportbro import Report # noqa: E402
from core.fonts import additional_fonts # noqa: E402
import fitz # noqa: E402
import reports.sign_receipt._build_template as T # noqa: E402
LONG = "(长文本测试内容用于触发产品名称折行显示效果以验证分页时整行保持完整不被拆开到两页之间)"
N = 28
items = []
for i in range(1, N + 1):
nn = f"{i:02d}"
pname = f"S{nn}产品名称{LONG}E{nn}"
model = f"M{nn}型号标识{LONG}N{nn}"
items.append({
"seq": str(i),
"product_name": pname,
"model": model,
"range_": "0~100MPa",
"qty": "12",
"paichan_order": f"R{nn}/ORD{nn}",
"box_no": f"B{nn}",
})
ctx = {
"receipt_no": "TESTKEEPTOGETHER",
"seq": 1,
"receipt_date": "2026-08-03",
"shipper_name": "发货单位甲",
"shipper_address": "北京市朝阳区建国路88号",
"shipper_phone": "010-88886666",
"receiver_name": "收货人乙",
"receiver_address": "上海市浦东新区张江路99号",
"receiver_phone": "021-66668888",
"customer_order_no": "CUST-001",
"customer_total_no": "TOTAL-001",
"transport_mode": "公路运输",
"ship_date": "2026-08-03",
"sign_date": "2026-08-03",
"signed_by": "收货人",
"maker": "王制单",
"receipt_remark": "备注测试",
"total_qty": N * 12,
"total_boxes": N,
"items": items,
}
report_def = T.build_report_definition(ctx)
report = Report(report_definition=report_def, data=ctx, additional_fonts=additional_fonts())
if report.errors:
print("❌ 模板渲染错误:", report.errors)
sys.exit(1)
out = PROJECT_ROOT / "out" / "verify_keep_together.pdf"
report.generate_pdf(str(out))
print(f"✅ 已生成:{out}")
doc = fitz.open(str(out))
pages_text = [" ".join(w[4] for w in pg.get_text("words")) for pg in doc]
problems = []
for i in range(1, N + 1):
nn = f"{i:02d}"
s, e = f"S{nn}", f"E{nn}" # 产品名称 头/尾标记
m, n = f"M{nn}", f"N{nn}" # 型号 头/尾标记
for a, b, label in [(s, e, "产品名称"), (m, n, "型号")]:
pa = [p + 1 for p, t in enumerate(pages_text) if a in t]
pb = [p + 1 for p, t in enumerate(pages_text) if b in t]
if set(pa) != set(pb):
problems.append((i, nn, label, f"{a}页={pa}{b}页={pb}"))
print(f"总页数:{doc.page_count},明细 {N}")
if problems:
print("❌ 发现跨页拆行的记录:")
for p in problems:
print(" ", p)
doc.close()
sys.exit(1)
else:
print(f"✅ 所有 {N} 条记录的「头/尾标记」均共页,无跨页拆行。"
f"(产品名称、型号的长文本折行均随整行一起移动)")
doc.close()