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
2.4 KiB
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
npm installnpm run generate-handoff-keys— generateskeys/handoff-private.pem(kept here, gitignored, never shared) andkeys/handoff-public.pem(copy this into every company app'skeys/handoff-public.pem, referenced by itsHANDOFF_PUBLIC_KEY_PATHS).- Copy
.env.exampleto.env, fill in a realDATABASE_URL, random JWT secrets, and (optional for now) Google/Microsoft OAuth credentials. npm run prisma:migratethennpm run prisma:seed— seeds one operatorPerson(joosth2002@icloud.com/ChangeMe123!— change this) and oneCompanyrow pointing at the company app onhttp://localhost:5173(the frontend, since/handoffis a frontend route).npm run dev— API onhttp://localhost:5100.cd client && npm install && npm run dev— Manager frontend onhttp://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 differentcompanySlugis rejected there. - Manual browser smoke: with both this Manager (
5100/5174) and the company app (5000/5173) running, openhttp://localhost:5174, sign in with the seeded operator, pick "Demo Company," and confirm you land inside the company app already authenticated (and thathttp://localhost:5173/loginno longer exists — it's/handoffnow). 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.