- 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/
132 lines
5.5 KiB
Python
132 lines
5.5 KiB
Python
"""签收单 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()
|