You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/docs/14-SPEC-HQ-CONSOLE.md

13 KiB

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): this is the doc-11 HQ Console 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. Postgres is the locked production engine (founder call 2026-07-10): the swap is a dedicated task sequenced AFTER HQ-2 and BEFORE the real-data import and go-live, so the 300 clients land once, directly in the final engine; SQLite remains the dev/test engine behind the same repositories. DDL snapshot for mapping work: apps/hq/schema.sql
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. Two founder additions (2026-07-10): quote content — an optional per-module block of "what's included" lines that flows into quotations under that module's line (editable per document; modules without content print nothing extra) — and packs: named purchasable variants offered as a pick-list, e.g. the SMS module sold as SMS 50,000 or SMS 1,00,000 packs. A pack is a module_price_book row whose edition is the pack name; the composer shows the module's packs with prices and the chosen pack name prints on the document line.
  • 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)

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.

HQ-1.5 (immediate follow-up to HQ-1): per-module quote-content blocks and pack pick-lists (SMS 50k / 1L style) in the composer and on the PDF (spec §2 module bullet).

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.