Expense Tracking
Expense tracking in NexusRMS lets you record business costs, link them to projects for accurate profitability reporting, and optionally charge expenses back to clients with a configurable markup. Every expense follows an approval workflow that ensures proper oversight before reimbursement.
Creating an expense
Navigate to Financial > Expenses and click New Expense. Each expense is automatically assigned a unique expense_number in the format EXP-YYYY-#### (e.g., EXP-2026-0001). The number auto-increments per calendar year, padded to four digits. The following fields are available:
- description — A detailed description of what the expense covers
- category — One of six predefined categories (see below)
- amount — The expense amount before tax, stored to two decimal places
- tax_amount — The tax or VAT portion of the expense
- total_amount — The full amount including tax (amount + tax_amount)
- currency — ISO 4217 currency code (e.g., GBP, USD, EUR)
- expense_date — The date the expense was incurred
- payment_method — How the expense was paid (e.g., company card, cash, bank transfer)
- notes — Client-visible notes that may appear on reports
- internal_notes — Staff-only notes not visible to clients
Expense categories
NexusRMS provides six standard categories to classify expenses:
- equipment — Costs related to equipment hire, purchase, or consumables
- labor — Crew wages, freelancer payments, and staffing costs
- materials — Raw materials, supplies, and physical goods
- transport — Vehicle hire, fuel, mileage, and delivery charges
- subcontract — Third-party subcontractor or outsourced service fees
- other — Any expense that does not fit the above categories
Expense statuses
Every expense progresses through a five-stage status workflow:
- draft — The expense has been created but not yet submitted for review
- pending_approval — The expense has been submitted and is awaiting manager approval
- approved — A manager has approved the expense for reimbursement
- paid — The expense has been reimbursed to the claimant
- rejected — The expense was declined by a manager
Approval workflow
The approval process tracks who submitted and who approved each expense:
- created_by_id — Records the user who originally submitted the expense
- approved_by_id — Records the user who approved the expense
- approved_at — Timestamp of when approval was granted
Approval actions
- approve(User $approver) — Validates that the expense canBeApproved() (status must be pending_approval), then sets the status to approved and records the approver and timestamp
- reject() — Sets the status to rejected. Only expenses with status pending_approval can be rejected.
- markAsPaid() — Marks the expense as paid after reimbursement. Only expenses with status approved can be marked as paid.
Edit and delete rules
- canBeEdited() — Returns true only when the status is draft or pending_approval. Once approved, paid, or rejected, the expense is locked.
- canBeDeleted() — Returns true only when the status is draft or rejected. Approved and paid expenses cannot be deleted to maintain the audit trail.
Client billing
Expenses can optionally be charged back to the client with a markup:
- billable_to_client — A boolean toggle that determines whether this expense should appear on a client invoice
- markup_percentage — The percentage markup to apply when billing the client (e.g., 15.00 for a 15% markup)
- client_charge_amount — The final calculated amount the client will be charged
The calculateClientCharge() method computes the client charge using the formula: total_amount multiplied by (1 + markup_percentage / 100). For example, a £200 expense with a 15% markup produces a client charge of £230.00. If billable_to_client is false or no markup_percentage is set, the method returns the total_amount unchanged.
Project linking
Each expense can be linked to a project via the project_id field. When linked, the expense appears in the project's Financial tab and contributes to the project's profitability calculations. Use the byProject() scope to filter expenses for a specific project.
Supplier linking
Expenses can also be linked to a supplier record via the supplier_id field. This allows you to track spending by vendor and generate supplier-level cost reports. Use the bySupplier() scope to filter expenses for a specific supplier.
Receipt upload
The receipt_url field stores the URL to an uploaded receipt image or PDF document. Attach receipts when submitting expenses to provide evidence for the approval workflow. Uploaded files are stored in your tenant's secure storage.
Filtering expenses
NexusRMS provides several query scopes for filtering the expenses list:
- byStatus(status) — Filter by any of the five statuses
- pending() — Show only expenses awaiting approval (status = pending_approval)
- approved() — Show only approved expenses
- paid() — Show only reimbursed expenses
- byProject(projectId) — Filter by a specific project
- bySupplier(supplierId) — Filter by a specific supplier
- billable() — Show only expenses marked as billable_to_client
Tips
- Submit expenses promptly — Move expenses from draft to pending_approval as soon as the receipt is attached. This keeps the approval queue current and prevents end-of-month backlogs.
- Always link to a project — Setting the project_id ensures the expense appears in project profitability reports. Unlinked expenses are tracked as general overheads.
- Use markup for pass-through billing — Enable billable_to_client and set a markup_percentage to transparently charge expenses to clients with your agreed handling fee.
- Attach receipts before submitting — Expenses without a receipt_url may be rejected during the approval process. Upload the receipt first, then change the status to pending_approval.
- Review rejected expenses — Rejected expenses can be deleted and re-created with corrected information. Check internal_notes from the approver for the reason.
- Use categories consistently — Consistent categorisation enables accurate cost breakdowns in financial reports. Choose the most specific category available rather than defaulting to other.
Was this article helpful?