# TechTitans POS — IPTV Legacy Parity & I2 Report

**Branch:** `techtitans/iptv-i2` (from `techtitans/iptv-i1`). Preserves the I1 foundation; adds the legacy
operational fields, the four distinct "credit" concepts, a documented billing boundary, a redesigned
account page, list/export improvements, and notification eligibility. **Not pushed.**

## Step 1 — Legacy ↔ new feature comparison

Legacy evidence: `oldportal/admin/services/iptv_services.php` (read-only), `iptv_details` schema,
`legacy-portal/04` + `06`. **Already** = I1; **Added** = this increment; **Boundary** = documented
financial seam; **Deferred** = later increment.

| Legacy feature (New Connection) | Status | Evidence / new home |
|---|---|---|
| Customer selection | Already | I1 `iptv_subscriptions.customer_id` → Customer CRM |
| User Login | Already | `account_identifier` |
| Actual User | **Added** | `actual_user` |
| MAC ID (duplicate-checked) | **Added** | `mac_id` + per-tenant dedup (legacy `getconnection:549`) |
| Portal | Already | `provider_id` (first-class provider, not free-text) |
| Subcontractor | **Added** | `iptv_subcontractors` + `subcontractor_id` (not a cascade parent) |
| Start / Payment-finishing (expiry) | Already | `start_date` / `expiry_date` (real DATE) |
| Unit Price / Monthly Charge | **Added** | `unit_price_minor` / `monthly_charge_minor` (minor units) |
| Box Model / Notes | **Added** / Already | `box_model` / `notes` + activity feed |
| Send Welcome Email / SMS | **Added (eligibility only)** | notification settings + log (no send in dev) |

| Legacy feature (Account Detail) | Status | New home |
|---|---|---|
| Free Credit (`free_for`) | **Added** | promotional **free extension** (`iptv.credit_grant`) — expiry only |
| Pay Credit (`pay_for`) / Pay Now | **Added + Boundary** | **paid renewal** via Order→Invoice→Payment gateway; `pay_now_minor` partial |
| Credit-month balance ("Credit – N months") | **Boundary** | = receivables (owed), read from the pipeline's outstanding balance (not a stored credit) |
| Bring to Current Date / Change date by | **Added** | **date adjustment** (`iptv.adjust_date`, to-today / ±days) |
| Change monthly charge | **Added** | `iptv.manage` charge change + activity note |
| Account notes & history | **Added** | `iptv_account_notes` (append-only) |
| Enable / disable | **Added** | status active↔suspended + note |
| Invoices / Payments / HST | **Boundary** | the existing Order→Invoice→Payment + `TaxClass` (config-gated, §Step 4) |
| Email/SMS options | **Added (eligibility)** | notification settings |

| Legacy feature (Connections List) | Status |
|---|---|
| Customer / User Login / Actual User / MAC / Portal / Expiry / Status | **Already + Added** (columns incl. MAC + actual user) |
| Last updated | Already (`updated_at`) |
| Phone / email (permission appropriate) | **Deferred** (PII export gating — customer_name only in CSV for now) |
| Search / filter | Already (term + state + provider) |
| CSV/Excel/PDF / Print | **Added (CSV)** — `iptv.export`, **never** exports credentials; Excel/PDF deferred |

## Step 2 — Extended account fields (additive)

Migration `2026_11_24_100002` adds `actual_user, mac_id (deduped), box_model, monthly_charge_minor,
unit_price_minor, paid_through_date, subcontractor_id`; `2026_11_24_100001` adds `iptv_subcontractors`.
Customer identity and payment records are NOT duplicated. I1 encrypted credential handling is untouched.
`paid_through_date` is distinct from `expiry_date`: it advances only on a **paid** renewal, so a free
comp moves expiry without moving paid-through (verified live: expiry 2027-01-20, paid-through 2026-12-20).

## Step 3 — Credit management (four DISTINCT operations)

Each has its own permission, audit action, and history, so the legacy "credit" ambiguity is resolved:

1. **Promotional / free extension** — `iptv.credit_grant`; `free_extension` history row; expiry moves, no
   charge, paid-through unchanged.
2. **Paid subscription renewal** — `iptv.renew`; `renewal` row with `paid_months` + charge; bills through
   the pipeline (Step 4); grant policy below.
3. **Manual date correction** — `iptv.adjust_date`; `date_adjustment` row (±days / to-today); no money.
4. **Financial customer credit** — the EXISTING customer store credit; not reimplemented (referenced only).

Monthly-charge change is a separate `iptv.manage` action (activity note). All use the existing renewal
service + idempotency; history is append-only and never rewrites invoices/payments.

## Step 4 — Invoice/payment integration (boundary)

The existing order-line pipeline requires a **catalog product/variant FK** (no ad-hoc service line) and a
restaurant `order_type` enum — so wiring is **config-gated**, not forced on, to avoid polluting the
catalog/engine. The seam is a gateway (`App\Iptv\Billing\IptvBillingGateway`):

