# ERP 数据处理 Pitfalls（2026-08-07）

## 合计行过滤

ERP 导出的最后一行是合计行，特征：
- `单据编号` 列为 `None` → `str(None)` = `'None'`（字面值，不是空字符串！）
- `合同日期` 列为 `"合计"`
- `销售员` 列为 `None`
- `价税合计` 为累计值（会被误统计）

```python
# ✅ 正确过滤
bn = str(row.get(bn_col, '') or '').strip()
if not bn or bn == 'None' or '合计' in bn:
    continue  # 跳转合计行

# ❌ 错误 — str(None)='None' 不触发 not bn
if not bn: continue
```

## openpyxl 陷阱

```python
# ❌ read_only=True → iter_rows 返回 ReadOnlyCell，遍历异常
wb = openpyxl.load_workbook(fp, data_only=True, read_only=True)
headers = [c.value for c in next(ws.iter_rows(min_row=1, max_row=1))[0]]
# → 'ReadOnlyCell' object is not iterable

# ✅ 去掉 read_only，用 values_only=True
wb = openpyxl.load_workbook(fp, data_only=True)
headers = [str(c) for c in next(ws.iter_rows(..., values_only=True))]
```

## 智能月份检测

从单据编号前缀识别月份，避免历史月数据覆盖当月 KPI：

```python
# ZHBR2607xxx → 2026-07, ZHBR2608xxx → 2026-08
bn = str(row.get(bn_col, '') or '').strip()
if bn.startswith('ZHBR') and len(bn) >= 10:
    ym = '20' + bn[4:6] + '-' + bn[6:8]  # → 2026-08
```

备选：文件名解析 `re.search(r'(2026\d{2})', saved_name)`

## 月份路由

| 检测结果 | 目标文件 | 说明 |
|---------|---------|------|
| 当前月 | `kpi_progress.json` | 当月KPI |
| 历史月 | `three_year_monthly.json` + `kpi_dashboard.json` | 历史校准 |

⚠️ `kpi_progress.json` 是当月数据，不应被历史月覆盖。

## kpi_dashboard vs kpi_progress

- `kpi_progress.json` — 当月数据（如 8月 KPI）
- `kpi_dashboard.json` — 累计数据（`total_h1_h2` = H1 + H2 全部）
- 前端 `ENDPOINTS` 独立读取这两个 JSON（非 `workbench_data.json` 内嵌）
- **两者都必须部署到 CloudBase**

## cb_sync_data.sh 文件清单

必须包含的 JSON：`kpi_dashboard.json`, `kpi_progress.json`, `workbench_data.json`, `shipment_status.json`, `mcp_customers.json`, `mcp_complaints.json`, `opportunity_sandbox.json`, `my_activities.json`, `_key_customers.json`, `_personnel.json`

⚠️ `kpi_dashboard.json` 之前未包含在 `cb_sync_data.sh` 中，导致部署遗漏（已修复 2026-08-07）。

## 前端累计达成计算

看板"累计达成"计算逻辑（`工具/index.html` line ~1152）：

```javascript
// ❌ 错误 — prog.total 仅当月，低估累计
var fullYearTax = filteredYtd + prog.total;

// ✅ 正确 — 无筛选时用 kpiDashboard 权威累计
if (!filteredSalesSet) fullYearTax = dash.total_h1_h2 || fullYearTax;
```

## MCP 订单数据不可靠

MCP 订单中 `field_a6e501`（单据类型）分三种：
- 测试销售订单 — 价税合计必为 0
- 预测订单（FO）— 价税合计必为 0
- 标准销售订单 — 部分行为 0（赠品行/多行拆分）

**KPI 铁律：ERP 是金额唯一可靠源，严禁从 MCP 拉金额。**
