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
This commit is contained in:
Joshua Oosthuizen
2026-09-03 13:27:19 +02:00
co-authored by Claude Sonnet 5
commit 4acbc03b35
279 changed files with 38537 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
# Phase 3 — Accounts Payable: Manual Test Checklist
Mirrors Accounts Receivable exactly, but for money owed to suppliers instead of money owed by
customers: Suppliers, Supplier Invoices (Bills), Debit Notes, and Payments with allocation —
same Draft → Post workflow, same shared `PostingService`, same Tax Codes. Backend correctness
is already covered by `tests/integration/ap.posting.spec.ts` (7 tests, passing alongside AR's
7 and GL's 9) and was additionally verified over the real HTTP API by hand (create supplier →
create/post invoice → create/post payment → allocate → invoice balance hit zero). This
checklist is for the parts only a human can judge.
Start the same four dev servers as before and sign in through the Manager.
## 0. New nav items
- [ ] Sidebar shows a new **Purchasing** section (between Sales and General Ledger): Suppliers,
Bills, Payments
## 1. Suppliers
- [ ] "New Supplier" requires a branch selection; creating one without a branch shows an error
- [ ] A created supplier appears in the Suppliers grid
- [ ] Branch switcher filters the Suppliers grid the same way it does Customers
- [ ] Clicking a supplier opens their detail page (the AP equivalent of Customer 360)
## 2. Supplier detail page
- [ ] Header shows the supplier's name and an Active/Closed status pill
- [ ] Three summary tiles: Outstanding Balance, Overdue (30+ days), Payment Terms
- [ ] A brand-new supplier with no invoices shows R0.00 everywhere
- [ ] Contact Details card shows code/email/phone/tax number (or "—" for anything empty)
- [ ] Three tabs — Invoices / Payments / Debit Notes — each show "No … yet." until you create one
- [ ] "New Invoice" and "Make Payment" buttons only appear if you have the AP permissions
## 3. Creating and posting a supplier invoice (Bill)
- [ ] From the supplier's page, "New Invoice" opens the form with that supplier pre-selected
- [ ] Due date auto-fills from invoice date + the supplier's payment terms and is read-only
- [ ] Adding a line, picking an expense account (only EXPENSE-type accounts for that supplier's
branch are offered) and a tax code updates that line's Amount live
- [ ] Applying the STD (15%) tax code to a R1,000 line shows R150 tax and R1,150 line total
- [ ] "Save Draft" creates the invoice with status DRAFT and does **not** create a GL entry
- [ ] "Post Invoice" posts it — status flips to POSTED, and a new balanced entry appears in
General Ledger under journal `PURCHASES`
- [ ] Opening that entry shows: debit line(s) to the expense account(s) for the subtotal, a
debit line to Input Tax Receivable for the tax (if any), and a credit line to Accounts
Payable Control for the total
- [ ] A draft invoice can be **Voided**; a posted invoice has no Void option — only a debit note
can reverse it
- [ ] The invoice appears in the supplier's Invoices tab and in the global Bills list
## 4. Making a payment
- [ ] From the supplier's page, "Make Payment" opens the form with the supplier pre-selected
- [ ] "Paid from" only offers accounts marked as a Bank control account
- [ ] Posting a payment for the invoice's exact total creates a balanced GL entry: debit to
Accounts Payable Control, credit to Bank, journal `CASH_PAYMENT`
- [ ] The posted payment's detail page shows an "Allocate Against Invoices" panel listing the
supplier's open (posted, balance > 0) invoices
- [ ] "Full" fills in the invoice's exact remaining balance, capped at what's still unallocated
on the payment
- [ ] Applying the allocation reduces both the invoice's balance and the payment's unallocated
amount — reload both pages and confirm the numbers persist
- [ ] Over-allocating (more than the payment's unallocated amount, or more than an invoice's
balance) is rejected with a clear error and changes nothing
- [ ] A part-paid invoice shows the correct partial balance on its detail page and on the
supplier's Invoices tab
## 5. Debit notes
- [ ] Create a debit note for the same supplier, one line mirroring a posted invoice line
- [ ] Posting it creates the mirror-image GL entry: debit to Accounts Payable Control, credit
to the expense account (+ credit to Input Tax if taxed) — and is balanced
- [ ] The debit note's detail page shows the same allocation panel; allocating it against the
open invoice reduces both the invoice's balance and the debit note's remaining balance
- [ ] A draft debit note can be voided; a posted one cannot
## 6. Cross-cutting checks
- [ ] Every invoice/payment/debit-note number follows its own series (`APINV-2026-######`,
`APPMT-2026-######`, `APDN-2026-######`) and never repeats or skips oddly
- [ ] The Dashboard's "Posted Movement by Account Type" bars now also reflect AP activity
(LIABILITY bar grows from AP Control movement, EXPENSE bar grows from bills)
- [ ] Switching to a specific branch shows only that branch's suppliers/invoices/payments
- [ ] No console errors in the browser dev tools while working through this checklist
---
Once this is ticked, the plan's next step is Tax reporting (VAT/output-input reconciliation),
then Stock Control + Procurement Matching (GRV/PPV, incorporating the Posting Ledger reference
already reviewed).