Commit Graph

63 Commits (6f50799d3c0f713c1a515a2079c71e0e2df6d0db)

Author SHA1 Message Date
Thomas Joise d6faf0d5bd merge: Client Detail redesign (D38) — composed onboarding pipeline, status line, payments context, mobile fixes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

# Conflicts:
#	STATUS.md
#	docs/06-DECISIONS.md
1 day ago
Thomas Joise 343fa94efd docs(client-detail): D38 redesign decision + STATUS in-progress note
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 days ago
Thomas Joise a9e5aa79d9 docs: bring README/STATUS/CLAUDE/DECISIONS current (D34-D37)
Records the work since the last doc pass: employee master import + invited/first-login
flag (D34), client type PACS/Store + directory-sourced LTD numbers and the name-correction
session (D35), login history with IP/device (D36), and the society category matching the
Kerala co-op directory taxonomy (D37). Adds the login_event table, the Login history
screen, and the client type/category fields; bumps the schema table count 31->32. Decisions
D34-D37 appended to 06-DECISIONS.md.
2 days ago
Thomas Joise d05c7ee31b docs: bring README/STATUS/CLAUDE/DECISIONS current (D34-D37)
Records the work since the last doc pass: employee master import + invited/first-login
flag (D34), client type PACS/Store + directory-sourced LTD numbers and the name-correction
session (D35), login history with IP/device (D36), and the society category matching the
Kerala co-op directory taxonomy (D37). Adds the login_event table, the Login history
screen, and the client type/category fields; bumps the schema table count 31->32. Decisions
D34-D37 appended to 06-DECISIONS.md.
2 days ago
Thomas Joise 6bc92bace3 docs(client-detail): redesign spec + backend/frontend implementation plans
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 days ago
Thomas Joise 11d45e805c chore: remove all 'Tecnostac' references — everything is SiMS
Founder: there is no Tecnostac in this application. Replaced the seed default
company.name ('Tecnostac' -> 'SiMS'), demo-seed data, ~22 test fixtures/emails, and
docs (Tecnostac -> SiMS, @tecnostac.com -> @sims.com). Live DB company.name also
updated to 'SiMS' (shows on dashboard, documents, letterhead, reminder emails). Owner
email was already SiMS (simssoftware13@gmail.com). Full suite 416 green.
2 days ago
Thomas Joise 62eb937012 feat(d33): database backup script (pg_dump + rotation)
scripts/backup.mjs (npm run backup): pg_dump the DB to a timestamped compressed
pg_restore-able file under ./backups (gitignored), keep newest HQ_BACKUP_KEEP (14).
Locates pg_dump on PATH or the Windows PostgreSQL install dir. Verified against live
data: 304 KB dump, 32 tables, valid archive. Backups hold plaintext creds (D32) so
backups/ is gitignored + documented as access-controlled. Pending: schedule + off-box
copy + DB password out of server.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise 583b663aa6 docs(d29,d30): log the cockpit/theming + Home-hub/nav decisions
- 06-DECISIONS.md: add D29 (cockpit Dashboard + palette axis Warm/Slate/
  Graphite/Zinc + Geist font) and D30 (Home hub landing + 6-group sidebar
  reorg + section count badges + per-module client count).
- CLAUDE.md: refresh the frontend page list (Home is the landing, Dashboard
  at /dashboard) and note the palette/font theme system.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 days ago
Thomas Joise 79717133b8 feat(d32): remove the key concept entirely + keyless legacy cleanup
Owner has no HQ_SECRET_KEY, so the D31 unlock-with-key path was impossible. crypto is
now fully keyless: encrypt/decrypt ignore keyHex (params kept so a key can be wired back
later); decrypt returns '' for any leftover legacy AES value (unreadable → treated as
unset, never fed back as ciphertext). Key-based /admin/migrate-plaintext replaced by
keyless /admin/clear-legacy-credentials (clearLegacyCiphertext) which NULLs unreadable
pre-D31 values. .env.example marks HQ_SECRET_KEY removed.

Ran the cleanup on live data: 76 SMS + 110 client DB passwords were unrecoverable and
cleared (usernames intact) — must be re-populated in plaintext (APEX re-import / re-entry).

