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.
 
 
 
Go to file
Thomas Joise 400abc224f fix(funnel): review fixes — stop-chase on convert/cancel/lost, scoped ?status=, send guards, F3/F4 convert hardening
Review findings on the quote-to-close slice, each verified against the code:

- convertDocument: ANY conversion now flips the source to 'invoiced' (previously
  only ->INVOICE), so a QT->PI quote leaves the daily scan's 'sent' set and the
  pipeline's Quoted/Waiting — the chase never resumes after convert (spec 7 stop
  conditions). Scan additionally excludes quotes with a live forward child
  (covers rows converted before this fix) and quotes of lost clients.
- convertDocument rule-4 guard (spec 8 F4): a live non-cancelled forward child
  blocks re-convert — one sale, one legal document; cancelling the child
  re-opens the path. Rule-2 fix (spec 8 F3): ->INVOICE recomputes the carried
  lines through computeBill on the invoice's own doc_date (mirrors
  createCreditNote); QT->PI stays copy-only (both non-legal).
- cancelDocument dismisses a cancelled quotation's open follow-up nudges in the
  same transaction, per-row audited — dead paper is not chased.
- GET /reminders?status= no longer bypasses the owner scope and pagination: the
  status param narrows the same scoped listQueue view. Web badge (Layout) now
  uses the honest scoped total instead of an unscoped flat list length.
- listQueue/queueCounts staff scope keeps doc-less reminders (renewal_due,
  amc_expiring, follow_up, email_bounced) visible — they have no derived owner
  and are shared work; previously they vanished for every staff viewer.
- sendReminder resolves the recipient BEFORE minting a public share (a send
  that can never succeed leaves no live artifact) and refuses quote_followup
  sends loudly when share.base_url is unset instead of emailing a dead relative
  link. The scheduler auto-drain parks a hard-failing send as 'failed' with its
  reason instead of crashing the scan.
- reminderContext takes an injectable today (threaded from deps.now in the send
  path) so dated schedule text resolves deterministically.
- Pipeline: quote-less lost clients (client.status=lost) now appear under
  filter=lost (spec 9); Dashboard queue table marks first-page truncation
  honestly (rule 8).

Typecheck clean; 270 tests green (255 before, +15 covering each fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 days ago
apps fix(funnel): review fixes — stop-chase on convert/cancel/lost, scoped ?status=, send guards, F3/F4 convert hardening 5 days ago
docs docs(decisions): D16 — employees ride staff_user; GET /employees returned whole 5 days ago
packages feat(hq-web): dashboard — skeletons, toned stats, tinted queue, 2-col grid 5 days ago
.gitignore chore: gitignore .superpowers brainstorm session files 5 days ago
CLAUDE.md docs: record redesign verification — 255 tests, new shell description, run-from-root warning 5 days ago
README.md docs: record redesign verification — 255 tests, new shell description, run-from-root warning 5 days ago
STATUS.md docs: record redesign verification — 255 tests, new shell description, run-from-root warning 5 days ago
package-lock.json feat(hq-web): grouped sidebar shell, top bar, Ctrl+K palette, responsive-lite 5 days ago
package.json Split: make this the standalone HQ project (remove Store) 5 days ago
tsconfig.base.json Split: make this the standalone HQ project (remove Store) 5 days ago
tsconfig.json chore: bring the full store-product codebase under version control 2 weeks ago
vitest.config.ts Split: make this the standalone HQ project (remove Store) 5 days ago

README.md

SiMS HQ — Internal Ops Console

The console for running our own software business. It manages our ~300 Classic clients end to end — and it replaces the internal Oracle APEX app. This repo is internal only; it is not shipped to clients.

One place for the client book, what each client has bought, the money they owe, and every document and conversation we've had with them.

What it does

  • Client book — the registry of ~300 clients: contacts, GSTIN, status, notes.
  • Modules + price book — the sellable software modules with a dated price book (a price change is a new row, not a code release) and per-client subscriptions.
  • Documents — quotations (QT), proforma invoices (PI), invoices (INV) and credit notes (CN), built on editable letterhead templates, rendered to PDF (headless Chromium), with shareable public links (one token-guarded, rate-limited, revocable read route).
  • Interactions — call / visit / meeting tracking against each client.
  • AMC contracts and AWS cost recovery — per-client cost attribution pulled monthly.
  • Payments + allocations, recurring plans, and reminders sent via Gmail on the company mailbox, with bounce handling and a manual queue when the token dies.
  • Reports, a Dashboard, an append-only audit log (every mutation), an APEX importer for the initial 300-client load, and a scheduler (runs on boot + every 6h: reminders, bounce polling, AWS cost pull).

Build state (start here)

Working codebase, not a plan. npm install + typecheck are clean and 255 tests pass. Current state and run-vs-pending detail live in STATUS.md; the go-live runbook (server, HTTPS, backups, Gmail/AWS/import wiring, the Postgres switch) is in docs/DEPLOY-HQ.md.

Architecture

  • Backend — apps/hq/src: an Express JSON API over better-sqlite3, behind portable repositories (repos-*.ts, one per domain: clients, modules, documents, payments, amc, aws, interactions, recurring, reminders, shares, reports, dashboard, email). SQLite is the dev/test engine; Postgres is the locked production engine (see DEPLOY-HQ.md). DB at data/hq.db; server on :5182 (also serves the built web UI and the public /share/:token route).
  • Frontend — apps/hq-web: React + Vite. Pages: Dashboard, Clients, ClientDetail, Documents, Modules, Reports.
  • Shared packages (trimmed forks): @sims/domain (ids, money, business-day, doc-series, gstin, documents), @sims/auth (pin/scrypt), @sims/billing-engine (compute, tax — our invoices are GST documents, computed to the paisa), @sims/ui (design system).

Planning docs

Doc What it covers
docs/14-SPEC-HQ-CONSOLE.md The console spec: shape, data model, features, delivery sequence (D15)
docs/11-ADMIN-SUPPORT-CONSOLE.md The vendor-side HQ / support console this app implements
docs/07-DB-AND-CONFIG.md Everything-in-DB design (messages, plans, settings) and the local DB engine analysis
docs/06-DECISIONS.md Founder decisions, including D15 (build HQ early) and the Postgres call
docs/16-PROJECT-RULES.md The hard rules a PR must satisfy to merge
docs/DEPLOY-HQ.md Go-live runbook for the console

Run

npm install                     # from the repo root (workspaces: packages/*, apps/*)

# Backend — HQ server. Build the bundle, then run it FROM THE REPO ROOT: the DB
# path resolves to ./data/hq.db against the current working directory, so the
# server must be launched from the repo root (not from apps/hq, which would look
# in apps/hq/data and boot an empty database).
npm run build --workspace @sims/hq
node apps/hq/dist/server.cjs     # serves :5182 (API, built web UI, /share/:token)
# First boot on an empty data/ creates data/hq.db and prints a one-time owner
# password — capture it. Log in as admin@tecnostac.com, then change it.

# Frontend — hq-web dev server (Vite on :5183, proxies /api and /share to :5182)
cd apps/hq-web && npm run dev

Nothing in apps/hq/.env is required to boot; each var unlocks a capability (Gmail sending, AWS cost pull, security peppers) — see apps/hq/.env.example and DEPLOY-HQ.md. Repo-wide checks from the root: npm run typecheck and npm test.