# TechTitans POS — Customer CRM Phase C4 Overview Redesign

**Date:** 2026-11-12 · **Branch:** `techtitans/integration` (backend + frontend) · Local commits only (no push/deploy).
**Builds on:** C1 legacy audit, C2 (search/phone/duplicates), C3 (premium profile + human reference). Primarily a UI/UX upgrade of the overview + one additive finance read-model. No legacy PHP executed, no legacy DB migrated/modified, NH POS untouched, nothing pushed. Existing architecture reused throughout.

## 1. Verification (done first, as required)

### What the old portal's Sales / IPTV / Repairing / Balance figures mean
Read `oldportal/admin/subadmin/profile.php` (static). Each line is **DUE / TOTAL** (outstanding first, grand total second):
- **Sales** (line 11/15): `SUM(due) / SUM(grand_total)` over `sales_details WHERE tbl_name='sales_details' AND status!='RETURNED'`.
- **IPTV** (line 17–25): `SUM(due where due>0 & ACTIVE) / SUM(grand_total)` over `sales_details WHERE tbl_name='iptv_details'`.
- **Repairing** (line 27–31): `SUM(due) / SUM(final_cost)` over `repairing_details`.
- **Balance** (line 115): `customer_details.balance` — a single store-credit figure.

### Modern data sources (verified)
- POS sales and IPTV charges are **both `orders`** in the one ledger — distinguished by `order_source`: IPTV renewals create **`SYSTEM`** orders (`app/Iptv/Billing/OrderPipelineBilling.php:55`), retail is **`POS`**. So "IPTV outstanding" vs "Sales outstanding" is a server-side split by source, not a parallel ledger.
- **Repairs have NO billing in R1** — `repair_tickets` migration says *"Billing/estimate/warranty/attachment columns are deliberately out of scope for R1"*. Repair outstanding is therefore **genuinely unavailable** and is reported as `n/a`, never a fabricated 0.
- **Store sales** = `SUM(grand_total)` over the tenant's non-cancelled orders in the primary currency, within the caller's location scope — feasible and indexed.
- **Last visit** = the single most-recent real interaction of any kind (order/payment/repair/IPTV), computed from the composed activity feed — no synthetic timestamps.
- CRM **relationships** (`customers/{id}/relationships` + types) and **groups** (`assign/unassignGroup`, `crm-options`) already exist and are reused for Family / Group actions.

## 2. Backend (additive, read-only)
`CustomerFinancialService::overview()` + `GET /customers/{id}/finance-overview` (payments.view-gated, 403 without; orders bounded to orders.view scope). Reads the SAME orders + payments tables (never a second ledger); applies the same "received = captured − refunded (real modes), cancelled excluded" rule. Returns a single primary-currency snapshot:
- `sales` / `iptv` lines each as `{ total, paid, outstanding }` (mirrors the legacy **due/total**: `outstanding` of `total`), split by `order_source`.
- `total` (sales+iptv), `repair.billing_available=false`, `store_sales_minor`, `contribution_pct`, `currency`, `multi_currency`.

## 3. UI changes

### Tabs (simplified)
`Overview · Sales · Repairs · IPTV · Appointments · Activity`. Addresses & contacts, CRM and Business/Financial are **not removed** — they're reachable from the Overview (Customer information → "Contacts & addresses"; Family & groups → "CRM details"; Financial detail folded into the Overview finance panel) as secondary views with a "Back to overview" link. Icon tabs, active state, count badge on Sales.

### Hero header
Name · **CUST- reference** (never the UUID) · type · status · phone · email · customer since · **last sale** · Edit customer · New sale.

### Summary cards row (legacy-parity due/total)
- **Sales** (blue): retail order count + `Due/Total` (POS orders).
- **Repairs** (purple): open / total tickets + `billing n/a` (honest — no R1 billing).
- **IPTV** (emerald): active / total connections + `Due/Total` (SYSTEM orders).
- **Balance** (coral/green): overall outstanding.

### Last-activity highlights
**Last sale · Last visit · Last repair · Last IPTV** — each a real timestamp from the module ledgers.

