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/CLAUDE.md

6.5 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; pages Dashboard (My Day) / Pipeline / Reminders / Clients / ClientDetail / Documents / NewDocument / DocumentView / Modules / Reports / Employees (owner-only) / Profile / APEX Import (owner-only) / Settings
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.tsissueDocument, 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 (372 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 at HQ_DATA_DIR; D19), SIMS_PIN_PEPPER (set once, never change), HQ_SECRET_KEY (before Gmail), GOOGLE_* (Gmail), AWS_* (Cost Explorer), HQ_PORT (default 5182).
  • 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, 372 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.