diff --git a/expected_undelivered.py b/expected_undelivered.py index e241d05..7c36ebd 100644 --- a/expected_undelivered.py +++ b/expected_undelivered.py @@ -4,7 +4,7 @@ 目的:对中通 / 顺心 / 韵达 / 安能 四个站点,比对各自的「应到货物数据」与 「实到货物数据」,找出应到却未到的运单,汇总到 output/应到未到数据.xlsx。 -(百世为站点直供未到明细,不参与本模块比对,见 process_baishi。) +(百世为站点直供未到明细,不参与 4 站比对;其应到/实到基数取自「扫描综合查询」应扫/已扫,见 process_baishi。) 核心口径(四站点统一,重构后): 1. 应到件数 = 应到表「交接件数」之和(按运单号去重 keep-first)。 @@ -297,10 +297,26 @@ def process_baishi(): df = pd.read_excel(path, dtype=str).fillna("") rows = df.to_dict("records") wb_count = df["运单号"].nunique() if "运单号" in df.columns else len(rows) + + # 应到/实到基数取自「扫描综合查询」应扫/已扫(到/接件扫描→当日), + # 由 baishi_download_undelivered_data_impl 在同次导航里抓取并落 site_settings。 + # 未抓取过则 get_setting 返回 "" → 视为无基数(报表显示「—」)。 + import state_store # 与 _read_business_dates 一致:比对模块纯离线,懒加载 + + def _to_int(v): + v = (v or "").strip().replace(",", "") + try: + return int(float(v)) if v not in ("", "-") else None + except (TypeError, ValueError): + return None + + exp_n = _to_int(state_store.get_setting("百世", "scan_expected_pieces")) + arr_n = _to_int(state_store.get_setting("百世", "scan_arrived_pieces")) + stats = { "运单数": wb_count, - "应到件": None, - "已到件": None, + "应到件": exp_n, + "已到件": arr_n, "未到件": len(rows), "涉及运单": wb_count, "完全未到": None, @@ -515,7 +531,13 @@ def build_summary(ws, results, generated_at, dates=None): if s is None: vals = [f"{name}(无数据)", 0, 0, 0, 0, 0, 0, 0] elif is_baishi: - vals = [name, s["运单数"], "—", "—", s["未到件"], "—", "—", "—"] + # 百世:未到明细已知;若已抓取应到/实到基数(扫描综合查询应扫/已扫)则填真实值 + if s["应到件"] is not None and s["已到件"] is not None: + srate = (s["未到件"] / s["应到件"]) if s["应到件"] else 0 + vals = [name, s["运单数"], s["应到件"], s["已到件"], + s["未到件"], srate, "—", "—"] + else: + vals = [name, s["运单数"], "—", "—", s["未到件"], "—", "—", "—"] else: srate = (s["未到件"] / s["应到件"]) if s["应到件"] else 0 vals = [ @@ -542,7 +564,7 @@ def build_summary(ws, results, generated_at, dates=None): cell.fill = PatternFill("solid", fgColor=ZEBRA) if isinstance(v, (int, float)): cell.number_format = "0.0%" if i == 5 else "#,##0" - if i == 5 and s is not None and not is_baishi: + if i == 5 and s is not None and isinstance(v, (int, float)) and not isinstance(v, bool): cell.fill = PatternFill("solid", fgColor=heat(srate)) ws.row_dimensions[r].height = 19 r += 1 @@ -594,7 +616,7 @@ def build_summary(ws, results, generated_at, dates=None): note_row = chart_anchor + 19 notes = [ "指标口径:未到率 = 未到件数 ÷ 应到件数;完全未到运单 = 整单零到货;部分未到运单 = 部分到货、部分缺件。", - "合计 / 图表仅含 4 站(顺心/中通/韵达/安能,应到−实到口径);百世为站点直供未到、无应到基数,单列不计入合计。", + "合计 / 图表仅含 4 站(顺心/中通/韵达/安能,应到−实到口径);百世应到/实到取自「扫描综合查询」应扫/已扫(到/接件扫描→当日),已填入百世行,但为保持 4 站口径一致、不计入合计与图表。", "本次下载失败的站点标注为(无数据)并计 0,不影响其余站点统计。", "明细见各站点工作表;未到明细仅列短少运单,并列出该运单实际扫到的单号(已到单号1…),缺件不再编造子单号。", "数据日期:各站本次纳入数据对应的业务日期(=应到数据下载日 − 日期偏移;韵达偏移 1 为前一日);合计为多站混合、不标注。", diff --git a/site_baishi.py b/site_baishi.py index b48bc52..64e1a86 100644 --- a/site_baishi.py +++ b/site_baishi.py @@ -174,6 +174,31 @@ def baishi_download_undelivered_data_impl(page): page.get_by_role("tab", name="实时扫描率").wait_for(state="visible") print("✅ 扫描综合查询界面已加载") + # 1.5 顺手抓取「到/接件扫描 → 当日」的 应扫/已扫(=应到/实到基数), + # 供汇总报表填写百世行的应到件/实到件。 + # 实时扫描率表头为 14 列 leaf:发/交件[昨日×3, 当日×4] + + # 到/接件[昨日×3, 当日×4],到/接件当日四列索引为 + # 10(应扫) 11(已扫) 12(未扫) 13(率)。(与下方 nth(12) 未扫同源) + try: + _first_row = page.locator(".ant-table-tbody > tr").first + _exp_txt = _first_row.locator("td").nth(10).inner_text().strip().replace(",", "") + _arr_txt = _first_row.locator("td").nth(11).inner_text().strip().replace(",", "") + + def _to_int(v): + try: + return int(float(v)) if v not in ("", "-") else 0 + except (TypeError, ValueError): + return 0 + + _exp_n, _arr_n = _to_int(_exp_txt), _to_int(_arr_txt) + if _exp_n > 0: + state_store.set_setting("百世", "scan_expected_pieces", str(_exp_n)) + state_store.set_setting("百世", "scan_arrived_pieces", str(_arr_n)) + print(f" ℹ️ 已记录百世应到/实到基数:应扫 {_exp_n} / 已扫 {_arr_n}") + except Exception as _e: + # 抓取失败绝不影响未到明细下载主流程 + print(f" ⚠️ 抓取百世应到/实到基数失败(不影响未到明细下载):{_e}") + # 2. 精准定位并点击【到/接件扫描 -> 当日 -> 未扫】的数字控件 print(">> 正在解析表格,提取当日到件未扫明细...") # 定位第一行数据的第 13 列(索引 12)