- **DeferredBilling** (default, dev-safe): records NO payment; computes the grand total incl. tax from the
  plan's `TaxClass`; returns it fully outstanding. No parallel ledger.
- **OrderPipelineBilling** (`config('iptv.billing.enabled')` + a configured IPTV service product/variant):
  opens a SYSTEM order for the customer/location, adds the renewal line (tax from the service product's tax
  class), records the optional `pay_now` offline payment (partial allowed), issues the invoice, and reads
  the outstanding balance + payment status back from `PaymentLedger`. Order/invoice ids are stored on the
  renewal row — **no parallel IPTV payment ledger**.
- **GRANT POLICY (explicit):** an invoice alone never grants service. Service is extended only when the
  charge is **PAID IN FULL** or an **authorized override** (`authorize_unpaid`, `iptv.renew`) is supplied;
  otherwise the renewal is recorded with its billing status and the expiry is left unchanged (pending).
- Currency + tax respect the plan/POS configuration. Enablement steps are in `config/iptv.php`.

## Step 5 — Redesigned account detail

Tabbed, compact page (`frontend/src/pages/iptv/IptvDetailPage.tsx`): **Overview** (customer/connection
header, status + expiry, operational fields, credential reveal, enable/disable), **Billing & renewal**
(account summary, renewal/billing history, Renew with pay-now + authorized-override, promotional free
months, monthly-charge), **Activity** (notes feed + manual date correction). Not a clone of the crowded
legacy screen; useful operational capabilities preserved.

## Step 6 — List improvements

Added MAC + actual-user columns; search + state/provider filters (I1); **CSV export** (`iptv.export`) that
**never includes credentials**. Phone/email export is deferred pending PII gating.

## Step 7 — Notifications

Per-tenant toggles (`iptv_notification_settings`) + expiry windows (I1 reminder rules). `processReminders`
computes eligibility and records it in `iptv_notification_log` with a UNIQUE dedup key (idempotent — a
second run logs nothing new). Welcome eligibility is recorded on activation. **No message is sent in
development** (`config('iptv.notifications.send_enabled')` off); outcome is `eligible`/`suppressed`.

## Database changes

`iptv_subcontractors`; `iptv_subscriptions` +7 columns; `iptv_subscription_renewals`
+`free_months/paid_months/order_id/invoice_id/billing_status` + broadened event-type CHECK;
`iptv_account_notes`; `iptv_notification_settings` + `iptv_notification_log`; permission backfill. All
additive; applied to the dev DB via plain `migrate`; test DB via RefreshDatabase.

## APIs (new)

`POST iptv/subscriptions/{id}/{free-extension,adjust-date,charge,enabled,notes}`, `GET …/{id}/notes`,
`GET iptv/subscriptions-export`, `POST iptv/reminders/process`, `GET/PATCH iptv/config/notifications`,
`GET/POST/PATCH iptv/subcontractors`. (33 IPTV routes total.)

## Permissions

Added `iptv.credit_grant`, `iptv.adjust_date`, `iptv.export` (location-sensitive; credit/adjust are
SENSITIVE/audited). Granted to Owner/GM/Location Manager (export also to Shift Supervisor). Backfilled.

## Tests

- **Backend** `IptvOperationsTest` — **10 passed** (operational fields + MAC dedup; free extension moves
  expiry not paid-through; date adjust ±/to-today; charge change note; enable/disable; **paid renewal does
  not grant without payment/override, override grants**; **paid-in-full grants + reads outstanding from the
  pipeline** (faked gateway); credit/adjust permission gating; **export CSV has no credential**;
  notification eligibility dedup). I1 `IptvSubscriptionTest`/`RenewalCalculatorTest` **20 passed** (no
  regression); `PermissionCatalogTest` updated + green.
- **Frontend** `IptvPages.test.tsx` — **7 passed** (list incl. MAC column; new wizard; detail reveal; renew
  + free extension via the Billing tab). `tsc` ✓, `eslint` ✓; customers suite green.

## Browser verification (Playwright, real servers, techtitans)

`frontend/e2e/iptv-i2.spec.ts` passed; screenshots in `frontend/test-results/iptv-i2/`: `list.png` (MAC
column + Export CSV), `detail-overview.png`, `detail-billing.png` (account summary + renewal/billing
history showing "1 paid" vs "1 free", Renew/Free/Charge panels), `detail-activity.png`, `settings.png`
(notifications + subcontractors). Data seeded via the API (IPTV-2026-0005 with operational fields + a free
extension; expiry 2027-01-20, paid-through 2026-12-20).

## Known limitations

- Billing pipeline is **config-gated off by default** (enabling needs an IPTV service product — §Step 4);
  development uses DeferredBilling (no real charges) + the explicit override to extend service.
- Phone/email list export + Excel/PDF + Print are deferred.
- No real email/SMS is sent (eligibility only); scheduled delivery is a later increment.
- No historical IPTV data migrated (out of scope per the task).

## Commit hashes (branch `techtitans/iptv-i2`, not pushed)

- Backend: **bf14508** + the commit carrying this report.
- Frontend: see the frontend commit on `techtitans/iptv-i2`.
