# Finance — Earnings + Wallet/Withdrawals + VAT — UI parity

## Demo UI primitives (`portals/finance/shared.tsx`)

`/private/tmp/Navagoo_MI_dev/navagoo-app/src/portals/finance/shared.tsx`:
- `CHARGE_META` (`:10-19`) — per-charge-type label + tone + icon for **five
  charge types**: `marketing_fee` (purple/Megaphone), `payment_processing_fee`
  (cyan/CreditCard), `subscription` (navy/Crown), `sms_fee` (amber), `wa_fee`
  (teal). `ChargeTypeBadge` (`:21-24`) renders them.
- `StatTile` (`:27-48`) — KPI card: uppercase label, big `tnum` value, optional
  caption + action. Top-accent stripe.
- `FormulaStrip` (`:54-92`) — the **settlement formula rendered as a left→right
  strip of coloured term chips**:
  `Collected + Tips − Marketing − Processing − Fee VAT = Net Payout`
  (`:50-53`), each term tinted to match its fee badge, result chip in `brand-600`,
  suffixed `SAR`. This is the signature finance visual.

Money helper `money(value, false)` + `tnum` numerals; `SAR` suffix.

## Our UI

- `frontend/views/earnings/index.php` — shop Earnings list. Tailwind "aurora"
  reskin (`EarningsController::actionIndex` sets `layout='tailwind'`, `:42`).
  - KPI tiles row (`:112-185`): **Total earnings, Bookings, Paid, Pending**, then
    **Total Amount Due to Shop, Net Amount Available for Withdrawal (+ Withdraw
    pill, gated on `cumulativeAvailable ≥ minimumWithdrawalAmount`, `:41,158-173`),
    Total Withdrawn**. Settlement-status badges (`:91-98`) cover the 6 ShopEarning
    statuses.
  - This is a **richer KPI header than the demo's StatTile usage**, but it does
    NOT render the demo's `FormulaStrip` breakdown.
- `frontend/views/earnings/view.php` — single earning detail.
- `frontend/views/agents-wallet/{index,create,_form,view,payment,view-payment,
  specialist-tips}.php` — withdrawal list, create-request form, transfer detail,
  payments, and a specialist-tips summary.

## Concrete UI gaps

1. **No `FormulaStrip`.** Ours shows a flat net-collectible number; the demo
   exposes the full `Collected + Tips − Marketing − Processing − FeeVat = Net`
   strip with colour-coded terms. No per-term breakdown anywhere in our views.
2. **No payment-processing-fee, SMS, WhatsApp, or subscription charge surfaces.**
   `CHARGE_META`'s five charge types collapse to one ("Navagoo marketing fees")
   on our side. No charge-type badges.
3. **No ledger / charges table.** Demo can list every charge row (incl. negative
   reversal rows); ours has only Earnings + ShopEarning rows, no reversal display.
4. **No grace-window / classification indicators** — nothing tells the shop a fee
   was waived (grace) or skipped (shop-owned customer).
5. **No carry-forward / "you owe Navagoo" state** in the wallet — the
   `Net Available` tile floors at the min and never shows a negative balance.
6. **Refund presentation** — `refund_value` is shown as a number; there is no
   zone (full/partial/none) explanation the demo's policy implies.

## Empty / error / RTL states

- **Ours — error/guard:** `actionCreate` flashes and redirects when no shop
  (`AgentsWalletController.php:221-228`) or when the eligible total is below the
  minimum (`:351-360`). Withdrawal `findModel` is shop-scoped → 404 cross-shop
  (`:648-656`); `actionGetBankAccountDetails` is shop-scoped (IDOR fix, `:659-675`).
  `EarningsController::actionView` 404s on cross-shop access (`:243-247`).
- **Ours — empty:** `$statistics` zero-defaults when no shop
  (`EarningsController.php:206-219`); list relies on GridView/Table empty text.
- **RTL/bilingual:** every label uses `Yii::t('backend'|'frontend', ...)` — the
  app is Arabic/English with RTL; the aurora layout handles direction. The demo is
  LTR English only, so RTL is an **ours-only strength**, not a gap. Per CLAUDE.md
  any new string must be added to both `common/messages/{ar,en}/*`.
- **Demo** states are component-level (Zustand selectors return empties; no server
  errors to model in the prototype).
