Billing policies — auto-invoice + completion gates
Three firm-wide toggles in Settings → Company Profile → Billing Policy control the most consequential M07 ↔ M11 hooks. Whether jobs auto-draft invoices on completion, whether jobs must be completed before invoicing, whether internal notifications fire. Plus per-engagement overrides on the job record.
The 3 firm-wide toggles
When job status flips to completed, system auto-drafts a tax invoice inheriting fee + lines. Saves 60 seconds per engagement; for firms with 100+ engagements/year, that's hours saved.
If ON, blocks tax-invoice send action when linked job is not completed/archived. Forces "no work, no invoice" discipline.
Internal notification fires to partner when a job hits completed. Includes auto-invoice link if drafted.
How auto-invoice-on-completion works
Hook fires post-commit
JobService::changeStatus($id, 'completed')commits the status change, then post-commit firesonJobCompleted($jobId)via EventDispatcher.Settings check
Hook reads
m11.auto_invoice_on_completion. If 0, exit. If 1, continue.Resolve fee source
Priority: linked accepted quote > job.estimated_fee > firm default. Lines mirror the quote if available; else a single line "Audit fee FY {year}" at the estimated rate.
Draft invoice
doc_type=tax_invoice, status=draft, auto-numbered. Inherits client + job + currency + T&Cs. Saves to DB.
Auto-allocate prior advances
If client has advance balance > 0, system silently allocates against the new draft up to min(advance, total).
Internal notification
Per
m11.notify_on_completion, partner gets notification with deep-link to draft invoice. Flash message on job dashboard: "Auto-drafted invoice INV/2026/0042 (clickable link)".
The completion gate
If m11.require_job_completion_for_invoice is ON:
- Tax invoice must be linked to a job (job_id required)
- Linked job's status must be completed or archived
- If not, send action is blocked with "Cannot send: linked job not completed"
- Draft can still be created — just can't be sent until job closes
Useful for firms that want strict "no premature billing" discipline. Default OFF because some firms invoice on retainer / phased basis.
Per-engagement overrides
Some jobs need to break the firm-wide policy. The job record carries:
jobs.bill_method— fixed_fee · time_and_materials · phased · retainerjobs.invoice_at_completion— explicit per-job toggle, overrides firm settingjobs.phased_billing_schedule— JSON array of {milestone, percent} for phased billing
The hook honours these in priority: per-job → firm setting → default.
Phased billing pattern
Audit firms often phase fees: 30% on engagement letter sign-off, 30% on planning complete, 40% on report issue. Configure:
- Set
jobs.bill_method = 'phased' - Set phased schedule:
[{milestone: 'planning', percent: 30}, {milestone: 'fieldwork', percent: 30}, {milestone: 'reporting', percent: 40}] - Each milestone task tick fires its phase invoice via the same auto-draft hook
- 3 separate invoices created over the engagement lifecycle
This is where the m11.require_job_completion_for_invoice gate must be OFF — phased billing intentionally bills before completion.
Job → Invoices roll-up
Job dashboard → Invoices tab shows:
- All invoices linked to the job
- Roll-up: billed (Σ grand_total) · paid (Σ paid) · outstanding (Σ balance_due) · advance held
- Status chips per invoice
- Billing timeline — last 6 status transitions across all linked invoices
One screen, all revenue context for the engagement.
Settings page walkthrough
Open Settings → Company Profile → Billing Policy
Three toggles + an explanation per setting.
Read defaults
auto_invoice ON · require_completion OFF · notify ON. Most firms keep these.
Toggle as needed
For firms enforcing strict invoicing discipline, flip require_completion ON. For firms with many phased engagements, leave OFF and rely on the per-job override.
Save
Changes apply immediately. New jobs use new policy; existing jobs unaffected unless their per-job override is null (in which case they inherit).
Set m11.auto_invoice_on_completion=1 + create a job from TPL-AA → walk it to status=completed. Within 2 seconds, a draft invoice appears in M11 with the right fee + client + lines. Check Activity log on the job — auto-invoice hook captured.
If you flip require_job_completion_for_invoice ON mid-year and have phased-billing engagements running, those phased invoices will be blocked. Set the per-job override on each existing phased engagement BEFORE flipping the firm-wide setting.
If a firm wants discount governance (e.g. partner approval for discounts > 10%), add a job-level field discount_approved_by + reject invoice send until approver is set when discount exceeds threshold. Customisation, but trivial against the existing service hooks.