修复安能实到日期设定 + 站点统计口径重构

- site_anneng.py: set_scan_date 改用 execCommand 全选替换+回车(修复 Ant DatePicker 受控组件未写入导致实到下成当天); wait_scan_form_ready 增加 settle 与 DatePicker 预热,消除首设竞态
- expected_undelivered.py: 应到口径改交接件数、实到改单号去重、未到明细列已到单号(不再编子单号),百世排除
- docs: 补充站点统计逻辑审查报告与韵达/安能计算逻辑梳理
This commit is contained in:
Misaka
2026-07-19 13:16:01 +08:00
parent f3ff188302
commit 8f79cbc5d7
4 changed files with 430 additions and 122 deletions

View File

@@ -0,0 +1,124 @@
# 韵达 / 安能 应到未到计算逻辑梳理(基于 expected_undelivered.py 重构版代码)
> 用途:供审核两站「应到件数 / 实到件数 / 未到件数」的实现逻辑与关联键。
> 代码基准:`InboundVerify/expected_undelivered.py`(重构版)。行号对应本次读取。
---
## 、两站共用的计算骨架process 函数,第 134216 行)
无论韵达还是安能,最终都走同一个 `process(name)`
1. 读应到表 + 实到表(第 148149 行)。
2. 应到表按关联键列去重、保留首条(第 152153 行)。
3. **应到件数** = 应到表「交接件数」之和(第 158168 行)。
4. **实到件数** = 实到表单件号列的**全局去重数**(第 176177 行,`act_pieces = sum(len(s) for s in arrived.values())`)。
5. 逐运单比较:实到扫到数 `<` 应到件数 的运单,才进未到明细(第 183201 行)。
6. **未到件数** = `max(0, 应到件数 实到件数)`(第 210 行)。
两站差异**只在配置**(关联键列、单件号列不同),算法完全一致。
---
## 一、韵达
### 1.1 配置STATIONS 第 106115 行)
| 配置项 | 值 | 含义 |
|---|---|---|
| exp应到文件 | `韵达-应到货物数据.xlsx` | 源 |
| act实到文件 | `韵达-实到货物数据.xlsx` | 源 |
| exp_qty | `交接件数` | 应到件数的取值列 |
| exp_wb | `运单号` | 应到表去重键 **+ 关联键** |
| exp_jd | `交接单号` | 未到明细展示字段 |
| arrived_pieces | `arrived_pieces_by_cols("主单号", "子单号")` | 实到解析 |
|  ├ wb_col | `主单号`(实到表) | 实到侧**关联基号**列 |
|  └ piece_col | `子单号`(实到表) | 实到侧**单件号**列 |
### 1.2 两套表的字段角色
- **应到表**`运单号`(去重+关联)、`交接件数`(该单应到件数)、`交接单号`(展示)。
- **实到表**`主单号`(关联基号,须与应到`运单号`同值域)、`子单号`(每件货物的单号,一个单号=一件)。
### 1.3 关联Join条件
```
应到表.运单号 == 实到表.主单号
```
机制:`arrived_pieces_by_cols` 以「主单号」为 key 建 dict第 7681 行);`process` 第 185 行 `arrived.get(wb)` 用**应到运单号 wb** 去查该 dict。两列值必须相等才对得上。
### 1.4 计算口径
- 应到件数 = Σ 交接件数(按运单号去重后)。
- 实到件数 = 实到表「子单号」全局去重数(不同主单号下即使子单号文本相同也只计一次)。
- 未到件数 = `max(0, 应到 实到)`
- 未到明细(每行一个短少运单):`交接单号 | 运单号 | 总件数(=应到件数) | 已到单号1..k`
「已到单号1..k」= 该主单号下所有子单号排序后填入k = 实际扫到件数(第 199200 行)。
仅当「实到扫到数 < 应到件数」才列入(第 187188 行 `arrived_cnt >= n` 跳过)。
### 1.5 当前数据状态实测2026-07-19 10:50
- 应到运单数 **57**,实到基号数 **58**,交集命中 **57**
- 应到件数 = 140实到单号去重 = 141
- 样本:`应到 617936212` == `实到主单号 617936212`,完全对齐
- **结论:韵达关联键现已对齐,逻辑可正确产出结果。**(此前 2/57 为旧数据,已失效)
---
## 二、安能
### 2.1 配置STATIONS 第 116125 行)
| 配置项 | 值 | 含义 |
|---|---|---|
| exp应到文件 | `安能-应到货物数据.xlsx` | 源 |
| act实到文件 | `安能-实到货物数据.xlsx` | 源 |
| exp_qty | `交接件数` | 应到件数取值列 |
| exp_wb | `运单号` | 应到表去重键 **+ 关联键** |
| exp_jd | `交接单号` | 未到明细展示字段 |
| arrived_pieces | `arrived_pieces_by_cols("所属单号", "扫描单号")` | 实到解析 |
|  ├ wb_col | `所属单号`(实到表) | 实到侧**关联基号**列 |
|  └ piece_col | `扫描单号`(实到表) | 实到侧**单件号**列 |
### 2.2 两套表的字段角色
- **应到表**`运单号`(去重+关联)、`交接件数`(应到件数)、`交接单号`(展示)。
- **实到表**`所属单号`(关联基号,须与应到`运单号`同值域)、`扫描单号`(每件单号,格式=`所属单号`+总数4位+顺序4位一个=一件)。
### 2.3 关联Join条件
```
应到表.运单号 == 实到表.所属单号
```
机制同韵达:`arrived_pieces_by_cols` 以「所属单号」建 key dict`process` 用应到运单号去查。
### 2.4 计算口径(与韵达结构完全一致)
- 应到件数 = Σ 交接件数。
- 实到件数 = 实到表「扫描单号」全局去重数。
- 未到件数 = `max(0, 应到 实到)`
- 未到明细:`交接单号 | 运单号 | 总件数 | 已到单号1..k`k=该所属单号下扫描单号数)。
### 2.5 当前数据状态实测2026-07-19 10:50
- 应到运单数 **97**,实到基号数 **140**,交集命中 **4**
- 应到件数 = 236实到单号去重 = 279
- 样本:`应到 750101236894` vs `实到所属单号 760237639793` —— 两套不同编号体系
- **后果**:实到 279 件几乎全无法对应到任何应到运单 → 明细把 93 个运单列「完全未到」,但汇总层 `实到=279 > 应到=236` → 未到净额被 `max(0,…)` 截断为 **0**,于是「明细列 96 行短少」与「汇总未到=0」自相矛盾。
- **结论:安能关联键未对齐,当前产出不可信。**
---
## 三、两站逻辑差异对照
| 维度 | 韵达 | 安能 |
|---|---|---|
| 应到关联键列 | `运单号` | `运单号` |
| 实到关联基号列 | `主单号` | `所属单号` |
| 实到单件号列 | `子单号` | `扫描单号` |
| 单件号是否带后缀 | 是(主单号+顺序号) | 是(所属单号+总数+顺序) |
| 关联键是否对齐(当前数据) | ✅ 57/57 | ❌ 4/97 |
| 计算结果是否可信 | 可信 | 不可信(自相矛盾) |
> 两站**算法完全相同**,唯一区别是实到侧的「基号列 / 单件号列」列名不同。因此问题不在代码逻辑,而在**安能的关联键取值域对不上**。
---
## 四、审核要点(请你判断)
1. **韵达**:关联键 `应到.运单号 == 实到.主单号` 是否符合业务实际?当前数据已对齐,似乎正确;若你确认,韵达逻辑可定稿。
2. **安能**:关联键 `应到.运单号 == 实到.所属单号` 是否正确实测两套编号不重合97 个应到运单仅 4 个能在实到找到)。可能的方向:
- (a) 实到表存在另一列能与应到`运单号`对应(需确认列名,可能改 `wb_col`
- (b) 应到/实到文件是按不同条件/批次拉的,需要按同一天、同线路重新拉取;
- (c) 暂时把安能排除(同百世),等数据对齐再加回。
3. **共同结构问题**:汇总「未到件数」用净额 `max(0, 应到−实到)`,而明细按「逐运单短少」列——当关联键断裂时二者会矛盾(安能现例)。关联键对齐后此矛盾自然消失;是否需要在代码里对「净额 vs 明细」做一致性校验/告警,也请你定。