14 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 as a new cloud
app (in the shared monorepo at the time; now its own repo C:/SiMS/hq, split
2026-07-16), 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 the store-server app (now the separate Store repo) |
| 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 |
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/linkstenant_idwhen 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_bookdeliberately).moduledeclares 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 D5plan_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 amodule_price_bookrow whoseeditionis 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, in the Store repo)
| 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).
Delivered beyond this phasing (2026-07-17, D16 — the quote-to-close funnel slice):
employee management with owner/manager/staff roles, account-owner routing on clients, the
cross-client Pipeline chase-list (derived stages, no new pipeline storage), escalating
quote follow-up reminders (owner nudge + client email at dated 3/7/14 offsets), invoice
overdue escalation at dated d7/d15/d30 milestones, one-click proforma→invoice
convert-and-send, proforma supersede, and the module→client roster with Notify-all and
CSV export — plus the warm ops-console UI redesign. Spec:
superpowers/specs/2026-07-17-quote-to-close-funnel-design.md; record: D16 in
06-DECISIONS.md; current state: ../STATUS.md.
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
- AWS tagging feasibility check (gates HQ-3 automation depth).
- Template capture: export the current APEX/letterhead quotation + invoice layouts so the HTML templates match them.
- 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).
- 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).
- APEX schema walk-through with whoever maintains it, before writing the importer.