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
39 lines
2.4 KiB
Markdown
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.
|