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

39 lines
2.4 KiB
Markdown

# 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.