# Executive + Operations Views — Design

Two audiences, two pages. Both consume the same underlying data but slice it
for different decision-makers.

| Page | Audience | Decision they make |
|---|---|---|
| `/admin/performance` | CEO + CFO | "Are we winning?" + "Where's the money going?" |
| `/admin/operations-scorecard` | Director of Operations | "Are we running well? Which supplier needs intervention?" |

Both live under nav groups **Executive** and **Operations** respectively, separate from the AR daily workflow.

---

## Page 1: `/admin/performance` (CEO + CFO combined)

### Section A — Snapshot (CEO view, top of page)

One-glance health. The 3 numbers a CEO actually checks.

```
SUPPLIER PORTFOLIO
  2 active · $315k paid YTD · top concentration: PAMS 60%

HEALTH
  Outstanding payables    $21,878
  Working capital stuck   $63,842    [refunds + uninvoiced + invoiced-unpaid]
  Suppliers paying on time  100%
  Overdue refunds (60+d)    242  ⚠

RISKS
  ⚠ Refund backlog growing — 242 stuck items
  ✓ No single-supplier risk (max share 60%)
  ✓ AP aging healthy (84% current)
```

### Section B — Supplier Scorecard (CFO view, weighted grades)

One row per supplier. Sort by Grade. Click row → supplier detail.

| Supplier | Spend YTD | Grade | Bill Acc | Stmt Lag | Credit Cycle | Disputes | Phantom Bill | DPO | Vol Share |
|---|---:|---|---:|---:|---:|---:|---:|---:|---:|
| PAMS | $192,653 | A- | 97.2% | 8d | 38d | 1.2% | 2.1% | 14d | 60% |
| Counselman | $122,614 | B+ | 94.8% | 12d | 45d | 0.0% | 0.0% | 26d | 40% |

**Weighted grade formula (tunable per company):**
- Billing accuracy 30%
- Credit cycle 20%
- Statement timeliness 20%
- Phantom billing 15%
- Dispute rate 15%

Each metric mapped to a 0-100 score, weighted, summed, lettered A/B/C/D/F.

### Section C — Cash Flow Forecast (CFO view)

```
NEXT 7 DAYS
  Counselman ~$29k (monthly cadence, due ~7/10)
  PAMS       ~$5k  (weekly cadence)
                  ──
                  ~$34k

NEXT 30 DAYS
  PAMS       ~$25k (4 weekly batches)
  Counselman ~$30k (1 monthly batch)
                  ──
                  ~$55k
```

Numbers from `supplier_configs.payment_cadence` + outstanding + run-rate.

### Section D — Concentration (CEO view)

Simple table — flags if any supplier > 40% (yellow) or > 60% (red).

```
PAMS         $192,653  60%  ⚠
Counselman   $122,614  40%
─────────────────────
                       100%

Concentration risk: MEDIUM (top supplier 60%)
```

### Section E — Year-End hooks (CFO view, surfaced in December)

Three downloadable reports surfaced via buttons:
- **1099-MISC export** (per-supplier total paid)
- **COGS by supplier** (purchases − credits)
- **YoY trend** (this year vs prior)

---

## Page 2: `/admin/operations-scorecard` (DoO view)

Different from the CFO scorecard — measures **process**, not money.

### Section A — Per-supplier Operations Grades

| Supplier | Cancel Rate | Stmt Lag | Refund Cycle | Disputes | Restock Leakage | Ops Grade |
|---|---:|---:|---:|---:|---:|---|
| PAMS | 5.9% | 8d | 38d | 2 | $1,795 | B |
| Counselman | 2.1% | 12d | 45d | 0 | $0 | A- |

**Metrics:**
- **Cancellation rate** — % of `feed_buy_prices` items where `orders_items.current_status IN (cancelled set)` / total shipped
- **Statement lag** — avg days between invoice creation and statement appearance
- **Refund cycle** — avg days from `return_label_date` to `credit_received_date`
- **Dispute count** — items with `order_notes.tag='dispute'`
- **Restock leakage** — sum of `|unit_price × qty| - |line_total|` on credit rows (supplier withheld vs gross credit)

### Section B — AR Team Throughput

Last 7 days / 30 days:
```
Mohit         12 notes, 47 refunds chased
Uday           8 notes, 22 refunds chased
Dan            2 notes, 0 refunds chased

Pace: 22 notes / week — workload growing 12% MoM
```

