Files
InboundVerify/docs/2026-08-04-实到按扫描日提取修正-design.md

113 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实到按「归属日当天扫描」提取修正 · 设计方案
> 日期2026-08-04
> 分支:`refactor/undelivered-by-expected`
> 状态已实施2026-08-04 验证通过)
> 目标:把差缺对比中的「实到货物数据」提取口径修正为「目标归属日当天实际扫描」,与业务口径对齐
---
## 一、业务口径(与业务部门对齐后的最终陈述)
差缺对比在**归属日期业务日期D** 这一维度上进行:
1. **应到货物数据**:取**归属日 = D** 的应到批次(批次归属日 = 出库大头日,已实现为 `expected_record.batch_out_date`)。
- 站点"提前下载"(韵达固定提前 1 天、个别站点偶发提前 1~2 天)不影响取数——提前提交的批次按其**出库日**自动归属到 D 日。
2. **实到货物数据**:取 **D 日当天实际扫描**的记录(`actual_record.scan_time::date = D`**全量、零加工**
- "当天实际扫描"指扫描行为发生在 D 日,与运单属于哪个批次无关。
- 重复单号在入库时即被吸收(`actual_record` 唯一键 `(site, piece_no)` + upsert对比层不再做任何加工。
3. 对取到的双方数据按运单号进行差缺对比。
**关键原则**:说"同一天"指的是**归属日期**,不是数据产生/下载的日期。
---
## 二、现状问题
| 环节 | 现状 | 问题 |
|------|------|------|
| 应到提取 | `WHERE batch_out_date = D`(应到驱动入口) | ✅ 已对齐 |
| 实到提取 | `WHERE waybill_no = ANY(批次运单)`**无扫描日过滤** | ❌ 跨日扫描混入 |
**实证案例(安能 08-03**
- 08-03 实到下载文件 = 355 行,全部扫描于 08-03获取层正确
- 今天 15:03 另有一次 08-04 实到下载176 行),其中 **6 件扫描于 08-04、运单属于 08-03 批次**
- 对比 SQL 按运单号取实到 → 08-03 实到 = 350当天+ 6次日= **356**,与"当天实际扫描"定义不符。
根因:`db_compare.py` 三处取实到的 SQL 只按运单号匹配,未按扫描日过滤。
---
## 三、修正方案
### 3.1 实到提取统一为「目标日当天扫描全量」
| 函数 | 用途 | 改动 |
|------|------|------|
| `compare_site_outdate` | 汇总报表(应到驱动,主入口) | 实到 SQL 改为 `WHERE site=%s AND scan_time::date=%s`,取当天扫描**全量**(不再按批次运单过滤) |
| `compare_site_date` | 实到驱动对照模式 | 同上Step 1 锚点本已按当天扫描Step 4 同步为全量口径) |
| `compare_site_batch` | 单批次审计(无日期语境) | **保持全量**docstring 注明"全批次全量实到,不受日期过滤" |
修正后统计口径:
```
应到 = 归属日 D 的批次全量应到(交接件数,不变)
实到 = D 日当天实际扫描的全量记录(零加工)
未到 = max(0, 应到 实到)(件级汇总);差缺明细按运单匹配(足额/溢到跳过)
```
**去重说明**:单号去重已在入库时完成——`schema.sql``actual_record``UNIQUE(site, piece_no)``store.py` 以 ON CONFLICT DO UPDATE 写入(韵达另有 drop_duplicates。对比层拿到的实到本就是去重后的数据不再做任何额外加工。
### 3.2 边界语义确认
| 场景 | 修正后行为 |
|------|-----------|
| 批次 D 的件扫描于 D+1迟到件 | 计入 D+1 日实到(其扫描日在 D+1D 日差缺按运单匹配判定,未到件照常呈现 |
| 批次 D 的件提前扫描于 D1 | 计入 D1 日实到(扫描日为准),不入 D 日实到 |
| 当天扫描但运单不在 D 日应到批次(孤儿) | 仍计入 D 日实到件数(实到=当天全量);差缺明细按运单匹配,无对应应到运单则不出现 |
| 一件多扫(同日) | 入库唯一键 `(site, piece_no)` 已吸收重复,对比层零加工 |
### 3.3 不做的事(范围外)
- 下载层各站查询日期口径已验证正确08-03 四站文件 100% 当天扫描),**不改下载层**。
- 获取层"日期回读校验"(韵达/中通/顺心选完日期后未校验输入框值)另立改进项,不阻塞本次。
- 百世为站级日聚合(已扫/应扫,固定当天),不涉及。
---
## 四、涉及改动清单
| 文件 | 改动 |
|------|------|
| `inbound_verify/db_compare.py` | `compare_site_outdate``compare_site_date` 两处实到 SQL 改为 `scan_time::date = 目标日` 全量取数;`compare_site_batch` docstring 注明口径差异;`_do_compare` 实到件数 = 实到记录全量 |
仅此一处文件;无需 schema 改动(去重本已由入库唯一键保证)。
---
## 五、验证结果2026-08-04 已重跑)
按新口径重跑 08-03 全站汇总(`build_full_report("2026-08-03")`
| 站点 | 应到 | 实到 | 未到 |
|------|------|------|------|
| 顺心 | 147 | 128 | 19 |
| 中通 | 305 | 276 | 29 |
| 韵达 | 54 | 53 | 1 |
| 安能 | 351 | **355**= 下载文件 355 行,零加工) | 0 |
08-04同为全量口径顺心 134、中通 214、韵达 102、安能 176。
- 安能 08-03 实到 355 与 `安能-实到货物数据.xlsx` 完全一致,验证"实到=当天扫描全量、零加工"成立。
- 差缺明细各运单"已到单号"扫描日全部 = 08-03。
- 报表已输出:`InboundVerify/output/应到未到数据.xlsx`
---
## 六、决策记录
- ✅ 实到 = 归属日当天实际扫描的全量(`scan_time::date = D`),对比时零加工。
- ✅ 单号去重由入库唯一键保证(`UNIQUE(site, piece_no)` + upsert对比层不重复去重。
-`compare_site_batch` 保持全批次全量(审计工具语义,不按日过滤)。
- ✅ 差缺明细按运单匹配,足额/溢到跳过,未到件 = max(0, 应到 实到)。