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
6.7 KiB
Phase 2 — Accounts Receivable: Manual Test Checklist
Adds Customers, Tax Codes, AR Invoices, Credit Notes, and Receipts with allocation — all
posting through the existing PostingService, so every one of these creates a real,
balanced GL entry you can trace from the Journal Entries screen. Backend correctness
(balanced postings, tax calc, allocation limits) is already covered by
tests/integration/ar.posting.spec.ts (7 tests, passing) and was additionally verified by
hand over the real HTTP API end-to-end. This checklist is for the parts only a human can
judge: does the UI make sense, does the data look right, does it match how the business
actually invoices customers.
Start the same four dev servers as Phase 1.5 (see PHASE_1.5_TEST_PLAN.md §0) and sign in
through the Manager.
0. New nav items
- Sidebar shows a new Sales section above General Ledger: Customers, Invoices, Receipts
- Administration section has a new Tax Codes item at the bottom
- Two tax codes are pre-seeded: STD (15%) and ZERO (0%) — visible at
/admin/tax-codes
1. Tax Codes
- "New Tax Code" creates a code (e.g.
EXPORT,Zero-Rated Export,0) and it appears in the grid - Toggling a tax code's Active switch off, then back on, works
- A tax code is usable immediately on new invoice/credit note lines without a page refresh
2. Customers
- "New Customer" requires a branch selection; creating one without a branch shows an error
- A created customer appears in the Customers grid with the right code/name/credit limit/terms
- Switching the top-right Branch switcher to a single branch filters the Customers grid to that branch only; switching back to Main shows all
- Clicking a customer row opens their Customer 360 page
3. Customer 360
- Header shows the customer's name and an Active/Closed status pill
- Four summary tiles: Outstanding Balance, Overdue (30+ days), Credit Limit, Payment Terms
- A brand-new customer with no invoices shows R0.00 everywhere, not blank or broken
- Contact Details card shows code/email/phone/tax number (or "—" for anything empty)
- Three tabs — Invoices / Payments / Credit Notes — each show "No … yet." until you create one
- "New Invoice" and "Receive Payment" buttons only appear if you have the AR permissions
(test with a restricted role, or trust the code path — both gate on
ar.invoice.manage/ar.receipt.manage)
4. Creating and posting an invoice
- From Customer 360, "New Invoice" opens the invoice form with that customer pre-selected
- Due date auto-fills from invoice date + the customer's payment terms and is read-only
- Adding a line, picking a revenue account (only INCOME-type accounts for that customer'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
- The footer Subtotal / Tax / Total roll up correctly across multiple lines with different tax codes (including a line with no tax code — contributes 0 to tax)
- "Save Draft" creates the invoice with status DRAFT and does not create a GL entry — confirm by checking General Ledger's entry count is unchanged
- From the draft's detail page, "Post Invoice" posts it — status flips to POSTED, and a new
entry appears in General Ledger (journal
SALES) that is balanced (Debits = Credits) - Opening that GL entry shows: one debit line to the AR Control account for the invoice total, one or more credit lines to the revenue account(s) for the subtotal(s), and (if tax was applied) a credit line to Output Tax Payable for the tax total
- "Post Invoice" directly from the form (skipping the separate detail-page click) also works and lands you on the now-posted invoice
- A draft invoice can be Voided (status → VOID, no GL entry ever created); a posted invoice has no Void option — only a credit note can reverse it
- The invoice now appears in the customer's Invoices tab and in the global Invoices list, with the correct balance and status pill
5. Receiving a payment
- From Customer 360, "Receive Payment" opens the receipt form with the customer pre-selected
- "Deposited to" only offers accounts marked as a Bank control account
- Saving and posting a receipt for the invoice's exact total creates a balanced GL entry:
debit to Bank, credit to AR Control, journal
CASH_RECEIPT - The posted receipt's detail page shows an "Allocate Against Invoices" panel listing the customer's open (posted, balance > 0) invoices
- Clicking "Full" next to an invoice fills in its exact remaining balance (capped at what's still unallocated on the receipt)
- Applying the allocation reduces both the invoice's balance and the receipt's unallocated amount by the applied amount — reload both pages and confirm the numbers persist
- Fully allocating a receipt makes the "Allocate" panel show "Fully allocated." instead of the invoice table
- Attempting to allocate more than a receipt'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 in the Customer 360 Invoices tab
6. Credit notes
- Create a credit note for the same customer, one line mirroring a posted invoice line (same account, same tax code, same amount)
- Posting it creates a GL entry that is the mirror image of the invoice's: debit to the revenue account (+ debit to Output Tax if taxed), credit to AR Control — and is balanced
- The credit note's detail page shows the same "Allocate Against Invoices" panel; allocating it against the open invoice reduces both the invoice's balance and the credit note's remaining balance
- A draft credit note can be voided; a posted one cannot
7. Cross-cutting checks
- Every invoice/receipt/credit note number follows its own series (
ARINV-2026-######,ARRCT-2026-######,ARCN-2026-######) and never repeats or skips oddly between runs - The Dashboard's "Posted Movement by Account Type" bars now reflect AR activity (ASSET bar grows from AR Control + Bank movement, INCOME bar grows from Sales)
- Switching to a specific branch (not Main) on any AR screen shows only that branch's customers/invoices/receipts
- No console errors in the browser dev tools while working through this checklist
Once this is ticked (or you've flagged issues), the plan's next step is Accounts Payable — Suppliers, Supplier Invoices, Debit Notes, Payments — built the same way, followed by Tax reporting and then Stock Control.