docs+chore: HQ DDL snapshot, gitignored import-drop, Postgres locked pre-go-live (D15)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/client-detail-redesign
Thomas Joise 2 weeks ago
parent 7bc7976e4e
commit fbf39d7a51

13
.gitignore vendored

@ -0,0 +1,13 @@
node_modules/
dist/
*.tsbuildinfo
.DS_Store
*.log
.env
.env.*
.gstack/
apps/store-server/data/
apps/hq/data/
/data/
# Real client data staging for the APEX->HQ import — never committed
import-drop/

@ -0,0 +1,115 @@
-- SiMS HQ database DDL (generated snapshot 2026-07-10; source of truth: apps/hq/src/db.ts)
-- Engine today: SQLite. Production target: Postgres (D15) — types map 1:1 (TEXT/INTEGER).
CREATE TABLE audit_log (
id TEXT PRIMARY KEY, at_wall TEXT NOT NULL, user_id TEXT NOT NULL, action TEXT NOT NULL,
entity TEXT NOT NULL, entity_id TEXT NOT NULL, before_json TEXT, after_json TEXT
);
CREATE TABLE client (
id TEXT PRIMARY KEY, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL,
gstin TEXT, state_code TEXT NOT NULL DEFAULT '32', address TEXT NOT NULL DEFAULT '',
contacts TEXT NOT NULL DEFAULT '[]', -- JSON [{name,phone,email,role}]
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('lead','active','dormant','lost')),
notes TEXT NOT NULL DEFAULT '', source TEXT NOT NULL DEFAULT 'hq', created_at TEXT NOT NULL
);
CREATE TABLE client_module (
id TEXT PRIMARY KEY, client_id TEXT NOT NULL, module_id TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'quoted' CHECK (status IN
('quoted','ordered','installing','installed','trained','live','expired','cancelled')),
kind TEXT NOT NULL, edition TEXT NOT NULL DEFAULT 'standard',
installed_on TEXT, completed_on TEXT, trained_on TEXT,
next_renewal TEXT, active INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE doc_series (
doc_type TEXT NOT NULL, fy TEXT NOT NULL, prefix TEXT NOT NULL, next_seq INTEGER NOT NULL,
PRIMARY KEY (doc_type, fy)
);
CREATE TABLE document (
id TEXT PRIMARY KEY, doc_type TEXT NOT NULL CHECK (doc_type IN
('QUOTATION','PROFORMA','INVOICE','RECEIPT','CREDIT_NOTE')),
doc_no TEXT, fy TEXT NOT NULL, client_id TEXT NOT NULL, doc_date TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN
('draft','sent','accepted','invoiced','part_paid','paid','lost','cancelled')),
ref_doc_id TEXT, -- QT→PI→INV chain; CN → the invoice it amends
taxable_paise INTEGER NOT NULL, cgst_paise INTEGER NOT NULL, sgst_paise INTEGER NOT NULL,
igst_paise INTEGER NOT NULL, round_off_paise INTEGER NOT NULL, payable_paise INTEGER NOT NULL,
payload TEXT NOT NULL, -- JSON { lines: BillLine[], totals: BillTotals, terms?: string }
source TEXT NOT NULL DEFAULT 'hq', -- 'apex' rows keep legacy numbers, exempt from series
created_by TEXT NOT NULL, created_at TEXT NOT NULL,
UNIQUE (doc_no)
);
CREATE TABLE document_event (
id TEXT PRIMARY KEY, document_id TEXT NOT NULL, at_wall TEXT NOT NULL,
kind TEXT NOT NULL, meta TEXT NOT NULL DEFAULT '{}'
);
CREATE TABLE email_account (
id TEXT PRIMARY KEY, address TEXT NOT NULL, refresh_token_enc TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','dead')), updated_at TEXT NOT NULL
);
CREATE TABLE email_log (
id TEXT PRIMARY KEY, document_id TEXT, to_addr TEXT NOT NULL, subject TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('sent','failed')), gmail_message_id TEXT,
error TEXT, at_wall TEXT NOT NULL
);
CREATE TABLE module (
id TEXT PRIMARY KEY, code TEXT NOT NULL UNIQUE, name TEXT NOT NULL,
sac TEXT NOT NULL DEFAULT '998313', -- IT services default; CA session confirms per module
allowed_kinds TEXT NOT NULL DEFAULT '["one_time","monthly","yearly","usage"]',
multi_subscription INTEGER NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 1,
quote_content TEXT NOT NULL DEFAULT '[]' -- JSON string[]: "what's included" lines on quotes
);
CREATE TABLE module_price_book (
id TEXT PRIMARY KEY, module_id TEXT NOT NULL, edition TEXT NOT NULL DEFAULT 'standard',
kind TEXT NOT NULL, price_paise INTEGER NOT NULL, effective_from TEXT NOT NULL
);
CREATE TABLE payment (
id TEXT PRIMARY KEY, client_id TEXT NOT NULL, received_on TEXT NOT NULL,
mode TEXT NOT NULL CHECK (mode IN ('bank','upi','cheque','cash','other')),
reference TEXT NOT NULL DEFAULT '', amount_paise INTEGER NOT NULL,
tds_paise INTEGER NOT NULL DEFAULT 0, created_by TEXT NOT NULL, created_at TEXT NOT NULL
);
CREATE TABLE payment_allocation (
id TEXT PRIMARY KEY, payment_id TEXT NOT NULL, document_id TEXT NOT NULL,
amount_paise INTEGER NOT NULL -- includes this allocation's TDS share
);
CREATE TABLE session (
token TEXT PRIMARY KEY, staff_id TEXT NOT NULL, expires_at TEXT NOT NULL
);
CREATE TABLE setting (
key TEXT PRIMARY KEY, value TEXT NOT NULL
);
CREATE TABLE staff_user (
id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, display_name TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('owner','staff')),
pw_salt TEXT NOT NULL, pw_hash TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE stg_client (
row_no INTEGER PRIMARY KEY, code TEXT, name TEXT, gstin TEXT, state_code TEXT,
address TEXT, phone TEXT, email TEXT, status TEXT, problems TEXT NOT NULL DEFAULT '[]'
);
CREATE TABLE stg_invoice (
row_no INTEGER PRIMARY KEY, client_code TEXT, doc_no TEXT, doc_date TEXT,
taxable_paise INTEGER, tax_paise INTEGER, total_paise INTEGER, paid INTEGER,
problems TEXT NOT NULL DEFAULT '[]'
);
CREATE TABLE tax_class (
class_code TEXT NOT NULL, rate_pct_bp INTEGER NOT NULL,
cess_pct_bp INTEGER NOT NULL DEFAULT 0, effective_from TEXT NOT NULL, effective_to TEXT
);

@ -139,6 +139,8 @@ AWS attribution, no path to the fleet console).
packages are shared. Guardrails: a bundle-isolation check in the test suite (store
bundle contains no HQ code and vice versa), per-app version tags, and the rule that
**HQ's DB is the cloud tier's nucleus** — the tenant registry is born from HQ's client
table (same UUIDs, promoted once), never built beside it. Engine may swap to Postgres
at AWS deploy time (portable repos make it cheap). Physical repo split stays a cheap,
table (same UUIDs, promoted once), never built beside it. **Postgres locked as the
production engine (founder, 2026-07-10)** — swap runs as its own task after HQ-2,
before the real-data import, so live data never migrates engines; SQLite stays the
dev/test engine behind the same repositories. Physical repo split stays a cheap,
reversible option if practice ever demands it.

@ -25,7 +25,7 @@ tracking against our catalog, no AWS attribution, no path to the fleet console).
|---|---|
| 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. Consider swapping the engine to Postgres at AWS deploy time (cheap behind the repos; removes even the engine migration later) |
| 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 |

Loading…
Cancel
Save