Advance balance — un-allocated client credit
Audit firms get advances all the time — proforma payments, retainers, deposits, "send me a receipt before fieldwork". M11 tracks these as is_advance=1 payments with their unallocated portion contributing to the client's running advance balance. Auto-applied to new invoices when the M11 auto-allocation runs.
What makes a payment an "advance"?
The is_advance column is computed at insert + on every allocation change. It's TRUE when:
- Zero allocations — payment recorded but not yet matched to any invoice
- Only proforma allocations — payment is against PI/{...}, which is informational only — actual revenue is recognised on tax invoice conversion
- Partial tax allocation with leftover — payment exceeds the tax invoice's balance; the unallocated remainder is the advance portion
5 worked scenarios
| Scenario | Payment | Allocations | is_advance |
|---|---|---|---|
| Full settlement | OMR 5,565 | INV/0042 — 5,565 (full) | 0 |
| Partial settlement | OMR 3,000 | INV/0042 — 3,000 (partial) | 0 |
| Cross-invoice settlement | OMR 12,500 | INV/0040 — 5,000 · INV/0041 — 5,000 · INV/0042 — 2,500 (Σ = 12,500) | 0 |
| Pure advance | OMR 5,000 | (none) | 1 |
| Proforma + leftover | OMR 6,000 | PI/0010 — 5,250 (informational) | 1 (5,250 + 750 leftover) |
Advance balance computation
Payment::advanceBalanceForClient($clientId) returns the current unallocated credit:
SELECT SUM(p.amount) - SUM(COALESCE(allocated_to_tax.amt, 0)) AS advance_balance FROM payments p LEFT JOIN ( SELECT pa.payment_id, SUM(pa.amount_allocated) AS amt FROM payment_allocations pa JOIN invoices i ON pa.invoice_id = i.id AND i.doc_type = 'tax_invoice' GROUP BY pa.payment_id ) allocated_to_tax ON allocated_to_tax.payment_id = p.id WHERE p.client_id = ? AND p.deleted_at IS NULL
Proforma allocations don't reduce advance balance — only tax-invoice allocations do.
Where advance balance surfaces
- Client detail → Billing card — orange tile if balance > 0
- Dashboard → Pulse panel — total firm-wide advance balance
- Invoice creation — auto-allocation hint when drafting new invoice for a client with advance
- Reports → Revenue by Client — separate column showing advance held
Auto-allocation on new invoice
When a new tax invoice is created for a client with positive advance balance, the system silently:
- Computes
min(advance_balance, invoice.grand_total) - Picks the oldest payment with unallocated capacity
- Inserts a payment_allocations row of that amount against the new invoice
- Recomputes invoice balance_due
- If balance_due reaches 0, invoice flips draft → sent → paid in one shot
- Flash message shows the auto-allocated amount
Behaviour can be disabled per-firm via m11.auto_apply_advances=0. Default ON.
Post-hoc allocation form
Sometimes an advance came in long before the invoice was issued. Click into the payment → Allocate button. Form:
- List of open invoices for the client (sorted oldest first)
- Editable allocation amount per invoice
- Running total + remaining unallocated
- Submit — same validation as primary allocation
Useful for: retainer applied to first work performed, year-end clean-up, applying old advances before write-off review.
Step-by-step — handling a retainer
Client sends OMR 5,000 retainer
"Apply when fieldwork starts." Bank transfer hits the firm account.
Record payment (no allocation)
M11 → Payments → New. client = X, amount = 5,000, method = bank_transfer, reference = bank ref. Save without allocations.
is_advance=1auto-set. Receipt PDF generated showing "Advance — un-allocated".Send receipt to client
Email the receipt PDF. M17 template
payment.received.Fieldwork happens · invoice raised
Job completes → tax invoice
INV/2026/0050auto-drafts for OMR 6,000.Auto-allocate on draft
System sees client has 5,000 advance, auto-allocates against the new invoice. Invoice balance_due = 1,000. Status moves draft → sent → partially_paid.
Client pays remainder
OMR 1,000 received. Allocate to invoice. Status → paid. Done.
Reporting on advances
The Reports module exposes:
- Total firm-wide advance balance — single KPI on dashboard
- Advance by client — list of clients with positive advance
- Aged advance — how long has each advance been sitting unallocated (typically you want this near zero — old un-allocated advances suggest service delivery is overdue)
Record a OMR 3,000 advance for a client with no open invoices. Check the client → Billing card — orange "Advance balance: OMR 3,000" tile appears. Now create a tax invoice for OMR 5,000 from that client. The auto-allocation triggers + the new invoice's balance_due becomes OMR 2,000. Flash message confirms.
Aged un-allocated advances (> 90 days) are an audit-finding waiting to happen — clients have paid for services not yet delivered. Either deliver or refund. Reports → Aged Advance highlights these. Best-practice firms keep advance balance < 1 month of revenue.
If you want to send the client a "this is what your money is going toward" doc upfront, issue a proforma. If they pay in trust (no specific invoice), record as a straight advance and apply later. Both are valid; proforma adds documentation discipline.