Also fixes the one real defect the D31 adversarial review confirmed: maybeRefreshSmsDaily
still had a 'keyHex==="" -> no-op' guard that would silently kill the DAILY auto-refresh
once the key was gone (manual refresh masked it). Removed; the daily pull now runs keyless
like the manual path. Full suite 416 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise 3e175b8da1 feat(d31): store credentials in the clear (owner decision) + one-time migration
Removes credential encryption at rest so the SMS-gateway logins (and other portal/DB
passwords, module secrets, Gmail token) are usable daily without a held-out
HQ_SECRET_KEY. Keystone: crypto.encrypt() stores tagged plaintext ('plain:'),
decrypt() untags keylessly and still reads legacy AES *with* the key to migrate it.
Drops the six 'refuses/cannot without HQ_SECRET_KEY' guards. Scope is the reversible
credential vault ONLY — login passwords stay one-way scrypt-hashed (@sims/auth).
Secrets still kept out of list payloads + audit rows; reveal still managerial+audited.

migrateLegacyCiphertext + POST /admin/migrate-plaintext (owner) rewrite existing AES
rows as plaintext using the key one final time (idempotent, one audit summary), after
which the key is never needed. SECURITY: a DB dump now exposes these in the clear.

Tests updated to the plaintext policy; new migrate-plaintext.test locks decrypt +
migration + idempotency. Full suite 416 green; typecheck clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise 58c97994bd feat(d28): SMS gateway live balance sync (BalAlert API) + daily auto + low alert
Pull each SMS client's actual credit balance from the provider panel instead of
manual entry. sms.balance_api_url (dated setting, default 164.52.195.161/API/
BalAlert.aspx) returns pipe-delimited code|STATUS|payload; parseBalanceResponse
reads the trailing number and flags ERROR lines, never guessing. Uses each
subscription's already-imported username/password_enc — the automated path decrypts
directly (no reveal-audit spam) and audits sms_balance ONLY when it changes;
per-poll telemetry lands in the unaudited sms_balance_check table (SQLite schema +
PG migration 010). Managerial 'Refresh from gateway' button (POST /reports/sms-
balances/refresh) + per-client refresh; scheduler does one pass per day
(maybeRefreshSmsDaily). Low-balance alert on the Dashboard + cockpit tile. Needs
HQ_SECRET_KEY (import-time key) to decrypt — no-ops with a clear message otherwise,
never stores plaintext. Parser unit-tested; full suite 408 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise bd793d390f docs: feature roadmap from ultracode design pass (9 features speced + build order)
Durable specs for tickets/reconciliation/MIS/sms-balance/tds/audit-viewer/
notifications/einvoice (+ renewals, delivered) with schema/repo/route/UI/test
detail and a collision-safe build order. WhatsApp design was transiently
classifier-blocked; to re-run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise 3247585e52 docs(d26): functional-gaps record (GST, search, statement, bulk, AWS)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 days ago
Thomas Joise 921fd7e6d1 docs(d25): module order, username login, nginx XFF, hardcoded-DB caveat
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 0a7b7e6118 docs: record SQL-injection re-audit (clean) + D25 PWA/mobile + nginx XFF
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 70234161dc fix(d24): pg allocation row-lock + index ordering; docs (D24 record, 389 tests)
- recordPayment locks the target invoice row (SELECT ... FOR UPDATE, Postgres
  only — SQLite is serialized by the connection mutex) before reading
  outstanding, so concurrent payments cannot over-allocate on prod.
- INDEX_DDL now runs AFTER migrate() in openDb, so indexes referencing
  migrate-added columns (owner_id, lockout cols) build on older DBs.
