# 客户价格表生成管道（2026-07-10）

## 概述

从多年ERP订单Excel中提取单个客户的完整采购历史，按货号#聚合，生成JSON + 交互式HTML价格表。

## 数据源

| 文件 | Sheet（原始数据） | 关键列 |
|------|------------------|--------|
| 2024全年订单.xlsx | **Sheet1** | 客户, 物料编码, 物料名称, 含税单价, 销售数量, 价税合计, 是否赠品, 合同日期 |
| 2025全年订单.xlsx | **Sheet1** | 客户, 物料编码, 货号#, 物料名称, 含税单价, 销售数量, 价税合计, 是否赠品, 合同月份 |
| 2026年1-6月订单.xlsx | **Sheet1** | 客户, 物料编码, 物料名称, 含税单价, 销售数量, 价税合计, 是否赠品, 合同月份 |

⚠️ **Sheet陷阱**：`pd.read_excel(sheet_name=0)` 可能读到透视表Sheet（如Sheet4/Sheet2/Sheet3），而非原始数据Sheet1。必须用 `pd.ExcelFile(fp).sheet_names` 确认，找含「客户」「价税合计」列的Sheet。

## 核心区别

| 字段 | 说明 |
|------|------|
| **货号#** | 对外SKU（如 M5274-073703），用于价格表展示 |
| **物料编码** | 内部编码（如 2.99.05.0043），同一货号可能有多个物料编码 |
| **客户物料规格型号#** | 客户侧的规格型号，可作为规格列来源 |

## 聚合规则

1. **按货号#聚合**（非物料编码）— 同一货号的多个物料编码合并
2. **最近成交价** = 有价格记录中日期最近的那条的含税单价
3. **历史成交价** = min~max 范围（若只有一次则为单值）
4. **赠品判断** = 是否赠品='是' 或 金额=0
5. **规格提取** = 客户物料规格型号# → 若无，从物料名称正则提取浓度规格（如 `5×`、`10×`）
6. **年份** = 合并所有年份，去重排序

## 生成脚本模板

```python
import pandas as pd, numpy as np, json
from collections import defaultdict

all_records = []
for year, fp, sheet in files:
    df = pd.read_excel(fp, sheet_name=sheet)
    xc = df[df['客户'].str.contains(customer_name, na=False)]
    for _, r in xc.iterrows():
        # 提取货号#（优先）或物料编码（fallback）
        sku = str(r.get('货号#', r.get('物料编码', '')))
        # 提取规格：客户物料规格型号# 或从物料名称正则提取
        spec = str(r.get('客户物料规格型号#', ''))
        if not spec or spec == 'nan':
            m = re.search(r'(\d+[×xX]\S*)', str(r['物料名称']))
            if m: spec = m.group(1)
        
        all_records.append({
            'sku': sku, 'name': str(r['物料名称']), 'spec': spec,
            'qty': float(r['销售数量']), 'unit_price': float(r['含税单价']),
            'amount': float(r['价税合计']), 'date': str(r.get('合同日期', r.get('合同月份'))),
            'is_gift': str(r.get('是否赠品')) == '是' or float(r['价税合计']) == 0
        })

# 按货号#聚合
products = defaultdict(lambda: {'total_qty':0, 'total_amt':0, 'prices':[], 'years':set(), ...})
for r in all_records:
    p = products[r['sku']]
    p['total_qty'] += r['qty']
    p['total_amt'] += r['amount']
    if r['unit_price'] > 0:
        p['prices'].append((r['date'], r['unit_price']))
    p['years'].add(r['year'])
    # ...

# 最近成交价 = 价格记录中日期最新的
p['prices'].sort(key=lambda x: x[0])
latest = p['prices'][-1]  # (date, price)
```

## HTML面板特性

- 暗色渐变顶栏（`linear-gradient(135deg,#1a1a2e,#16213e,#0f3460)`）
- 客户选择器（下拉切换）
- KPI卡片：客户名/累计采购/付费产品数/赠品数
- 年份筛选（全部/2024/2025/2026）+ 类型筛选（全部/付费/赠品）
- 搜索框（货号/品名实时搜索）
- 可排序列：数量、最近成交价、总成交金额
- 赠品行半透明灰色显示
- 零外部依赖（纯HTML+CSS+JS，无Chart.js/HermesTable）

## 表头规范

| 货号 | 品名 | 规格 | 数量 | 最近成交价 | 历史成交价 | 总成交金额 | 年份 | 最近采购 |

## 入口集成

在统一工作平台顶栏添加 `💰 价格表` 链接按钮。

## 新产业案例参考

- 265条订单，104个货号，累计9.1万
- 仅3个货号有付费记录（M5274-073703 Neoscript 8.2万）
- 其余101个货号均为免费测试样品
- MD6104从未正式采购（仅2025-07赠品测试204支）