### Finance panel + contribution chart (the §4/§5 asks)
Total sales · Total paid · Total outstanding · IPTV outstanding · **Repair outstanding (n/a)** · Overall balance — all from the authoritative snapshot. A two-bar **contribution chart** (Customer vs Store sales) with the percentage: *"This customer represents N% of CAD store sales."* Real figures only.

### Family & groups (§6)
Clear overview actions: **Add family member** (reuses the CRM relationship drawer — creates/links another customer + stores the relationship type; never auto-merges) and **Group customer** (new drawer reusing the CRM customer-groups assign/unassign; multiple customers can share a household/business/group). No duplicate structures.

### Activity tab (§8)
A filterable timeline combining **Sales / Repairs / IPTV / Payments / Appointments / Notes** — real records with links and timestamps; filters show only kinds that have data (so nothing fake). Appointments will populate once its backend exists (§9).

### Appointments tab (§9)
The primary tab exists now; the scheduling **backend does not**. Rather than fake it, the tab shows an honest placeholder. **Required next step (documented, not built):** an `appointments` table (customer_id, location_id, scheduled_at, duration, status, assigned_employee_id, notes) + CRUD API + reminders, feeding the activity timeline (an `appointment` activity kind is already wired on the frontend).

## 4. Security / preservation
Tenant isolation, location scope, customers/repairs/IPTV/payments permissions, identity rules, C2 search/phone/duplicate logic, financial calculations and existing routes/contracts are all unchanged. The finance overview is payments.view-gated (403 without); no passwords/credentials/attachments are exposed; money is never recomputed outside the ledger.

## 5. Testing

### Backend (`php artisan test --filter=Customer`) → **109 passed**
New `CustomerFinancialTest` cases: *finance overview splits sales vs IPTV and computes store contribution* (POS 1000/due 600, IPTV 2000/paid, other customer 3000 → store 6000, contribution 50%, repair billing unavailable) and *finance overview is gated by payments.view* (403). Pint + PHPStan clean on all changed files.

### Frontend → **62 customers+iptv tests passed**
`CustomerProfilePage.test.tsx` updated for the C4 overview (Sales label, finance panel + contribution text, New sale, CRM-via-overview). `tsc -b`, `eslint`, `npm run build` clean.

### Live Playwright (real dev UI)
Primary tabs = **Overview · Sales · Repairs · IPTV · Appointments · Activity**; finance chart renders with contribution %; Group-customer drawer opens; **no horizontal overflow** at 1920/1440/768/390 and zoom-emulated 125 %/150 %/200 %; **0 console errors**. Screenshots: `screenshots/customer-crm-c4/` (`c4-overview`, `c4-appointments`, `c4-group-drawer`, `c4-mobile`, plus `c3-overview-before`).

## 6. Before / after
- **Old portal:** due/total text rollups + store-credit balance, session-bound, N+1 queries.
- **C3:** premium profile, 4 service cards, composed activity, 9 tabs.
- **C4 (now):** simplified 6-tab nav, legacy due/total cards, last-activity highlights, **finance panel + store-contribution chart**, clear family/group actions, appointments tab stub.

## 7. Remaining gaps
- **Appointments backend** (table + API + reminders) — documented above; the tab + activity kind are ready for it.
- **Repair billing** — R1 has none; once repair estimates/payments land, the Repairs card + finance panel can show real repair outstanding (the UI already labels it `n/a`).
- **Store-credit balance** — the legacy `customer_details.balance` has no modern equivalent; "Balance" currently means overall outstanding. A true store-credit ledger is a future increment.
- **Multi-currency finance** — the overview shows the primary currency and flags when others exist; a per-currency breakdown could follow.
- **Server-side audited activity endpoint** — the timeline is still composed client-side from the module ledgers (adequate + honest); a single audited endpoint would add create/update events + employee attribution.

## 8. Commits (local only — no push)
- **Backend:** this commit — finance-overview service/endpoint + tests + this report + screenshots.
- **Frontend:** `3fa5373` — overview redesign (tabs, hero, due/total cards, highlights, finance panel + chart, family/group drawers, appointments stub).