Lets DoO see if AR is keeping up with supplier growth.

### Section C — Open Issues by Supplier

Cross-supplier rollup of every "needs human" item (uninvoiced+old, overdue refunds, disputes):

```
PAMS        14 items needing attention
Counselman  37 items needing attention
```

Click row → filtered to that supplier's open items.

### Section D — Process Trends (deferred)

12-week chart of cancellation rate, statement lag, refund cycle. Requires the
snapshot table (see below) to have a few months of history.

---

## What's needed before all of this works perfectly

### Snapshot table (set up ASAP)

For trends to work, we need to capture daily state. Build a small
`supplier_daily_snapshot` table + daily cron:

```sql
CREATE TABLE supplier_daily_snapshot (
  id, supplier_code, snapshot_date,
  outstanding, unpaid_invoices, unused_credits,
  refund_count, refund_value,
  uninvoiced_count, uninvoiced_value,
  total_paid_to_date, ...
);
```

Cron at midnight inserts one row per supplier. After ~30 days you have enough
history for trend arrows in Performance. After ~90 days you have enough for
"is supplier X getting worse?" detection.

Without this, all "vs last week" / "trending up/down" UI is fake.

### Statement upload flow

Several DoO metrics (statement lag, dispute count from statement reconciliation)
become real only when statements flow through `StatementProcessor` + `supplier_statement_lines`
populated. Today this is empty because all uploads have been one-time catchups.

When the first real PDF flows, these metrics start working.

### Order notes maturity

`order_notes.tag='dispute'` exists but is sparsely used today (1 note as of
writing). Dispute rate becomes meaningful as AR adopts the notes system.

---

## Build order recommendation

1. **Performance page** — high-value, can build today with current data (no trend arrows yet, just current values)
2. **Operations Scorecard** — same caveat (current values; trends later)
3. **Snapshot cron + table** — invisible but starts collecting history immediately
4. **Performance: add trend arrows** — once 4+ weeks of snapshot data exists
5. **Operations: process trend charts** — once 8+ weeks of snapshot data

---

## Mapping current data → metric computation

This section is for whoever builds these — exact queries.

### Billing accuracy
% of POs where `|sum(feed_price) - sum(invoice_buy_prices.line_total where line_total>0)| ≤ $1`, scoped to a date window (last 90 days).

### Credit cycle time
```sql
SELECT AVG(DATEDIFF(credit_received_date, return_label_date))
FROM expected_refunds
WHERE supplier_group = ? AND status = 'received'
  AND credit_received_date >= NOW() - INTERVAL 90 DAY;
```

### Phantom billing rate
Invoices for cancelled orders / total invoices. After our cleanup the rate should be ~0; track to detect new occurrences.

### DPO (Days Payable Outstanding)
```sql
SELECT AVG(DATEDIFF(paid_date, invoice_date))
FROM invoice_buy_prices
WHERE supplier_group = ? AND payment_status = 'paid'
  AND paid_date >= NOW() - INTERVAL 90 DAY
  AND line_total > 0;
```

### Restock leakage (DoO)
```sql
SELECT SUM(ABS(unit_price * qty) - ABS(line_total))
FROM goparts.`supplier_charges-pamsauto`
WHERE line_total < 0 AND ABS(unit_price * qty) > ABS(line_total);
```

### Cancellation rate (DoO)
```sql
SELECT
  SUM(CASE WHEN oi.current_status IN (7,44,45,49,51) THEN 1 ELSE 0 END) / COUNT(*) AS rate
FROM goparts.orders_items oi
WHERE oi.supplier_id IN (...)
  AND oi.created_at >= NOW() - INTERVAL 90 DAY;
```

### Weighted grade
```php
$score = 0
  + (clip($billingAccuracy / 100, 0, 1)) * 30
  + (1 - clip($creditCycleDays / 60, 0, 1)) * 20
  + (1 - clip($statementLagDays / 30, 0, 1)) * 20
  + (1 - clip($phantomRate / 0.05, 0, 1)) * 15
  + (1 - clip($disputeRate / 0.05, 0, 1)) * 15;

return match(true) {
  $score >= 90 => 'A',
  $score >= 80 => 'B',
  $score >= 70 => 'C',
  $score >= 60 => 'D',
  default => 'F',
};
```

Plus letter modifiers (A+ / A- / B+ / B- etc.) based on +/- 3 from boundary.
