Invoicing & Payments
Quote-to-cash — checkout from rentals, invoice lifecycle, payments, credit notes, deposit/pro-forma invoices, and billing communications.
Overview
Phase 4 closes the quote-to-cash loop: a rental converts to an invoice with a frozen tax snapshot, the invoice is issued and paid, credit notes reconcile adjustments, and PDFs and billing emails reach the customer through the Document Engine and Notification Engine.
Invoices, payments, and credit notes are Verbs event streams with projected read models — the same pattern as rentals. Reads hit the projection; writes fire events and dual-write the read model.
Finance list pages live under the Finance navigation section, gated on finance permissions (invoices.access, credit-notes.access, payments.access).
| Area | Route |
|---|---|
| Invoices index | /invoices |
| Create invoice | /invoices/create |
| Invoice detail | /invoices/{invoice} |
| Invoice communications | /invoices/{invoice}/communications |
| Credit notes | /credit-notes |
| Payments | /payments |
| Rental checkout tab | /rentals/{rental}/invoices |
| Account invoices tab | /accounts/{account}/invoices |
Bare /finance/credit-notes and /finance/payments URLs redirect with 301 to the canonical routes above.
Checkout
Checkout generates a draft invoice from a rental's active quote version, copying line items, costs, surcharges, and tax with a penny-reconciliation invariant at fire time. Tax is frozen on the invoice — later rental changes do not alter issued documents.
Checkout is available from:
- The rental Invoices tab in the UI
POST /api/v1/rentals/{id}/invoices— see Invoices API
Invoice types
| Type | Purpose |
|---|---|
| Standard | Full invoice from the rental (default checkout type) |
| Deposit | Deposit invoice with dedicated partial-invoice tax policy |
| Pro-forma | Pro-forma invoice before final billing |
Pass type in the checkout request body to select deposit or pro-forma generation.
Source states
Checkout validates that the rental is in an allowed source state for invoicing (configured in invoices.source_states). Attempting checkout from a disallowed state returns a validation error.
Idempotency
Financial POST endpoints honour the Idempotency-Key header to prevent duplicate side effects on retry. Scoped to the bearer token; replays stored 2xx/422 responses for up to 24 hours. Applies to checkout, payment recording, and refunds.
Invoice lifecycle
| Status | Meaning |
|---|---|
| Enquiry | Editable header; not yet issued to the customer |
| Issued | Finalised; payments and credit notes apply |
| Paid | Fully settled (derived from balance) |
| Overdue | Issued past due date with outstanding balance |
| Void | Cancelled issued invoice |
Actions
| Action | When | Notes |
|---|---|---|
| Issue | Draft → Issued | Assigns document number; locks lines |
| Void | Issued (unpaid or partially paid per policy) | Negates billing intent |
| Write-off | Issued with uncollectable balance | Closes outstanding amount |
Partial-quantity invoicing lets you bill only part of an order line — useful when equipment is dispatched in stages.
Draft invoices can be edited (header fields) or soft-deleted. Issued invoices are immutable except through payments, credit notes, void, or write-off.
Payments and refunds
Record payments against issued invoices from the invoice detail page or via POST /api/v1/invoices/{id}/payments. The running balance (amount_outstanding) updates across payments and credits.
Refunds reverse a prior payment via POST /api/v1/invoices/{id}/refunds. Receipt documents can be generated through the Document Engine.
There is no standalone GET /api/v1/payments list endpoint — payments are accessed through invoice nested routes and the Finance UI index. See Payments API.
Credit notes
Credit notes mirror invoice tax with negated amounts. Issue full or partial credit from an invoice. Credit-note PDFs render through document templates.
List and show credit notes at /credit-notes or via GET /api/v1/credit_notes. See Credit Notes API.
PDFs and email
- Invoice PDF —
GET /api/v1/invoices/{id}/pdfor download from the UI - Credit note PDF — generated via the Document Engine
- Billing email —
POST /api/v1/invoices/{id}/emaildispatches invoice, receipt, or statement communications through merge-field templates
Human-readable download filenames are set on every document download path.
Invoice margin
When the viewer has costs.view, invoice detail shows real margin figures computed from line costs. Users without cost visibility see totals without cost breakdown — cost fields are gated, not whole resources.
Money in API responses
All monetary fields serialise as decimal strings (e.g. "125.50"), not floats or raw integers. Values are stored in integer minor units internally. Tax follows the framework's ex-tax model with tax computed finally and grouped by tax type.
Using it in the app
Finance screens live under the Finance section of the main sidebar and header mega-menu. The whole Finance block — Invoices, Credit Notes, and Payments — is visible only when the user holds invoices.access. Individual pages then check finer-grained invoice permissions (invoices.view, invoices.create, and so on).
| Screen | Route | Mount permission |
|---|---|---|
| Invoices index | /invoices |
invoices.access |
| Credit notes index | /credit-notes |
invoices.view |
| Payments index | /payments |
invoices.view |
| Invoice detail | /invoices/{invoice} |
view policy on invoice |
| Invoice communications | /invoices/{invoice}/communications |
view policy on invoice |
| Rental Invoices tab | /rentals/{rental}/invoices |
invoices.view |
| Account Invoices tab | /accounts/{account}/invoices |
invoices.view |
Finance index pages
All three Finance list pages use the shared data table with column toggles, export, Ransack-style column filters, and a live filter selector (Default / System / Shared / My Live filters). Saved views drive columns, filters, and sort — the same machinery as other Phase-4 index pages.
Invoices (/invoices) shows status chips across the top (All, Draft, Issued, Paid, Overdue, and so on) with live counts. Columns include Number, Type, Status, Account, Warehouse, Total, Outstanding, and Issued. Overdue invoices carry a red Overdue badge in the Status column. The toolbar offers New Draft when the user has invoices.create.
Credit Notes (/credit-notes) mirrors the same pattern with status chips and columns for Number, Invoice, Account, Issued, Total, and Status. The header mega-menu labels this area Refunds & adjustments.
Payments (/payments) lists every payment and refund recorded against invoices. Columns include Reference, Invoice, Account, Method, Paid date, Amount, and Type — refunds appear with an amber Refund badge.
Invoice detail — quote to cash
Open any invoice from the index, a rental, or a account tab. The page header shows the invoice number, status badge (colour-coded — red for overdue), type badge, and deposit/pro-forma chips when applicable.
Header actions are policy-gated and appear only when legal for the current status:
| Action | Permission | When shown |
|---|---|---|
| Edit | invoices.edit |
Draft only |
| Issue | invoices.issue |
Draft |
| Record Payment | payments.record |
Issued with balance |
| Credit Note | invoices.credit |
Issued with creditable balance |
| Download PDF | — | Non-draft |
| — | Non-draft (dispatches billing communication) | |
| Void | invoices.void |
Issued per policy |
| Delete | invoices.delete |
Draft only |
The Overview tab shows line items, payments, credit notes, and a summary sidebar (subtotal, tax, total, paid, outstanding). When the viewer has costs.view, line items gain a Margin column and the summary panel shows an aggregate Margin figure; without that permission the cost columns are omitted entirely — totals remain visible.
The Communications tab lists billing emails and related customer communications for this invoice (channel, status, title, timeline) using the same log table component as the admin Communication Log.
Credit notes from invoice show: click Credit Note to open a modal. Choose full credit or partial credit with per-line quantities capped at the remaining creditable amount. Notes are optional. Issued credit notes appear in the Credit Notes panel and on /credit-notes.
Payments: Record Payment opens a modal pre-filled with the outstanding balance. Enter amount, method (bank transfer, card, cash, and so on), and an optional reference.
PDF download uses an existing generated document when available; otherwise it queues generation and prompts you to retry shortly. Email triggers the invoice billing communication through the notification engine.
Rental Invoices tab
On a rental, open the Invoices tab (/rentals/{rental}/invoices, badge = invoice count). The tab requires invoices.view; generation buttons require invoices.create plus an allowed rental source state.
| Button | Creates |
|---|---|
| Invoice | Standard invoice from the active quote (hidden when fully invoiced) |
| Generate Deposit | Deposit invoice using the default deposit percentage setting |
| Generate Pro-forma | Pro-forma invoice |
When every order line has been fully invoiced, the primary Invoice button is replaced by a Fully invoiced label — no further standard invoices can be generated from the UI. Progressive partial-quantity checkout is supported by the API (lines on the checkout payload) but is not exposed as a line-selection UI on this tab yet.
Successful generation redirects to the new invoice's detail page.
Account Invoices tab and statement
The account Invoices tab (/accounts/{account}/invoices) shows an Outstanding Balance summary panel and a table of all invoices for that account. Users with accounts.view can pick a Statement warehouse and click Email Statement to dispatch a account statement communication (SendAccountStatement).
Permissions summary
| Permission | UI effect |
|---|---|
invoices.access |
Finance nav links (Invoices, Credit Notes, Payments) |
invoices.view |
View lists, rental/account tabs, invoice detail |
invoices.create |
New Draft, rental generate buttons |
invoices.edit |
Edit draft header |
invoices.issue |
Issue action |
invoices.void |
Void action |
invoices.delete |
Delete draft |
invoices.credit |
Credit Note modal |
payments.record |
Record Payment modal |
costs.view |
Invoice margin column and summary |
Recording refunds and write-offs are not yet exposed in the invoice detail UI — use the Payments API for refunds.
Related documentation
- Tax & Compliance — zone-aware tax frozen at checkout
- Documents & Templates — PDF rendering and numbering
- Notifications & Comms — billing email delivery
- Invoices API — REST surface and abilities