- D24 remediation recorded in 06-DECISIONS; test counts 377 -> 389.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 86af9ecb27 docs(d22): onboarding tracker D-record; test count 377
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 0e85163b68 docs(d23): SMS rate-card D-record; catalogue additions; test count 372
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 13d2b5fabb docs(d21): stamp DELIVERED, D-record, real-data verification, test count 363
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 2d4344e098 docs(d20): stamp DELIVERED with trial-import reconciliation; D20+D21 records
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise a25fbf8a66 docs(spec): D21 module-defined field schema (config on the module)
Each module declares its own operational fields (field_spec JSON);
per-client values organized by that module schema; generalized encrypted
secret map. Flexibility mandate made concrete: new module or new field =
no code, no migration. Queued after the D20 trial import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 7d5223908f docs(spec): D20 APEX parity slice — ticket desk, service data, branches, importer v2
Survey verdicts for all 25 real APEX tables; founder calls locked:
full ticket desk (4,567-ticket history), client_module service columns
(provider/username/password_enc/details), client_branch, five modules
= SMS, RTGS, WhatsApp, Mobile App, ATM.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise bb8dfc3b97 docs: D20 — hq.simssoftware.com on the company EC2; runbook gets the decided target + RDS-first sequencing (D19 switch delivered)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 days ago
Thomas Joise 30a1ea1c0a docs: D19 delivered — spec stamped with deviations, D-record, env docs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 08be48817e docs(spec): D19 Postgres switch design + pg driver dependency
Locked decisions: pg driver behind an async Db interface with dual
engines (sqlite for fast tests, pg for prod), bigint paise, TEXT ISO
dates, jsonb payloads, numbered migrations, ?->$n in the adapter,
DATABASE_URL selects the engine. Build order: async refactor first
(zero behavior change), then schema, adapter, dual-engine suite, smoke.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 83f12685c4 docs: record ideal-web-app pass delivered — 346 tests, Settings hub row, spec stamped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 days ago
Thomas Joise 19fe037bb4 docs: record the review pass — 345 tests, all 8 findings closed
STATUS: count + review summary, D18 sentence in the intro, honest migrate()
column list, routes table gains /reminders /documents /profile /import and the
collapse/user-menu shell description. README + CLAUDE: 345. D18 record in
06-DECISIONS gains the review outcome. Spec banner notes the post-review
deltas (CSV support columns, LIKE filters).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 days ago
Thomas Joise 8064c49465 docs: record the delivered D18 go-live cluster — 344 tests, gaps closed, spec stamped (Phase 10)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 66ec742523 docs(spec): go-live cluster gains WS-G editable roster + one-click convert everywhere, WS-H section-per-module quotation PDF (now 10 phases)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise e68b3b3b55 docs(plan): ideal-web-app pass — 10-task implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 7a9c6f9459 docs(spec): ideal-web-app pass — edit everywhere, dialogs, toasts, settings hub
Pinterest-chrome modals (Dialog/ConfirmDialog), Toaster, client editing (server
already supports it), send-by-email modal replacing window.prompt, /settings hub
with dated reminder-schedule rows + sharing default (two small additive backend
slices), favicon + per-route titles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 02bc75d691 docs(spec): go-live cluster design — due dates, import UI, reminders/documents pages, shell & profile, client support data (8-phase roadmap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 8cb37f648a docs: bring all documents current with the delivered redesign (D17)
- 06-DECISIONS: new D17 entry — warm ops-console direction, teal/ink,
  mono/sans split, Ctrl-K palette, Client 360 ribbon+tabs, UI-only scope
- DEPLOY-HQ: fix pm2 launch — start from repo root (a pm2 app started from
  apps/hq would boot an empty DB in apps/hq/data), no rebuild-per-restart
- 14-SPEC: cross-link the delivered UI-direction spec (D17)
- README: redesign feature bullet; STATUS: login + Client 360 screen rows
  reflect split panel, KPI row, pulse ribbon, deep-linkable tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 418555179b docs: bring every document current — runbook gains D16 go-live knobs, delivery stamps on all executed specs/plans, 14-SPEC delivery note, README doc table
- DEPLOY-HQ.md §6b: share.base_url (required for quote follow-up emails),
  quote.followup.policy manual/auto, dated reminder_schedule cadences,
  employee accounts + account-owner assignment at go-live
- STATUS/README/CLAUDE already at 293 tests; specs/plans now carry their
  DELIVERED/EXECUTED stamps so the archive reads honestly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 7862452989 docs: record funnel phases 7-10 delivered — 293 tests, gaps list updated (D16 final)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 1db0a8c6b7 docs(spec): record as-built deviations from final review adjudication
PageHeader slots trimmed; Client 360 KPI substitution (no server outstanding
figure); 'since' dropped (no createdAt); §8 render tests → pure-function tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 5a1c84f1de docs: record quote-to-close funnel slice (D16)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 803320a5d5 docs(decisions): D16 — employees ride staff_user; GET /employees returned whole
Records the decision the code already cites (staff_user IS the employee
table; rename would churn FKs and audit names) and the reviewed deviation
from the funnel spec's blanket pagination rule: GET /employees returns the
bounded console-user set whole with total, no page/pageSize.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise e919a6b871 docs(plan): HQ console redesign — 14-task implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 52420e072c docs(spec): HQ console redesign — warm ops-console system upgrade
Direction A v2 discipline (flat border-defined surfaces, black-ink primary,
accent-for-state, table/state patterns, Ctrl+K palette) wearing the H-04 warm
palette + teal accent. Client 360 with 12-month relationship-pulse ribbon.
UI-only; no backend changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise ffba5c3d3e docs(spec): APEX 82963 UX reference — team mental-model patterns for the HQ polish pass
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise 02306b1088 docs(spec): quote-to-close funnel design (10-phase roadmap)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
Thomas Joise a925352e70 docs: rewrite HQ plans/specs to be self-contained after the split
The historical HQ-1/2/3a build plans and the live-preview spec referenced sibling
Store apps (apps/store-server, apps/backoffice) as copy-sources and cited Store-only
docs. Rewrote those to describe the patterns intrinsically and to name the Store
product/repo where the scope boundary is genuine, so each plan stands alone in the
HQ repo. Technical content and task lists unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 75e1795bfd docs: bring planning docs current with the HQ/Store split
Update the shared-heritage docs that still described the old combined monorepo:
D15 and the HQ console spec now note the 2026-07-16 split into two independent
repos, and the deploy runbook uses the correct repo name (sims-hq, was sims-next).
Historical decisions are preserved; only stale present-tense/naming is corrected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise c03fddc699 docs(spec): record split delivered + backup deleted
Marks the split spec DELIVERED with the outcome: both repos verified, the
pre-split combined repo deleted, and the kept-@sims/kept-env decisions recorded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 115f2e6c41 docs: HQ project identity (README, STATUS, CLAUDE) + fix cross-repo doc links
Adds the front-page README, a first STATUS.md build-state doc (the console had
none), and a CLAUDE.md with the house rules that govern this repo. De-links four
references to Store-only planning docs (04-ROADMAP, 08-MARKET-ARCHITECTURES,
09-UX-FLOWS-AND-MENUS) that now live in the Store repo, and regenerates the
lockfile to drop the removed Store workspace entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 7e1c93cee0 Split: make this the standalone HQ project (remove Store)
This repo keeps its 60-commit HQ history and becomes independent of the Store
product. Removed the Store apps (pos, store-server, backoffice), the Store-only
packages (config, scanning, search-core, printing — printing was already a dead
dependency in hq-web), and the Store planning docs.

Shared packages are forked and trimmed to exactly what HQ uses: domain keeps
ids/money/business-day/doc-series/gstin/documents; auth keeps pin; billing-engine
keeps compute/tax. Store-only tests for the removed modules were dropped, and
auth.test.ts was trimmed to its PIN blocks. Package renamed sims-hq; root
tsconfig/vitest paths narrowed to the four kept packages.

Verified independently: npm install + typecheck clean, 177 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 85adbb553a docs(spec): design for splitting HQ and Store into independent projects
Two products share one repo: HQ (internal client book, proforma billing,
call tracking) and Store (the GST retail product shipped to ~300 clients).
Different DBs, different audiences, different cadences — 60 of 75 commits
touch HQ, 2 touch Store.

Design: two repos. HQ inherits the existing history; Store starts fresh so
its history carries zero HQ bytes. Shared packages fork into each side,
trimmed to verified usage — safe because GST rates are dated DB rows, not
code, so the forked arithmetic has no reason to diverge.

Project tracking and conversion reporting are deferred: they don't exist
yet, and mixing new features into a mechanical move hides failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 days ago
Thomas Joise 3d3487c85f wip(store-product): returns, GST returns, offline queue, security hardening + docs
Snapshot of the store-product workstream's in-progress work (apps/pos,
apps/backoffice, apps/store-server, packages): returns + credit notes, GST
returns exports, offline bill queue, rate-limiting, session policy, print
guard, batch tracking, and the SEC-C6/SEC-A5 auth fixes; plus docs/18 red-team
review. Committed to preserve work for the repo push. HQ console already
committed separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago
Thomas Joise 4559958fa4 docs(hq): go-live runbook, env template and S3 backup script
Turnkey deployment for the AWS box: documented env vars, pm2/systemd run,
HTTPS reverse-proxy, nightly SQLite->S3 backup with rotation, and the Postgres
switch note. No secrets; unblocks go-live the moment the server/creds arrive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 week ago