# baoyu-dashboard-update — data sources

## ⚠️ ERP Sheet 陷阱（2026-07-10）

`pd.read_excel(sheet_name=0)` 可能读到的是**透视汇总表**而非原始订单数据。2024/2025/2026 ERP文件的Sheet命名不直观：

| 文件 | sheet_name=0 | 原始数据Sheet |
|------|-------------|--------------|
| 2024全年订单.xlsx | Sheet4（透视表） | **Sheet1** |
| 2025全年订单.xlsx | Sheet2（透视表） | **Sheet1** |
| 2026年1-6月订单.xlsx | Sheet3（透视表） | **Sheet1** |

**铁律**：必须先用 `pd.ExcelFile(fp).sheet_names` 列出所有Sheet，找行头含「客户」「价税合计」的Sheet作为数据源。

当用户说"根据原始数据更新所有数据"、提供 `销售分析/` 目录路径、或需要从零重建所有派生JSON时使用。此模式从三份订单文件+客户分析报告+工作汇报分析出发，批量重建全部9个JSON文件。详见 `references/full-rebuild-from-sources.md`。


## ⚠️ 数据源碎片化问题（关键陷阱），导致同一客户/年份在不同模块中数值不一致。这种情况的根因和修复方法：

**常见症状**：customerGroups中某集团2025年=384万，但客户分析仪表盘中同集团2025年=560万。

**根因排查**：
1. erp销售订单_20260328_*.xlsx 导出时间截止3月28日，可能**不完整**（如2024年缺1-4月，2025年总量仅3112万 vs 实际4163万）
2. 客户分析报告.xlsx 可能用了不同的ERP导出批次
3. 仪表盘各模块可能混用了不同数据源

**ERP数据源权威性排序（2026-06-30 新增）**：

不同ERP导出文件覆盖不同的时间范围和完整度。**按权威性排序**：

| 优先级 | 文件 | 覆盖范围 | 完整度 | 说明 |
|--------|------|---------|--------|------|
| 🥇 首选 | `销售订单_2023～20260430.xlsx` | 2023-2026.04（含部分5月） | ★★★★★ | 行数最多(20K+)，2023-2026全覆盖。用于累计统计和年度对比 |
| 🥈 次选 | `erp销售订单_20260328*.xlsx` | 2024-2026.03 | ★★★★ | 16K行，有产品(物料编码/货号)信息。用于产品维度分析 |
| 🥉 补充 | May Sheet1 + June汇总 | 2026.05-06 | ★★★ | 月度增量。用于补充最新数据 |

**⚠️ 致命陷阱**：`erp销售订单_20260328*.xlsx` 是2026年3月28日快照，**数据远不完整**：
- 缺2023全年数据
- 2024-2026各年金额都偏低（如丽珠：ERP=391万 vs 完整=735万）
- **不能作为客户累计金额的唯一数据源！尤其不能用于调研报告的累计汇总数字**

**调研报告数据更新流程（2026-06-30 新增）**：
1. 用首选文件（`销售订单_2023～20260430.xlsx`）提取客户全量累计金额
2. 补充最新月度数据（May Sheet1 + June汇总）
3. 用仪表盘 YOYR_CUSTS 提取的集团映射聚合子公司
4. 输出年度同比表（含金额、笔数、YoY%、趋势解读）
5. 更新报告KPI卡片（累计总金额 + 当期金额 + 同比 + 年化预估）
6. 更新"最近采购"日期为最新可用数据

**修复方法**：
1. 统一使用三个完整文件：
   - 2024: `订单分析/2024全年订单.xlsx` (Col0=合同日期, Col3=客户, Col11=价税合计) — 3448万
   - 2025: `订单分析/2025全年订单.xlsx` (同上结构) — 4163万
   - 2026: `CRM及ERP数据/销售订单_2023～20260430.xlsx` (首选) + May/June — 累计约2300万(1-6月)
2. 用同一个Python脚本从三个文件统一聚合，确保集团映射、金额一致性
3. 生成的数据数组一次性替换掉HTML中所有旧数据（total2024/2025/2026, monthly_*, top20Data, top10TableData 等）
4. 聚合后核对：关键集团（圣湘/天隆/丽珠）的年度金额应在所有模块中一致

**跨文件列结构差异**：

详见 `references/erp-column-mapping.md`。关键速查：

| 文件 | 区域列 | 产品过滤列 | 金额列 | 
|------|--------|-----------|--------|
| 旧ERP (16K) | Col22=区域# (5大区) | Col24=产品类型#="核酸诊断" | Col3 |
| 新订单 (2026H1) | **Col5=区域** (23细分) | **Col25=产品大类#**="核酸诊断" | Col13 |

⚠️ 新文件必须用 Col25 过滤，用 Col24 会遗漏子类。Col5直接聚合会包含非诊断原料部门订单。


## ⚠️ 新ERP数据源：2025/2026客户价格.xlsx（2026-06-30 新增）

**权威性最高的ERP导出文件**，行业标准格式，优先于旧数据源：

| 文件 | 行数 | 覆盖 | 列结构 |
|------|------|------|--------|
| `2025客户价格.xlsx` | 31,167行 | 2025全年 | 合同日期/单据类型/客户/区域/销售员/物料名称/含税单价/价税合计/是否赠品 |
| `2026客户价格.xlsx` | 11,585行 | 2026至6月29日 | 同上+税率% |

### H1过滤

```python
df25 = pd.read_excel('2025客户价格.xlsx', dtype=str)
df25['amt'] = pd.to_numeric(df25['价税合计'], errors='coerce')
# 仅取H1：1-6月，排除赠品/零金额
df25_h1 = df25[(df25['amt'] > 0) & df25['客户'].notna() & 
               df25['合同日期'].str.match(r'2025-0[1-6]', na=False)]
```

### 区域字段

2025/2026价格文件含 `区域` 列（23细分区域），可直接用于集团级区域归属：
```python
# 建立客户→区域映射
cust_region = {}
for _, r in df26_h1.iterrows():
    cust = str(r['客户']).strip()
    reg = str(r['区域']).strip()
    if cust and reg and cust not in cust_region:
        cust_region[cust] = reg
```
