# ERP 九月增量入库 — 实现与教训（2026-10-01）

## 任务
用户上传 `销售订单_20260901-0930.xlsx`（481,140 B / 2,837 行 / 31 列），要求更新 9 月 ERP 数据。

## 结果
- 现用件 0928（24,836 行）→ 合并件 **`销售订单_20260101-20260930.xlsx`（25,027 行 / 2.31 MB）**
- 9 月 **390.1 万 → 431.4 万**（+41.3 万）｜ 拓展 258.9→286.0 ｜ 大客户 124.3→137.6 ｜ 生命科学 6.9→7.9
- 全年 3401.5 → **3442.8 万**；H1 审计锁 **2269.0 万未被动** ✅
- 历史月 1–8 月**逐月 0.0 差值**（MERGE_OK=1）
- 部署 **16 个 JSON**，本地 == CDN **16/16 逐字节一致**

## 五步流程（实跑命令）
```bash
# 0 归档增量件（缓存会被清）
ARCH="$HOME/Desktop/hermas输入-工作台数据库/月度销售额数据/_增量归档"
cp -p ~/.hermes/profiles/zone-b/cache/documents/doc_<hash>_销售订单_YYYYMMDD-YYYYMMDD.xlsx "$ARCH/"
# 1 拍基线（旧快照先存 _baseline_history/）
python3 脚本/erp_baseline.py
# 2 核验增量
ERP_INCREMENT="$ARCH/xxx.xlsx" python3 脚本/erp_inspect_increment.py
# 3 合并 + 回归（自带 MERGE_OK）
ERP_INCREMENT="$ARCH/xxx.xlsx" python3 脚本/erp_merge_increment.py
# 4 主管道
python3 脚本/refresh_erp.py && python3 数据/mcp_kpi_pipeline.py && python3 脚本/refresh_erp_amount.py
# 5 派生物（不在自动管道！）+ 依赖 ERP 的三步 + 合并 + 部署
```

## 踩坑与教训（新增 4 条）

### ① 【高危】派生物**既不在自动构建链、也不在 cb_sync.sh 部署清单**
`cb_sync.sh` 的 `FILES` 数组只有 13 项，**不含** `price_index / order_sku_index / customer_price_history / customer_order_context / orders_detail_2026 / product_bom_map / product_parent_child_map`。
实测：线上这 6 个文件 **last-modified 全部停在 09-24 00:01**，本次已落后 7 天。
→ **重建后必须手动 `tcb hosting deploy`**，否则前端静默用旧版。
→ 待办：把这 6 个文件补进 `cb_sync.sh` 的 FILES + 加构建步骤（本次未改 cb_sync.sh）。

### ② 依赖 ERP 的三步也会漏
`build_customer_360.py`（3c）、`build_customer_dossier.py`（3d）、`refresh_key_customers.py --apply`（3e）**都在 cb_sync 链里、也读 ERP**，但手工跑主管道时容易漏 → `customer_360.json` / `pricing/_key_customers.json` 的 2026 金额会停在旧 ERP。
→ 本次补跑后 `customer_360.json` **+59 家 ERP 新增客户**。

### ③ 基线快照可能**落后于现用件**（隐蔽）
`_erp_baseline_snapshot.json` 来源是 `…20260923.xlsx`（抓于 09-29 06:59），而 `find_latest_erp()` 当时选中的已是 `…20260928.xlsx`（mtime 09-29 07:00，**晚 1 分钟**）。
→ **回归前必须核对 `baseline['source_file'] == basename(find_latest_erp())`**，否则整张回归表的基线是错的。
→ 归档旧快照时按真实来源命名（本次改名为 `_erp_baseline_from0923_stale.json`）。

### ④ `for f in $LIST` 在**本机不分词** → 整个字符串当一个路径
`LIST="a b c"; for f in $LIST` 实测把整串当成**单个** `$f`，`tcb` 报「部署失败」而看不出原因（`$0` 显示是 `/bin/bash`，仍复现）。
→ **一律用显式列表** `for f in a b c; do`，或用 `"${FILES[@]}"`（cb_sync.sh 的写法是对的）。
→ 副作用：失败信息极具误导性（后续 12 个文件被当作 1 个）。

## 顺手修掉的小 bug
`脚本/refresh_key_customers.py` L148 `print(f"    理论全年（three_year_monthly）: 3401.5万")` —— **f-string 里的纯硬编码，无插值**，每次刷新都打印同一个陈旧数字，极易被误读成「勾稽未通过」（本次即被误导一次）。
→ 已改为实读 `数据/three_year_monthly.json` 的 2026 合计；备份 `脚本/refresh_key_customers.py.bak-hardcode-label-20261001-201607`。
→ 验证：修补后打印 **3,442.8 万** ✅。

## 四、修复落地（用户 2026-10-01 拍板「1、要，2、要」）

