refactor(db_compare): 差缺统计改为应到驱动,以出库日为批次归属日
将未到统计从实到驱动(实到锚点反推批次)改为应到驱动(batch_out_date 归属日直接取应到批次),以吸收应到任务提前 1~2 天提交的扰动(韵达固定 +1、中通偶发 +1)。 - expected_record 新增 out_date / batch_out_date + 索引;入库解析出库时间并聚合批次归属日,支持历史回填 - 新增 compare_site_outdate 应到驱动入口,保留 compare_site_date 实到驱动作对照 - 未到任务 / POST /compare / 全站汇总切换到应到驱动 - 附现状梳理、设计、实现总结三篇文档
This commit is contained in:
@@ -224,6 +224,83 @@ def compare_site_date(site: str, target_date: str) -> CompareResult | None:
|
||||
return None
|
||||
|
||||
|
||||
def compare_site_outdate(site: str, target_date: str) -> CompareResult | None:
|
||||
"""应到驱动差缺比对:以「批次归属日(batch_out_date)」为准取应到。
|
||||
|
||||
与 compare_site_date(实到驱动)区别:
|
||||
1. 应到来源 = expected_record WHERE batch_out_date = target_date
|
||||
2. 不再依赖实到锚点反推;应到空时返回 None(明确"当日无应到")
|
||||
3. 提前提交的批次按其出库日归属,自动归入正确日期
|
||||
|
||||
Args:
|
||||
site: 站点名("顺心"/"中通"/"韵达"/"安能")
|
||||
target_date: 目标业务日期 "YYYY-MM-DD"
|
||||
|
||||
Returns:
|
||||
CompareResult 或 None(当日无应到批次)
|
||||
"""
|
||||
cfg = SITE_COMPARE_CONFIG.get(site)
|
||||
if cfg is None:
|
||||
print(f"[db_compare] 不支持的站点: {site}")
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = _connect()
|
||||
cur = conn.cursor()
|
||||
|
||||
# ── Step 1: 取目标日应到批次(按批次归属日)──
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT DISTINCT handover_no FROM expected_record
|
||||
WHERE site = %s AND batch_out_date = %s
|
||||
ORDER BY handover_no
|
||||
""",
|
||||
(site, target_date),
|
||||
)
|
||||
batches = [r[0] for r in cur.fetchall()]
|
||||
if not batches:
|
||||
print(f"[db_compare] {site} {target_date}: 当日无应到批次")
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
# ── Step 2: 展开批次全量应到 ──
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT waybill_no, handover_no, handover_pieces
|
||||
FROM expected_record
|
||||
WHERE site = %s AND handover_no = ANY(%s)
|
||||
ORDER BY handover_no, waybill_no
|
||||
""",
|
||||
(site, batches),
|
||||
)
|
||||
exp_rows = cur.fetchall()
|
||||
if not exp_rows:
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
all_wbs = [r[0] for r in exp_rows]
|
||||
|
||||
# ── Step 3: 取批次全量实到 ──
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT waybill_no, piece_no FROM actual_record
|
||||
WHERE site = %s AND waybill_no = ANY(%s)
|
||||
ORDER BY waybill_no, piece_no
|
||||
""",
|
||||
(site, all_wbs),
|
||||
)
|
||||
act_rows = cur.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
# ── Step 4: 逐运单比对 ──
|
||||
return _do_compare(site, target_date, batches, exp_rows, act_rows, cfg)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[db_compare] {site} {target_date} 应到驱动比对异常: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def compare_site_batch(site: str, handover_no: str) -> CompareResult | None:
|
||||
"""按指定交接单号执行全批次比对(不依赖实到锚点)。
|
||||
|
||||
@@ -499,11 +576,9 @@ def _stats_to_dict(s: CompareStats) -> dict:
|
||||
|
||||
|
||||
def _target_date_for(site: str) -> str:
|
||||
"""4 站比对锚点:today - actual_offset(以实到扫描日为锚,与 _site_undelivered_handler 一致)。"""
|
||||
from inbound_verify import state_store # 懒导入,避免成环
|
||||
|
||||
offset = state_store.get_offset(site, "actual")
|
||||
return (date.today() - timedelta(days=offset)).strftime("%Y-%m-%d")
|
||||
"""4 站比对锚点(应到驱动):批次归属日默认取今天。
|
||||
各站统一以出库日(batch_out_date)为准,不再依赖站点偏移配置。"""
|
||||
return date.today().strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
def _baishi_from_pg(cur, target: str):
|
||||
@@ -555,7 +630,7 @@ def _baishi_from_pg(cur, target: str):
|
||||
|
||||
def build_full_report(date=None) -> str:
|
||||
"""DB 版全站汇总报表:4 站走 DB 比对、百世走 PG,复用 compare.build_summary 渲染。
|
||||
产出 output/应到未到数据.xlsx(/report 下载)。date=None 时各站按 actual_offset 算锚点(以实到扫描日为锚)。
|
||||
产出 output/应到未到数据.xlsx(/report 下载)。date=None 时各站取今天为批次归属锚点(应到驱动)。
|
||||
返回输出路径。"""
|
||||
from inbound_verify import compare # 复用 build_summary / write_station / OUTFILE
|
||||
|
||||
@@ -584,7 +659,7 @@ def build_full_report(date=None) -> str:
|
||||
continue
|
||||
target = date or _target_date_for(name)
|
||||
site_targets[name] = target
|
||||
result = compare_site_date(name, target)
|
||||
result = compare_site_outdate(name, target)
|
||||
if result is not None:
|
||||
results.append((name, _stats_to_dict(result.stats)))
|
||||
_write_sheet(wb.create_sheet(name), result)
|
||||
|
||||
Reference in New Issue
Block a user