# ERP 销售数据清洗铁律

## 1. 删除合计行

ERP导出的xlsx最后一行通常是合计汇总行（`单据编号`='合计'），**必须在解析前删除**：

```python
erp=erp[~erp['单据编号'].str.contains('合计',na=False)]
erp=erp[erp['单据编号'].notna()&(erp['单据编号']!='')]
```

**症状**：不删合计行 → 金额翻倍（如226.5万 vs 实际78.0万）

## 2. 空白销售员映射

ERP导出中大量行的`销售员`列为空或"未知"，需通过客户名称→销售员映射回填：

```python
# 从客户清单加载映射
cust_sp={}
for s in xl.sheet_names:
    df=pd.read_excel(fp, sheet_name=s, header=1, dtype=str)
    for _,r in df.iterrows():
        name=str(r.get('客户名称','')).strip()
        sp=str(r.get('销售员','')).strip()
        if name and name!='nan' and sp and sp!='nan':
            cust_sp[name]=sp

# 回填空销售员
for i,r in erp.iterrows():
    if not r['销售员'] or r['销售员']=='未知':
        cust=str(r.get('客户','')).strip()
        if cust in cust_sp:
            erp.at[i,'销售员']=cust_sp[cust]
```

## 3. 金额列选择

ERP导出使用`价税合计`列（而非`金额`/`含税单价`）：

```python
erp['价税合计']=pd.to_numeric(erp['价税合计'],errors='coerce').fillna(0)
total=erp['价税合计'].sum()
```

## 4. MCP vs ERP 数据源确认

| 来源 | 金额可用？ | 实时性 | 适用场景 |
|------|:--:|------|------|
| MCP `销售订单`.`金额` | ❌ 始终为0 | 实时 | 仅计数/编号 |
| MCP `销售订单`.`单价` | ❌ 始终为0 | 实时 | 不可用 |
| ERP导出 `价税合计` | ✅ 准确 | 手动导出 | 累计销售额、按人统计 |

**铁律**：销售金额统计只能用ERP导出，MCP不可用于此目的。

## 5. 文件归档位置

ERP导出文件 → `~/Desktop/hermas输入-工作台数据库/月度销售额数据/销售订单{月}月{日区间}.xlsx`

## 6. 百分比格式化

达成率、同比、百分比显示统一使用整数：

```python
rate=round(actual/target*100,1)  # 如 31.2
f'{rate}%'  # 显示为 "31.2%"
```

## 7. 2025年ERP销售员列代号陷阱

2025全年ERP（`2025全年订单.xlsx`）的`销售员`列使用**代号而非真实姓名**：

| 代号 | 真实姓名 | 部门 |
|------|---------|------|
| `大客户1` | 黄明月 | 大客户部 |
| `大客户2` | 赵云浩 | 大客户部 |

2026年ERP已改用真实姓名。筛选跨年对比时必须做代号→姓名映射：

```python
# 2025代号映射
SALES_CODE_MAP_2025 = {'大客户1': '黄明月', '大客户2': '赵云浩'}

# 筛选大客户部时必须同时匹配代号和真实姓名
ka_names = {'赵云浩', '张立娅', '黄明月', '刘新元'}
ka_codes = {'大客户1', '大客户2'}
is_ka = df[sales_col].isin(ka_names) | df[sales_col].isin(ka_codes)
```

**症状**：用真实姓名筛选2025年ERP → 大客户部数据为空（只有'大客户1/2'）

## 8. ERP测试单提取模式（客户维度）

ERP的`单据类型`列含`测试销售订单`，可用于提取测试单数据并按客户汇总：

```python
# 过滤测试单
test_orders = df[df['单据类型'].astype(str).str.contains('测试')]

# 客户维度汇总（按单据号去重）
cust_stats = test_orders.groupby('客户').agg(
    测试单数=('单据编号', 'nunique'),
    销售员=('销售员', lambda x: '、'.join(sorted(set(x))))
).sort_values('测试单数', ascending=False)
```

⚠️ 测试单的`价税合计`通常为0或空（赠品），统计时只计数不计金额。
