Builds out subscription credentials on top of the rename.
- Operator chooses the credential type; only QR is live (RFID shown disabled
"soon"). Backend/schema keep accepting both — re-enabling RFID is UI-only.
- QR codes are AUTO-GENERATED server-side (SUB-<base32>, crypto-random,
globally-unique-checked) — the customer/operator never picks the value.
RF stays operator-entered (the physical card id). Reader output decided =
TCP/IP full string (Wiegand-numeric fallback noted).
- Multi-month: form takes a `months` count → server sets validTo =
validFrom + N months (day-clamp); one record/one window; total = N×monthly.
- The QR card is PRINTED so the operator can hand it over: real ESC/POS 2D QR
(GS ( k) added to the Rongta driver (printSubscriptionCard); auto-print on
create (best-effort — never fails the create; returns {printed,printError})
+ reprint via POST /api/subscriptions/:id/print and a "Print code" button.
Verified via buildServer+inject incl. a TCP capture of the on-wire QR bytes
(autogen+uniqueness, Jan31+3mo→Apr30, auto-print, GS ( k QR with embedded
code, reprint, no-QR→409). Updated wiki (subscription, rongta-printer). No
migration.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
The "permit/lejet" feature is really a subscription. Full rename of the
mutable master data, plus a recurring monthly price.
- DB (migration 0004, data-preserving ALTER RENAME): permits→subscriptions,
permit_credentials/_plates→subscription_*, sessions.permit_id→subscription_id.
- Pricing: per-subscription priceMinor + period(monthly) + currency, with a
site default (site_config.subscription_monthly_price_minor) pre-filling the form.
- Server: subscription-flow.ts (SubscriptionFlow), routes/subscriptions.ts
(/api/subscriptions). Web: SubscriptionManager, route, i18n (sq Abonimet/en).
- The signed ledger `permitId` payload is intentionally kept — immutable
hash-chained history; renaming it would break verification of past events.
Deferred (wiki notes): fee collection into the ledger/shift (a shift-attributed
payment), LPR/ANPR plate source, time-of-day access windows (overnight subscriber).
Also carries the device-footer UI surface (api DeviceStatus, router mount,
i18n devices) due to shared-file overlap with the preceding footer commit.
Verified end-to-end on a fresh DB and migration on a live-DB copy (sessions
preserved). Live DB migrated. Full monorepo builds clean.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V