### 修复 1：`脚本/cb_sync.sh` — 派生物纳入自动构建 + 部署
- **新增 3f 段**（在 3e `refresh_key_customers` 之后、`# 5` 部署之前）：
  `build_price_index.py` / `build_order_data.py` / `gen_orders_detail.py` / `build_product_mapping.py`
  （每个都带 `|| echo "⚠️ 失败"`，因脚本有 `set -e`，不能裸跑）
- **`FILES` 数组 13 → 20 项**：补进 7 个派生物
  （`price_index` / `order_sku_index` / `customer_price_history` / `customer_order_context` /
   `orders_detail_2026` / `product_bom_map` / `product_parent_child_map`）
- 备份：`脚本/cb_sync.sh.bak-derivatives-20261001-202251`

### 修复 2：`~/.hermes/scripts/daily_full_refresh.py` — 派生物纳入每日 9 点全量刷新
- `PIPELINES` 4 → 8 步：加上面 4 个派生物构建步（cwd=ROOT）
- 新增 `deploy_derivatives()`：把 7 个派生物推上 CloudBase（`shutil.which('tcb')` 守卫；
  失败只告警不改退出码）
- **构建步有失败则跳过上线**，避免推半成品
- 备份：`~/.hermes/scripts/daily_full_refresh.py.bak-derivatives-20261001-202251`

### ⚠️ 修复过程中发现的第 5 个坑：`daily_full_refresh.py` 原先**没有 `if __name__ == '__main__'` 守卫**
用 `import daily_full_refresh` 只是读 PIPELINES 常量，却**触发了整条流水线执行**（当次因此超时 300s）。
→ 已重构为 `def main():` + `if __name__ == '__main__': main()`；修后 import 耗时 0.02s。
→ **教训：任何可能被 import 的脚本都要有主守卫**；验证配置时用 `python3 -c "import ..."` 很危险。

### 验收证据（2026-10-01 20:29–20:32 实跑）
- `bash 脚本/cb_sync.sh` → **exit=0**
- 日志出现 `>>> 重建派生物（价格索引/订单数据/订单明细/BOM）...` 及 4 步产物 ✅
- 日志出现 `✓ 数据/price_index.json` … `✓ 数据/product_parent_child_map.json`（7 个）✅
- 7 个派生物 **本地 vs CDN sha256 = 7/7 一致**；CDN `last-modified` 由 **09-24 → 10-01 20:31** ✅
- `refresh_key_customers` 打印 `理论全年（three_year_monthly）: 3,442.8万`（硬编码修复生效）✅

## 五、`mcp_sales_pipeline.py`（销售单）从 PIPELINES 摘除（用户 2026-10-01 拍板）

- 产物 `数据/daily_sales_mcp.json` 自 **2026-07-27** 起停更，该管道已定性「暂不更新」
- 但每天仍挂到 **600s 超时** → 白耗 10 分钟，且让脚本报 `exit=1`（**假失败**，会淹没真实失败）
- 改法：PIPELINES 里该行**注释掉**（保留代码 + 4 行注释说明原因与恢复方法），不删除
- 备份：`~/.hermes/scripts/daily_full_refresh.py.bak-before-removesales-20261001-204545`（改前 sha `6cd3afcf…`）

### 摘除后实跑（2026-10-01 20:47）
```
✅ 客诉+审批阻塞 8s ｜ ✅ KPI更新 3s ｜ ✅ 知识库索引重建 1s
✅ 价格索引重建 11s ｜ ✅ 订单数据重建 36s ｜ ✅ 订单明细重建 4s ｜ ✅ 产品BOM重建 3s
✅ 派生物上线 7/7
===== 全量刷新完成: 7/7 =====   exit code 0
总耗时 84 秒（摘除前 ~660 秒）→ 省 87%
```
- 7 个派生物 **本地 vs CDN sha256 = 7/7 一致** ✅

### 前置前提核实（勿凭记忆）
- `CloudBase数据同步`（job `4e22bc74300b`，`0 */2 * * *`，deliver=local）→ 跑 `cb_sync.sh`，**每 2h** ✅
- 全量刷新 job `f846b80f102d`（`0 9 * * *`，deliver=local）
- ⚠️ 两个 job 的 `deliver` 都是 **local**，失败不会推到飞书，只在本地留痕

## 验收口径（可复用）
1. `MERGE_OK=1` + 历史月逐月 0.0 差值
2. 9 月在 `kpi_progress.total` / `erp_amount.monthly['2026-09']` / `three_year_monthly.2026['9']` **三处一致**
3. `kpi_dashboard.total_h1` 保持审计锁 2269.0 万
4. 未来月份（10–12 月）必须是**空白而非 0**
5. 本地 vs CDN **逐文件 sha256**（带 `Cache-Control: no-cache`）
6. 收尾重拍基线，供下次回归

## 数据源
- 增量件归档：`~/Desktop/hermas输入-工作台数据库/月度销售额数据/_增量归档/`
- 合并件：`~/Desktop/hermas输入-工作台数据库/月度销售额数据/销售订单_20260101-20260930.xlsx`
- 保险备份：`数据/_erp_file_backup/销售订单_20260101-20260928.xlsx.bak-preymerge`
- 基线历史：`数据/_baseline_history/`
