# 14 — SPEC: HQ Console (internal ops app) — started early Founder requirement (2026-07-09): an **internal tool for running our own software business** — ~300 existing clients, 6 sellable modules, quotations/proforma/invoices "in minutes", recurring bills with auto/manual email reminders, per-module payment + AMC tracking, per-client AWS usage & cost, and the full client memory (installation, training, visits, calls, meetings, sales outcomes) that today lives partly in an Oracle APEX app and partly in people's heads. **Decision (D15, [06-DECISIONS.md](06-DECISIONS.md)):** this is the [doc-11 HQ Console](11-ADMIN-SUPPORT-CONSOLE.md) **started early** — built in this monorepo as a new cloud app, aimed first at the *current Classic client base*, replacing the APEX app entirely. Roughly 70% of the requirement (tenant directory, subscription state, renewals pipeline, dunning, our own GST invoices, support notes) was already specified in doc 11; this spec adds the missing 30% (quotations/proforma from letterhead templates, AMC, AWS cost attribution, interaction/visit logs) and re-sequences delivery. Approved via brainstorm 2026-07-09. Alternatives rejected: separate codebase (rebuilds GST engine/UI/patterns and builds doc-11 twice); off-the-shelf CRM (no module-level tracking against our catalog, no AWS attribution, no path to the fleet console). ## 1. Shape | Aspect | Call | |---|---| | App | `apps/hq` — Node server + web app + JSON API, same pattern as `apps/store-server` | | Hosting | Small AWS instance (Mumbai), HTTPS; store product stays local per D14 — this is vendor-side cloud, which doc 11 already prescribes | | DB | SQLite (WAL) behind portable repositories (`repos.ts` pattern); nightly + on-change backup to S3; restore drill scheduled. **This database is the nucleus of the future cloud tier** — at HQ-4 the tenant registry grows *from* it (same rows, same UUIDs), never beside it; there is never a second client list to reconcile. Consider swapping the engine to Postgres at AWS deploy time (cheap behind the repos; removes even the engine migration later) | | Reuse | `@sims/billing-engine` **compute engine** (our invoices are GST documents — computed to the paisa), `@sims/domain` money/FY/UUIDv7 helpers + doc series (**generalized in HQ-1**: scope becomes an opaque key, separator/width configurable, 16-char GST rule kept — today it is per-counter), `@sims/ui` (design system), `@sims/auth` patterns (scrypt, lockout). HQ defines its **own document/payment types** — quotation/proforma/credit-note and B2B payment modes (NEFT/cheque) don't exist in the retail document model; shared packages stay generic (D15) | | Auth | Email + password (scrypt), optional TOTP for owner; roles: **Owner** (everything incl. pricing/finance), **Staff** (operations; finance edits owner-gated). Small team — same people do sales, accounts, support | | Audit | Every action logged — same discipline as the store product | | Email | **Gmail API on the company mailbox** (OAuth app published to **Production** status; the grant belongs to the company mailbox account, refresh token stored encrypted): mails send as us, appear in Gmail Sent, replies land in the normal inbox; every send logged against its document. **Token death is a first-class failure**: on `invalid_grant` auto-sends halt, pending items flip to the manual queue, a persistent dashboard banner + a non-Gmail fallback alert reach the owner, and one-click **Reconnect Gmail** re-authorizes | | PDFs | HTML/CSS templates matching the existing APEX/letterhead designs, rendered server-side (headless Chromium); stored on disk + S3 | ## 2. Data model (core tables) - **client** — the registry, imported from APEX: name, GSTIN, address, multiple contacts, status (lead / active / dormant / lost), notes. *Identity rule for the doc-11 future: `client.id` (UUIDv7) becomes/links `tenant_id` when the cloud tenant registry stands up (HQ-4) — same records and ids, promoted once, never re-entered; lead/dormant/lost states stay HQ-only.* - **module** + **module_price_book** — the 6 sellable modules, editions, dated prices (a price change is a row, not a release — D5 philosophy; named apart from D5's product `price_book` deliberately). `module` declares its **allowed subscription kinds** and whether a client may hold **multiple concurrent subscriptions** of it (a per-module flag — some modules allow several, others exactly one). At HQ-4, SiMS Next plans join this catalog carrying their D5 `plan_code`, so licensing stays single-sourced. - **client_module** — the heart. Per client × module: lifecycle status (*quoted → ordered → installing → installed → trained → live → renewed / expired / cancelled*), key dates (installation started/completed, payment received, training given), subscription kind (one-time / monthly / yearly / usage-based — must be one the module allows), current plan, next renewal date. Where the module permits multi-subscription, several active rows per client × module may exist; Client 360° rolls them up into one module line. - **document** — quotation / proforma / invoice / receipt / **credit note** (the GST-compliant correction path: a credit note with its own per-FY series and negative lines links to the invoice it amends — issued invoices are never edited or deleted, CGST Act s.34). Per-FY number series per type (e.g. `QT/26-27-0001`), line items priced from module_price_book (editable), GST via billing-engine, stored PDF, status trace (*draft → sent → accepted → invoiced → paid / part-paid / lost / **cancelled*** — a cancelled invoice keeps its number consumed in the series). One-click conversion QT → PI → INV carries lines forward. - **payment** — mode + reference, with a **payment_allocation** child (payment × invoice, optionally per line): default allocation oldest-invoice-first, pro-rata across its lines, overridable at entry — this is what makes the per-module paid/not-paid view deterministic. A **TDS-deducted** field treats amount + TDS as full settlement (the 194J reality of Indian B2B); unallocated payments sit as **advances** applied to future invoices. - **recurring_plan** — scope (client or client_module), cadence, amount or usage formula, next_run, policy: **auto-send** or **queue-for-manual-send**. - **amc_contract** — client, coverage, period, amount, renewal date. AMC renewals are **invoiced as documents**; paid status is *derived* from linked payments (a manual flag exists only for imported legacy contracts) — one source of truth, no drift. - **interaction** — typed from a user-extensible lookup (seeded: call / site visit / training / courtesy meeting / committee meeting / demo / complaint): date, who, notes, outcome (positive / neutral / negative), follow_up_date → feeds the daily follow-ups queue. *The "memories into the system" table.* - **aws_usage** — per client per month: data stored/transferred, cost ₹ (see §5). - **email_log** — every outbound mail: recipient, subject, linked document, Gmail message-id, status. Full traceability. - **audit** — everything. ## 3. Flows **Quotation in minutes:** New Quotation → type-ahead client → tick modules/plans → prices prefill from module_price_book (editable) → GST auto-computed → live PDF preview on letterhead → Send (Gmail) or Download. Same flow for proforma & invoice; conversions are one click. Recording a payment marks the invoice (part-)paid; optional receipt. **Reminders & recurring:** a daily scheduler scans `recurring_plan.next_run`, overdue invoices, renewals due in N days, AMC expiring, follow-ups due. On next_run it **generates the invoice** (lines from the plan's amount or usage formula, GST via billing-engine, letterhead PDF) and then auto-emails or queues it per the plan's policy; next_run advances only once the invoice exists. Each rule configurable: auto-send the templated email, or queue on the dashboard for one-click manual send — both modes exist per the founder requirement. **Scheduler semantics:** an idempotency key per (rule, due-period) makes every send at-most-once across crashes and catch-up after downtime; a failed send never advances the schedule — the item lands in the manual queue with its error visible; a bounce-detection pass polls the mailbox for delivery-status notifications and flags stale client contacts on the dashboard ("sent" ≠ "delivered"). Dashboard home = today's money: overdue, due this week, renewals this month, follow-ups today, recent payments. **Client 360°:** one page per client — modules with status timeline, all documents with payment state, AMC, renewals, every interaction, AWS usage trend. The whole relationship in one scroll. ## 4. APEX migration (replace fully) One-time importer reading the APEX app's Oracle tables (node-oracledb thin, same as D12 tooling) → staging → verification report (counts + eyeballed samples) → import clients, module assignments, statuses, open dues, and historical bills as history-only documents (no PDFs regenerated). **Trial run, verify, then final cutover; APEX goes read-only.** No double entry at any point. **GST series continuity at a mid-FY cutover:** each per-FY document series is **seeded from the last APEX-issued number** of the current FY (or switches to a distinct new prefix — confirm with the CA, same session as Open item 4). Imported documents carry a `source=apex` flag, keep their original numbers, and are exempt from the new-series constraint while still collision-checked. ## 5. AWS usage & cost per client — automated Nightly job pulls AWS Cost Explorer (cost-allocation tags per client) plus storage/ bandwidth metrics for shared resources, allocating shared cost proportional to usage. Stored monthly per client; Client 360° shows usage trend and **cost vs. what they pay us** (margin per cloud client). **First implementation step is a feasibility check:** how cleanly are current cloud resources separable per client? Where tagging is impossible, that portion falls back to proportional allocation — never manual entry as the primary mechanism. HQ-3 also ships the **cross-client AWS cost chart**: clients ranked by monthly cost and share of total, each client's rank surfaced on their Client 360° — the founder's "where do they sit in the AWS cost chart" view. ## 6. Phasing (mirrored in [04-ROADMAP.md](04-ROADMAP.md)) | Phase | Ships | |---|---| | **HQ-1** | App skeleton + auth + audit; client registry (APEX import + verification); module catalog + module_price_book; client_module tracking; **quotation/proforma/invoice generation + letterhead PDF + Gmail send + payment recording (allocation + TDS + advances) + document traceability**; credit notes + cancelled status; per-FY series seeded from APEX at cutover; Gmail token-death handling | | **HQ-2** | Recurring plans (invoice generation on next_run); reminder engine (auto + manual queue, idempotent, bounce-aware); AMC contracts; dashboard (money view + follow-ups); interaction/visit/call/training log | | **HQ-3** | AWS usage & cost automation + margin view + cross-client cost chart (rank/share); reports (dues aging, module-wise revenue, client profitability); Client 360° polish | | **HQ-4** | Convergence with doc-11 fleet features when SiMS Next ships: migration tracker, fleet dashboards, support queue — on the same client registry, no rebuild | All founder-listed capabilities land somewhere in HQ-1…4; phase numbers order delivery, nothing is dropped. HQ-1's headline is the founder's stated top pain: documents in minutes. ## 7. Non-goals (v1) Dealer portal; impersonation; client-facing portal; WhatsApp sending (email first — the message templates live in one place so WhatsApp can be added as a channel later); double-entry accounting (payments feed reports, books stay in the existing accounting tool); mobile app (the web app must simply work well on a phone browser). ## 8. Open items 1. **AWS tagging feasibility check** (gates HQ-3 automation depth). 2. **Template capture**: export the current APEX/letterhead quotation + invoice layouts so the HTML templates match them. 3. **Gmail OAuth setup**: Google Cloud project **published to Production**, consent granted by the company mailbox account (owner action; the grant — and any revocation — follows that account). 4. **CA session**: SAC codes per module, place-of-supply for out-of-state clients (IGST — the engine already handles CGST/SGST vs IGST), credit-note fields, and the mid-FY series-continuity call (seed from APEX vs new prefix). 5. **APEX schema walk-through** with whoever maintains it, before writing the importer.