Skip to content
Pro · Professional planRequires Easy Invoice Pro with a Professional (or Agency) license. Compare plans →

Expense Tracking & Reimbursable Items

Track project expenses — software subscriptions, mileage, materials, contractor costs, travel — with receipt uploads. Apply per-expense markup and roll selected unbilled expenses into a draft invoice in one click. Same mental model as Time Tracking, for everything that isn't billable hours.

When to use it

  • You bill clients for expenses you incur on their behalf (project costs, AWS bills, paid stock photography, sub-contractor fees, travel)
  • You're already using Time Tracking for billable hours and need the matching workflow for everything else
  • You want receipt photos stored alongside expense entries for tax-time reconciliation
  • Your current expense workflow is "type it as a line item by hand each time and hope you remember every receipt"

Enabling

  1. Open Easy Invoice → Addons
  2. Find Expense Tracking & Reimbursable Items
  3. Click Activate

The Easy Invoice → Addons → Expense Tracking entry appears in the in-app sidebar. On first activation the addon creates one custom table:

sql
{prefix}_easy_invoice_expenses

Schema:

ColumnTypeNotes
idBIGINT PK
user_idBIGINTWho logged the entry — for sales-rep attribution.
client_idBIGINTNullable — solo / overhead costs allowed.
projectVARCHAR(255)Free-text label.
categoryVARCHAR(64)software / mileage / materials / contractor / travel / meals / other.
descriptionTEXTThe label that ends up as the invoice line item.
amountDECIMAL(12,2)Your cost (before markup).
currencyCHAR(3)ISO 4217 — defaults to your site currency.
markupDECIMAL(5,2)Markup % applied when converting to invoice line.
expense_dateDATEWhen the cost was incurred.
receipt_attachment_idBIGINT NULLWP media-library attachment ID for the receipt.
is_billableTINYINT(1)1 = billable to client, 0 = absorbed cost.
billed_invoice_idBIGINT NULLSet when expense is rolled into an invoice.
notesTEXTOptional internal note.
created_at, updated_atTIMESTAMP

Indexed on client_id, category, billed_invoice_id, expense_date, and the composite (is_billable, billed_invoice_id) that powers the "billable + unbilled" filter. Deactivating the addon keeps the table so you don't lose entries if you re-enable later.

Logging an expense

The page has a quick-entry form along the top with these fields:

FieldRequiredWhat it does
DateWhen the expense was incurred. Defaults to today.
ClientoptionalPick a client; leave blank for internal / overhead costs.
ProjectoptionalFree-text label that's prefixed onto the invoice line: [Project] Description.
Categorysoftware / mileage / materials / contractor / travel / meals / other.
DescriptionWhat the expense was for — this becomes the invoice line item description.
AmountYour cost.
CurrencyDefaults to your site currency.
Markup %optionalPer-expense markup; defaults to the global default (see Settings below).
BillabletoggleCheck to bill it to the client; uncheck to track as internal cost.
ReceiptoptionalUpload via the WP media library (image or PDF).
NotesoptionalInternal note; not shown on the invoice.

Click Log expense to save. The list below the form updates immediately.

Converting expenses into an invoice

The list table has a checkbox column for unbilled, billable expenses. Pick the rows you want, pick a client from the toolbar dropdown, click Create invoice from selected. The addon:

  1. Creates a draft easy_invoice post titled Expenses Invoice — {Client Name} with the next available invoice number.
  2. Atomically claims the selected rows by linking them to the new invoice ID (UPDATE ... WHERE billed_invoice_id IS NULL — race-free even when two admins do this concurrently).
  3. Populates the invoice's line items: one per claimed expense, with quantity 1 and rate = amount × (1 + markup/100). The project prefix is included in the description.
  4. Stores source metadata on each line item (source: expense_tracking, expense_id, category, base_amount, markup_pct) so you can trace any line item back to its receipt.
  5. If another admin won the race and you ended up with no claimed rows, the empty draft is rolled back automatically.

Filters

The list has filters for:

  • Client (any client present in the expense table)
  • Category (any of the 7 categories)
  • Status — Unbilled (default) / Billed / All
  • Billable — Any / Billable / Internal

The summary cards above the form reflect the filter set, not just the visible page — so "Billable, unbilled" tells you exactly how much is pending invoicing across your entire history.

Settings

The addon appends one setting to Easy Invoice → Settings:

  • Default Expense Markup (%) — applied to new entries unless overridden per-entry. Default 0.

Hooks

HookWhen
easy_invoice_expense_tracking_invoice_created (invoice_id, client_id, claimed_entries)A draft invoice has been generated from claimed expenses.
easy_invoice_audit_log('expenses_invoiced', …)If the Team Roles & Audit Log addon is active, every conversion gets an audit-log row.

Pairs with

ProExpense Tracking is part of the Professional tier. Upgrade to Professional →