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

86 lines
7.6 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 at `HQ_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_KEY` was 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 with `npm run backup` (D33 → `pg_dump` to `./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.