# -*- coding: utf-8 -*- """domain.py — 站点 / 文件名 / 列映射的共享配置(单一来源)。 比对(compare)与入库(store)都依赖这套配置;抽出独立 leaf 模块, 让 store 不必为读配置而依赖整个比对引擎。纯数据,无 state_store / 文件 IO 依赖。 """ from collections import defaultdict # 汇总报表覆盖的全部站点(4 站在前、百世在末;汇总页图表只取 4 站) ALL_REPORT_SITES = ["顺心", "中通", "韵达", "安能", "百世"] # 4 站单站未到明细文件名(百世未到文件由站点直接产出,名为 BAISHI_FILE) SITE_UNDELIVERED_FILE = "{name}-未到数据.xlsx" BAISHI_FILE = "百世-应到未到货物数据.xlsx" BAISHI_COLUMNS = ["类型", "子单号", "运单号", "最新扫描记录"] def arrived_pieces_zhongtong(df): """中通:实到「运单号」为复合串(H + 运单号(12) + 总数(4) + 顺序(4))。 基号 = v[:-8](与应到表运单号对齐),单件 = 整串(每串即一件)。""" res = defaultdict(set) for v in df["运单号"]: v = str(v).strip() if len(v) > 8 and v[-4:].isdigit(): res[v[:-8]].add(v) # 以完整复合串作为“已到单号”存入 return res def arrived_pieces_by_cols(wb_col, piece_col): """顺心 / 韵达 / 安能:按干净运单列分组,单件 = 子单号 / 扫描单号。 wb_col:实到表中与应到运单号对齐的干净列 (顺心=运单号 / 韵达=主单号 / 安能=所属单号) piece_col:实到表中每件货物的单号列(子单号 / 扫描单号)""" def parse(df): res = defaultdict(set) for m, s in zip(df[wb_col], df[piece_col]): m, s = str(m).strip(), str(s).strip() if m and s: res[m].add(s) return res return parse STATIONS = [ { "name": "中通", "exp": "中通-应到货物数据.xlsx", "act": "中通-实到货物数据.xlsx", "exp_qty": "交接件数", # 应到件数口径:交接件数(非录单件数) "exp_wb": "运单号", # 应到表运单号列(兼作去重键) "exp_jd": "交接单号", # 未到数据需展示的交接单号 "arrived_pieces": arrived_pieces_zhongtong, "columns": ["交接单号", "运单号", "总件数"], }, { "name": "顺心", "exp": "顺心-应到货物数据.xlsx", "act": "顺心-实到货物数据.xlsx", "exp_qty": "交接件数", "exp_wb": "运单号", "exp_jd": "交接单号", "arrived_pieces": arrived_pieces_by_cols("运单号", "子单号"), "columns": ["交接单号", "运单号", "总件数"], }, { "name": "韵达", "exp": "韵达-应到货物数据.xlsx", "act": "韵达-实到货物数据.xlsx", "exp_qty": "交接件数", "exp_wb": "运单号", "exp_jd": "交接单号", "arrived_pieces": arrived_pieces_by_cols("主单号", "子单号"), "columns": ["交接单号", "运单号", "总件数"], }, { "name": "安能", "exp": "安能-应到货物数据.xlsx", "act": "安能-实到货物数据.xlsx", "exp_qty": "交接件数", "exp_wb": "运单号", "exp_jd": "交接单号", "arrived_pieces": arrived_pieces_by_cols("所属单号", "扫描单号"), "columns": ["交接单号", "运单号", "总件数"], }, ] def _site_cfg(name): """按名称取 4 站配置(百世不在 STATIONS,返回 None)。""" return next((c for c in STATIONS if c["name"] == name), None)