Professional invoicing, payment tracking, and financial reporting for contractors. Inspired by FreshBooks, Stripe Invoicing, and ServiceTitan.
InvoiceDashboard component with stat tiles and filterable list
useQuery(api.billing.invoices.list) · useMemo for filter
InvoiceCreateForm — job selector auto-fills customer + line items
useMutation(api.billing.invoices.create) · pricebook lookup
InvoiceDetail — full invoice with payment timeline and actions
useQuery(api.billing.invoices.getById) · Stripe payment link
PaymentRecordForm — partial payments with remaining balance
useMutation(api.billing.payments.record) · balance recalculation
RecurringInvoiceList — active/paused toggles with revenue summary
useQuery(api.billing.recurring.list) · useMutation(api.billing.recurring.toggle)
EstimateList — status filters with one-tap convert to invoice
useQuery(api.billing.estimates.list) · convertToInvoice mutation
ExpenseTracker — receipt scanning, categories, mileage GPS
useQuery(api.billing.expenses.list) · Clearbit Logo API for vendor icons, fallback to category IconCircle
FinancialReports — revenue charts, AR aging, tax summary
useQuery(api.billing.reports.summary) · useQuery(api.billing.reports.topCustomers)
// Invoice flow
InvoiceDashboard → app/(contractor)/billing/index.tsx
InvoiceCreateForm → app/(contractor)/billing/create.tsx
InvoiceDetail → app/(contractor)/billing/[id].tsx
PaymentRecordForm → app/(contractor)/billing/record-payment.tsx
// Supporting screens
RecurringInvoiceList → app/(contractor)/billing/recurring.tsx
EstimateList → app/(contractor)/billing/estimates.tsx
ExpenseTracker → app/(contractor)/billing/expenses.tsx
FinancialReports → app/(contractor)/billing/reports.tsx
// Invoice CRUD
api.billing.invoices.list
args: { status?: string, limit?: number }
returns: Invoice[]
api.billing.invoices.getById
api.billing.invoices.create
api.billing.invoices.send
api.billing.invoices.void
// Payments
api.billing.payments.record
api.billing.payments.listByInvoice
// Estimates & Recurring
api.billing.estimates.list
api.billing.estimates.convertToInvoice
api.billing.recurring.list
api.billing.recurring.toggle
// Expenses & Reports
api.billing.expenses.list
api.billing.expenses.create
api.billing.reports.summary
api.billing.reports.topCustomers
InvoiceStatus =
'draft' | 'sent' | 'viewed'
| 'overdue' | 'partial' | 'paid' | 'void'
EstimateStatus =
'draft' | 'sent' | 'accepted' | 'declined'
PaymentMethod =
'cash' | 'check' | 'card'
| 'transfer' | 'zelle'
RecurringFrequency =
'weekly' | 'monthly' | 'quarterly'
ExpenseCategory =
'materials' | 'fuel' | 'tools'
| 'permits' | 'subcontractor' | 'other'
invoices {
contractorId: Id<contractorUsers>
jobId?: Id<jobs>
customerId: Id<posterUsers>
invoiceNumber: string
status: InvoiceStatus
lineItems: LineItem[]
subtotal: number
taxRate: number
discount: number
total: number
paymentTerms: string
dueDate: number
stripePaymentLink?: string
}
expenses {
contractorId: Id<contractorUsers>
jobId?: Id<jobs>
category: ExpenseCategory
amount: number
receiptUrl?: string
vendor: string
mileage?: number
}