Step-by-Step Guide — Follow each step exactly as written. All values are provided — do not improvise.
TC-BIL-008: Total Parity & Payment Reconciliation (Detailed)
Module: Billing & Invoicing — Totals / Payments
Primary Test User: treasurer@demo.membervu.com / Treasurer123! (Finance Manager); testmember@rcme.membervu.com / Member123! (member)
Priority: P1
URLs for this test:
Frontend:
Admin invoices:
Finance dashboard:
Member invoices:
MailHog:
Frontend:
https://stg-rcme.membervu.com/loginAdmin invoices:
https://stg-rcme.membervu.com/admin/invoicesFinance dashboard:
https://stg-rcme.membervu.com/admin/financeMember invoices:
https://stg-rcme.membervu.com/invoicesMailHog:
https://stg-webmail.membervu.com
Payment gateway is INERT on staging (Paynamics). The multi-invoice payment initiation in Step 3 creates a real payment-initiation record but does NOT complete a live charge — verify the initiation/allocation records, not a captured payment.
Step 1: Set up an invoice with discount + line items + credits (BIL8-01/02/03)
| Step | Action (EXACT clicks/typing) | Expected Result (EXACT text/behavior) | Test Value |
|---|---|---|---|
| 1 | Go to https://stg-rcme.membervu.com/login; sign in. | Dashboard loads. | treasurer@demo.membervu.com / Treasurer123! |
| 2 | Sidebar → Finance → Invoices. Create a new manual invoice with 2 line items. | Invoice created with itemized total = sum of line items. | Line 1: Annual Dues ₱15,000.00 · Line 2: Late Fee ₱500.00 (total ₱15,500.00) |
| 3 | Apply a FIXED discount of ₱1,500.00 to this invoice (Apply Discount modal). | Net drops to ₱14,000.00. | Discount ₱1,500.00, reason QA BIL-008 parity setup |
| 4 | Record a partial manual payment of ₱6,000.00 against this invoice and approve it (settled allocation/credit). | Invoice balance now ₱8,000.00 outstanding. | ₱6,000.00 |
| 5 | Note the invoice number and the exact figures: original ₱15,500.00 → net after discount ₱14,000.00 → balance after payment ₱8,000.00. | Recorded for cross-check. | — |
Step 2: Cross-check the SAME total across all 4 surfaces (BIL8-01/02/03)
| Step | Action | Expected Result | Test Value |
|---|---|---|---|
| 1 | Open this invoice's detail view (GET /api/billing/admin/invoices/:id). | Shows original ₱15,500.00, discount −₱1,500.00, net ₱14,000.00, paid ₱6,000.00, balance ₱8,000.00. | — |
| 2 | Go back to the invoice listing (GET /api/billing/invoices/tenant) and find this invoice's row. | The listing row's amount/balance figures are IDENTICAL to detail — same cents, no rounding drift. | — |
| 3 | Run a reporting export (Finance → Reports, or the CSV export on the invoice list) covering this invoice's period/source. | The exported row for this invoice number shows the SAME total figures as detail/listing. | — |
| 4 | Open the Finance dashboard (/admin/finance, GET /api/billing/admin/finance/summary) for the relevant period. | The aggregate figures are consistent — this invoice's ₱6,000.00 collected and ₱8,000.00 outstanding contribute correctly to the summary totals (not double-counted, not omitted). | — |
Data assertion: all four surfaces are fed by the same canonical
projectInvoiceTotals projector (#443/#450). If ANY of the 4 shows a different total for this same invoice, that is a P1 money-parity defect — record which surface(s) diverged and the exact conflicting figures.
Step 3: Multi-invoice payment initiation (BIL8-04)
| Step | Action | Expected Result | Test Value |
|---|---|---|---|
| 1 | Log out; log in as the member. Go to My Invoices (/invoices). | List of the member's own outstanding invoices. | testmember@rcme.membervu.com / Member123! |
| 2 | Select 2 or more outstanding invoices via checkboxes (if the UI supports multi-select) and initiate ONE payment via a wallet channel. | UI calls POST /api/billing/member/invoices/bulk-pay/paynamics with an array of invoice IDs. GCash/Maya only — CREDITCARD is not offered for bulk pay (deferred, GR150). | Channel: GCash or Maya |
| 3 | Inspect the response / resulting records (admin view of payments, or API response). | A single payment-initiation groups the invoices, but each selected invoice receives its OWN Allocation record with the correct per-invoice amount — no invoice left un-allocated, none double-allocated. | — |
| 4 | Verify each invoice's balance individually. | Each invoice's balance reflects exactly its own allocation — sum of all allocations across the selected invoices equals the total payment amount. | — |
Step 4: Payment-confirmation email — reconcile job window (BIL8-05)
| Step | Action | Expected Result | Test Value |
|---|---|---|---|
| 1 | Complete a dues payment (manual payment approved by admin, or the online-record path). | Payment status settles. | — |
| 2 | Immediately check https://stg-webmail.membervu.com. | Confirmation email may or may not have arrived yet — this is expected either way. | — |
| 3 | If not present, wait up to ~10 minutes (the background reconcile job's cycle window) and re-check MailHog. | The confirmation email arrives within the window. Backend field Payment.confirmationEmailSentAt gets populated (verifiable via admin payment detail if exposed, or by re-confirming the email content matches this payment). | — |
| 4 | Only if the email is STILL missing after 10+ minutes, log it as a genuine miss. | Do not file "email not sent" as a bug if checked before the 10-minute window elapses — that is a false positive per #118's documented reconcile cadence. | — |
Step 5: Reject a manual payment — invoice stays listed + balance reverts (BIL8-06)
| Step | Action | Expected Result | Test Value |
|---|---|---|---|
| 1 | As member, submit a manual bank-transfer payment with proof against a fresh OUTSTANDING invoice (e.g. ₱10,000.00), for the full amount. | Payment record created, status PENDING. Invoice remains outstanding (unsettled allocation doesn't reduce balance). | ₱10,000.00 |
| 2 | As Finance Manager, go to Finance → Payments, locate the pending payment, click Reject, enter a reason. | POST /api/billing/admin/payments/:paymentId/reject → 200. Payment status becomes FAILED (verificationStatus: REJECTED). | Reason QA BIL-008 reject test |
| 3 | Go back to the invoice list (Outstanding filter). | The invoice still appears in the list — it was NOT removed/hidden by the rejection. | — |
| 4 | Open the invoice detail. | Balance is restored to the full pre-payment amount — ₱10,000.00 outstanding, not a stale partial or ₱0. The backend re-runs recomputeAndPersistInvoiceStatus on rejection specifically so the stored status/outstanding-tab query reflects this correctly. | — |
Step 6: Payment rows show both references (BIL8-07)
| Step | Action | Expected Result | Test Value |
|---|---|---|---|
| 1 | Open Finance → Payments list; locate any settled payment row (e.g. from Step 1). | Row/detail displays the internal payment reference AND, where applicable, the gateway reference — both visible, not just one collapsing into the other. | — |
CLEANUP
- Void or flag the QA invoice created in Step 1 so it isn't mistaken for a real member obligation in later regression passes.
- Confirm the rejected payment from Step 5 stays rejected (do not re-approve unless intentionally re-testing).
- If any multi-invoice payment in Step 3 left an ambiguous state (e.g. one invoice under-allocated), note the exact invoice IDs for the bug report before moving on.