# 2026-07-28 Session Patterns & Fixes

## 1. SF Express Route Status Fix (顺丰 fine_status bug)

**Bug**: `fine_status()` used text matching before opCode check. "已派送成功" (delivery success) matched '派送' and was classified as `delivering` instead of `delivered`. 138 orders misclassified.

**Fix**: Prioritize opCode:
```python
def fine_status(routes, reason=''):
    if not routes: return ('no_data' if reason else 'unknown'), None
    last = routes[-1]
    op = last.get('opCode', '')
    remark = last.get('remark', '')
    # opCode 优先（最可靠）
    if op == '80': return 'delivered', last        # 已签收
    if op == '204' or op == '30': return 'delivering', last  # 派送中
    if op == '54': return 'picked_up', last         # 已揽收
    # 文本兜底
    if '签收' in remark or '已派送至' in remark or '派送成功' in remark:
        return 'delivered', last
    if '派送' in remark: return 'delivering', last
    if '揽收' in remark: return 'picked_up', last
    return 'in_transit', last
```

**Key insight**: "月结卡号不匹配" is a normal SF API response (requires phone verification), not a real error. Orders with route data despite this message are fine.

## 2. Order Deduplication (订单重复)

**Bug**: MCP `销售订单` returns multiple work items per order (one per product line). `pull_orders.py` deduped by `work_item_id` but not `单据编号`, causing same order to appear 4-12 times in `shipment_status.json`.

**Fix in pull_orders.py**:
```python
# 按单号去重，保留第一条
seen_oid = set()
for o in all_items:
    oid = o.get('name', '')
    if oid in seen_oid: continue
    seen_oid.add(oid)
    # ... build output
```

Result: 384 → 186 records, 103 duplicate order IDs → 0.

## 3. KPI Data vs Shipment Amounts (看板/订单管理金额)

**Bug**: Dashboard "累计达成" and order management "本年累计" summed `taxTotal` from shipment orders (187 records, 138 have taxTotal=0, total only 62.5万). Should use KPI data (H1 actual + H2 current = 2353.7万).

**Root cause**: MCP `field_51d592` (价税合计) returns 0 for most orders. MCP does not store actual amounts.

**Fix**: Use `kpi_dashboard.json.total_h1` + `kpi_progress.json.total`:
```javascript
var dash = state.data.kpiDashboard || {};
var prog = state.data.kpiProgress || {};
var fullYearTax = (dash.total_h1 || 0) + (prog.total || 0);
```

**⚠️** `renderDashboard` 中 `dash`/`prog` 必须显式声明，否则取到 undefined 导致 ReferenceError。

## 4. Multi-Select Filter Component

Reusable vanilla JS multi-select dropdown:
```javascript
multiSelect(id, label, options, selected, onChange);
renderMultiSelect(id, label, options, selected, labelMap);
```
Features: checkbox dropdown, tag display, select all/clear, click-outside-close, labelMap support. Applied to 3 tabs, 11 filters total.

## 5. Preparation Status for Orders Without Tracking

For orders without tracking numbers, compute status based on days since creation:
- ≤3 days: 🟢 备备中 (CSS: `prep`, green)
- 4-5 days: 🟡 备货预警(N天) (CSS: `prep-warn`, yellow)
- 6-7 days: 🟠 备货超期(N天) (CSS: `prep-overdue`, orange)
- >7 days: 🔴 严重超期(N天) (CSS: `prep-critical`, red)

## 6. Order Creator from 下单人 Role

Customer→owner mapping showed departed employees. Fixed by querying `get_workitem_brief` per order for `role_members["下单人"]`, cached in `order_role_cache.json`.

## 7. Contract Number Field

`field_4a1b47` (合同编号#) added to MQL SELECT, output mapping, and search scope.

## 8. CloudBase HTML Sync

`cb_sync.sh` now includes `tcb hosting deploy 工具/index.html /workbench/index.html` as final step.

## 9. Frontend 14→1 JSON Merge

`merge_workbench_data.py` → `workbench_data.json` (~2.3MB). `loadData()` single fetch replaces 14 concurrent fetches.

## 10. Dashboard Customer Count = 0

Admin filtered by userName. Fixed: `isAdmin` check before customer filter.

## 11. Complaints Sort by Date

`items.sort(function(a,b){return (b.created||'').localeCompare(a.created||'')})` in `renderComplaints()`.

## 12. Data Directory Cleanup

41 unused JSON files (11.8MB) → `数据/_archive/`. cb_sync 13→9 dynamic files.
