Files
Joshua OosthuizenandClaude Sonnet 5 4acbc03b35 Initial commit: multi-company financial management system
Root company app (src/, client/) — GL foundation, AR, and AP:
- Segmented Chart of Accounts (Sage 300-style), double-entry PostingService with
  three-layer balance enforcement (app validation, CHECK constraints, deferred
  DB trigger), gap-free document numbering, RBAC with branch restrictions.
- Accounts Receivable: Customers, Invoices, Credit Notes, Receipts with
  allocation, all posting through PostingService.
- Accounts Payable: Suppliers, Supplier Invoices, Debit Notes, Payments with
  allocation, mirroring AR's posting pattern.
- Tax Codes shared between AR and AP.
- Company-app login is a token handoff from the Account Manager (RS256,
  single-use, company-scoped) rather than a local password.
- Frontend reskinned to the approved mockup: navy sidebar, dashboard, tree-view
  Chart of Accounts, Customer/Supplier 360 pages.

Account Manager (manager/, manager/client/) — central login gateway:
- Password + TOTP 2FA + Google/Microsoft OAuth, company picker, and the RS256
  handoff token issuance that the company app verifies.

Covered by unit + integration tests against a real Postgres database (GL
balance invariants, AR/AP posting correctness and allocation limits, the full
handoff exchange including replay/cross-company/expiry/forgery rejection).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tq2DbEvzHzs13jDFhLaX44
2026-09-03 13:27:19 +02:00

2.4 KiB

Setup — Account Manager (Phase A)

Prerequisites

  • Node.js 18+ (tested on v22)
  • A reachable PostgreSQL 15+ database, separate from any company app's database
  • The company app checked out as a sibling (../ from this directory) — Phase A wires this Manager to exactly one manually-provisioned company

First-time setup

  1. npm install
  2. npm run generate-handoff-keys — generates keys/handoff-private.pem (kept here, gitignored, never shared) and keys/handoff-public.pem (copy this into every company app's keys/handoff-public.pem, referenced by its HANDOFF_PUBLIC_KEY_PATHS).
  3. Copy .env.example to .env, fill in a real DATABASE_URL, random JWT secrets, and (optional for now) Google/Microsoft OAuth credentials.
  4. npm run prisma:migrate then npm run prisma:seed — seeds one operator Person (joosth2002@icloud.com / ChangeMe123! — change this) and one Company row pointing at the company app on http://localhost:5173 (the frontend, since /handoff is a frontend route).
  5. npm run dev — API on http://localhost:5100.
  6. cd client && npm install && npm run dev — Manager frontend on http://localhost:5174.

Verifying it works

  • npm test — unit tests (handoff token sign/verify, tampering, expiry — no DB required).
  • npm run test:integration — against the real Manager DB: login, list companies, issue a handoff token, reject a handoff to a company with no membership.
  • The company app has its own integration test (../tests/integration/handoff.spec.ts) that mints a token with this Manager's real private key and exchanges it against the real company app DB — proving both sides of the handoff agree, replay is rejected, and a token minted for a different companySlug is rejected there.
  • Manual browser smoke: with both this Manager (5100/5174) and the company app (5000/5173) running, open http://localhost:5174, sign in with the seeded operator, pick "Demo Company," and confirm you land inside the company app already authenticated (and that http://localhost:5173/login no longer exists — it's /handoff now).
  • GET /health — basic liveness check.

Adding a second company (still manual in Phase A)

Phase B automates this. Until then: stand up another copy of the company app on a different port/database by hand, then insert matching Company and PersonCompanyMembership rows here.