7.6 KiB
CLAUDE.md — HQ Ops Console
This repo (C:/SiMS/hq) is the internal console for running our own software business —
NOT software we ship to clients. It manages our ~300 existing Classic clients: client book,
sellable modules with a dated price book, documents (quotation / proforma / invoice / credit
note) on editable letterhead templates with PDF rendering and shareable public links,
call/interaction tracking, AMC contracts, AWS cost recovery, payments + allocations,
recurring plans, and reminders emailed via Gmail with bounce handling — plus reports, a
dashboard, an append-only audit log, an APEX importer, and a scheduler.
The Store/POS retail product is a SEPARATE repo. It is local-first software installed in shops. Do not add Store/POS/counter/retail features here, and do not reference them. This app is vendor-side cloud only (the "HQ Console" of doc 11, started early per D15).
Layout
| Part | Where | Notes |
|---|---|---|
| Backend | apps/hq/src |
Express over better-sqlite3, DB at data/hq.db (WAL) |
| Frontend | apps/hq-web/src |
React + Vite. Home card launcher is the landing (/); Dashboard (My Day) is at /dashboard (D30). Pages: Home / Dashboard / Pipeline / Reminders / Tickets / Onboarding / Clients / ClientDetail / Documents / NewDocument / DocumentView / Renewals / Modules / Module data / Portals / Reports / MIS cockpit (owner-only) / SMS credits / Employees (owner-only) / Profile / APEX Import (owner-only) / Audit log (owner-only) / Settings. Theme = light/dark, 7 accents, 4 neutral palettes (Warm/Slate/Graphite/Zinc), Geist font -- all in Settings > Appearance (D29) |
| Repos | apps/hq/src/repos-*.ts |
one file per domain; plain functions over a DB handle |
| Schema | apps/hq/src/db.ts + apps/hq/schema.sql |
CREATE TABLE IF NOT EXISTS + additive migrate() |
| Shared packages | packages/* |
trimmed forks — see below |
Shared packages (generic on purpose — keep them so): @sims/domain (ids/UUIDv7,
money/paise, business-day, doc-series, gstin, documents), @sims/auth (scrypt PIN
- lockout),
@sims/billing-engine(compute,tax),@sims/ui. HQ owns its own document/payment types (quotation, proforma, credit note; NEFT/cheque/UPI modes) — do not push those into the shared packages.
Non-negotiable rules
These are the house hard rules (docs/16-PROJECT-RULES.md, docs/07-DB-AND-CONFIG.md)
distilled to what applies to HQ. A change that breaks one does not land.
| # | Rule | In this repo |
|---|---|---|
| Money is integer paise | Every amount is Paise (integer). Fractional-rupee floats never enter the domain; convert at the edge with fromRupees, format with formatINR. Every rounding step is explicit. |
all *_paise columns; @sims/domain/money |
| One billing engine, re-verified | GST is computed only through @sims/billing-engine computeBill — save and preview run the same path so they cannot drift. The server recomputes to the paisa; a mismatch is rejected. |
repos-documents.ts |
| Config over code, dated | Anything that varies — prices, tax rates/slabs, settings, doc-series prefixes, SAC codes, templates, message text — is a DB row resolved by business date, never a constant in code. A price or rate change is a new dated row, not a release. | module_price_book, tax_class (effective_from/_to), doc_series, setting, template, reminder_schedule (reminder cadence + follow-up text, resolved by resolveSchedule), reminder-templates |
| Documents immutable once issued | Issuing assigns the number; after that the document is never edited or deleted. Corrections are credit notes (own per-FY series, links the invoice via ref_doc_id, CGST Act s.34). Cancel keeps the number consumed; only issued, unpaid, unallocated docs may cancel. QT→PI→INV convert by carrying lines forward into a new draft. |
repos-documents.ts — issueDocument, raiseCreditNote, cancelDocument, convertDocument |
| Portable-repo pattern | All data access is plain exported functions taking the DB handle first arg — no ORM, no repo classes, no ambient singletons. Multi-write operations wrap in db.transaction(...). Keep SQL portable (SQLite today, Postgres is the locked production engine — D15); avoid engine-specific SQL. |
every repos-*.ts |
| Append-only audit | Every mutation calls writeAudit(db, userId, action, entity, id, before, after) in the same transaction as the change. The audit log is append-only — never update or delete rows. Previews and the public share route write nothing. |
audit.ts |
| Client id is the future tenant id | client.id is a client-generated UUIDv7 that becomes tenant_id when the cloud tier stands up — same rows, promoted once. Never re-key clients. |
repos-clients.ts |
| No silent caps | Every list that can grow paginates or warns; never silently truncate. | reports, directory queries |
| Strict TS, green before landing | Strict TypeScript everywhere; npm run typecheck and npm test both clean before anything lands. Money math is locked by tests — a rounding change without a test change does not land. |
tsc, vitest (416 tests) |
Running it
npm install # workspace root, once
# backend (serves API on :5182, and the built web UI + public /share links)
# RUN FROM THE REPO ROOT — the DB path resolves to ./data/hq.db from CWD, so
# `cd apps/hq && npm start` would boot a fresh empty DB in apps/hq/data instead.
npm run build --workspace @sims/hq
node apps/hq/dist/server.cjs # first boot on an empty data/ prints a one-time owner password
# frontend dev (Vite on :5183, proxies /api and /share to :5182)
cd apps/hq-web && npm run dev
- Owner login on first boot:
admin@tecnostac.com+ the one-time password printed in the server log (change it immediately). The "Gmail disconnected" banner is expected until Gmail is connected. - Env:
DATABASE_URL(set → Postgres engine, absent → SQLite atHQ_DATA_DIR; D19),SIMS_PIN_PEPPER(set once, never change),GOOGLE_*(Gmail),AWS_*(Cost Explorer),HQ_PORT(default 5182). - Credentials are stored in the clear (D31/D32).
HQ_SECRET_KEYwas removed — portal / gateway passwords, module secrets, client DB passwords and the Gmail token are plaintext, usable keyless (login passwords stay scrypt-hashed). A DB dump exposes them, so protect the DB file and its backups. Back up withnpm run backup(D33 →pg_dumpto./backups). - SMS gateway (D28): the SMS module's per-client balance is pulled live from the provider
panel (
sms.balance_api_url, keyless) — manual "Refresh from gateway" + a daily scheduler pass; low balances surface on the SMS credits screen, cockpit tile and dashboard alert. - Postgres integration tests:
HQ_PG_TEST_URL=postgres://hq:hq_dev@localhost:5432/hq_test npm test(wipes that database each run — never point it at real data). - Health check:
GET /api/health→{ ok: true }. - Verified state:
npm install+ typecheck clean, 416 tests pass. Exercise real flows end to end before claiming done.
Docs (kept in docs/)
14-SPEC-HQ-CONSOLE.md (the HQ spec — read first), 11-ADMIN-SUPPORT-CONSOLE.md (the console
this grows into), 16-PROJECT-RULES.md (hard rules), 07-DB-AND-CONFIG.md (config-over-code +
DB strategy), 06-DECISIONS.md (every decision has a D-number — add one when you decide
something), DEPLOY-HQ.md (go-live runbook). Follow The Tables Rule: anything that can be a
table IS a table, in docs and UI.