Files
WareShipManifest/reports/sign_receipt/add_stress_rows.py
Misaka_Company df8f25f6da 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/
2026-08-04 08:55:09 +08:00

132 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""签收单 20260803001 压力测试数据。
目的:观察当前「单页绝对定位」模板在【内容 > 1 页】时的表现:
* 明细行 > MAX_ROWS(20) 时,超出部分被静默截断(合计仍按全量计)。
* 即便在 20 行内,若累计高度超过内容区,行会越过页脚、与签收栏重叠。
* 发货/收货地址为固定高度(12mm)文本,加长后会换行并压到下一行(电话)上。
可重复运行STRESS_BOXES 为显式列表,运行前先 DELETE 这些箱,再 INSERT
地址直接 UPDATE幂等
运行:
.venv/Scripts/python.exe reports/sign_receipt/add_stress_rows.py
注意:本脚本只动 20260803001重新跑 setup_db.py 会清空 202608030% 测试
数据(含本脚本追加内容),恢复干净状态。
"""
from __future__ import annotations
import sys
from pathlib import Path
# 让脚本能 import core.*
PROJECT_ROOT = Path(__file__).resolve().parents[2]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from core.db import get_engine
from sqlalchemy import text
RECEIPT_NO = "20260803001"
# 显式选取一批真实箱(来自 finished_goods_box单箱明细行数较多确保 > 20 行)。
# 格式:(paichan_no, box_no)
STRESS_BOXES = [
("R07200", 1), ("R06994", 1), ("R06994", 2),
("R06626", 1), ("R06687", 1), ("R06884", 1),
("R05541", 1), ("R06006", 87),
]
LONG_SHIPPER_ADDR = (
"北京市朝阳区南三环成寿寺路甲135号院1号楼 北京布莱迪仪器仪表有限公司 "
"仓储物流中心收发室近地铁成寿寺站B口工作日8:30-17:30可收货"
"节假日及夜间到货请提前与仓管预约货车限高3.2米,卸货区在厂区西北角)"
)
LONG_RECEIVER_ADDR = (
"上海市浦东新区张江高科技园区博云路2号三期厂房B栋5层收货平台 "
"收货联系人王女士电话15230028484请走北门货运通道"
"货车需登记后入厂卸货位B1-07到货前1小时请短信通知"
)
# 静态 VALUES 列表STRESS_BOXES 为硬编码常量,无注入风险)
VALUES_SQL = ", ".join(f"(:p{i}, :b{i})" for i in range(len(STRESS_BOXES)))
PAIR_VALS = {f"p{i}": p for i, (p, _) in enumerate(STRESS_BOXES)} | \
{f"b{i}": b for i, (_, b) in enumerate(STRESS_BOXES)}
def main() -> None:
eng = get_engine()
with eng.begin() as conn:
# 1) 清掉上一轮追加的箱VALUES + EXISTS兼容 SQL Server
conn.execute(
text(
f"DELETE FROM warehouseOutbound.delivery_receipt_item "
f"WHERE receipt_no = :r AND EXISTS ("
f" SELECT 1 FROM (VALUES {VALUES_SQL}) v(pa, bo) "
f" WHERE v.pa = paichan_no AND v.bo = box_no)"
),
{**PAIR_VALS, "r": RECEIPT_NO},
)
# 2) 仅当箱真实存在才插入
exist = {
(p, b) for (p, b) in conn.execute(
text(
f"SELECT v.pa, v.bo FROM (VALUES {VALUES_SQL}) v(pa, bo) "
f"WHERE EXISTS (SELECT 1 FROM CargoTrace.finished_goods_box b "
f" WHERE b.paichan_no = v.pa AND b.box_no = v.bo)"
),
PAIR_VALS,
).fetchall()
}
missing = [pb for pb in STRESS_BOXES if pb not in exist]
if missing:
print(" 警告:以下箱在 finished_goods_box 不存在,已跳过:", missing)
for (p, b) in STRESS_BOXES:
if (p, b) in exist:
conn.execute(
text(
"INSERT INTO warehouseOutbound.delivery_receipt_item "
"(receipt_no, paichan_no, box_no) VALUES (:r, :p, :b)"
),
{"r": RECEIPT_NO, "p": p, "b": b},
)
# 3) 拉长地址(幂等 UPDATE
conn.execute(
text(
"UPDATE warehouseOutbound.delivery_receipt "
"SET shipper_address = :sa, receiver_address = :ra "
"WHERE receipt_no = :r"
),
{"sa": LONG_SHIPPER_ADDR, "ra": LONG_RECEIVER_ADDR, "r": RECEIPT_NO},
)
# 校验
with eng.connect() as conn:
n = conn.execute(
text("SELECT COUNT(*) FROM warehouseOutbound.delivery_receipt_item "
"WHERE receipt_no = :r"), {"r": RECEIPT_NO}).scalar()
rows = conn.execute(
text(
"SELECT COUNT(*) FROM ("
"SELECT bi.id FROM warehouseOutbound.delivery_receipt_item di "
"JOIN CargoTrace.finished_goods_box b ON b.paichan_no=di.paichan_no AND b.box_no=di.box_no "
"JOIN CargoTrace.finished_goods_box_item bi ON bi.box_id=b.id "
"WHERE di.receipt_no = :r) t"
), {"r": RECEIPT_NO}).scalar()
sa = conn.execute(
text("SELECT LEN(shipper_address) FROM warehouseOutbound.delivery_receipt "
"WHERE receipt_no=:r"), {"r": RECEIPT_NO}).scalar()
ra = conn.execute(
text("SELECT LEN(receiver_address) FROM warehouseOutbound.delivery_receipt "
"WHERE receipt_no=:r"), {"r": RECEIPT_NO}).scalar()
print(f" {RECEIPT_NO} 明细箱数={n},预计明细行数(产品粒度)={rows}")
print(f" 发货地址长度={sa}字符,收货地址长度={ra}字符")
print(f" MAX_ROWS=20 → 超出 {max(0, rows - 20)} 行将被截断(合计仍按 {rows} 行计)。")
if __name__ == "__main__":
main()