Financials & P&L
VERA calculates revenue, cost, and margin live from time entries, rate history, and project metadata. No manual spreadsheet formulas needed on the operational side — the data flows automatically to the reporting sheet.
Revenue by project type
Revenue recognition depends on the project type.
Pro Bono
Revenue = $0Always. Hours are tracked for cost purposes only.
T&M (Time & Materials)
Revenue = hours_logged × billing_rateAccrues per entry. Work should stop when hours_budget is exhausted — logging beyond that point earns no additional revenue and runs at pure cost.
Fixed Price
Revenue = (hours_logged ÷ hours_budget) × contract_value
capped at contract_value (100%)Revenue accrues as a percentage of completion. Logging 50% of the budgeted hours recognises 50% of the contract value. When the hours budget is used up, 100% of the contract value has been recognised.
Retainer
Revenue is the fixed monthly retainer fee — this is not calculated per-entry. Instead, VERA tracks burn rate: whether the agency is delivering the contracted hours and therefore earning the fee. See Retainers.
Agile (Sprint-based)
Revenue per sprint = (hours_logged ÷ hours_budget) × contract_value
capped at contract_value
when sprint status = 'active'Sprints with status = 'planned' recognise $0 — work has not started. Once activated, revenue accrues the same way as Fixed Price but per-sprint.
Cost calculation
Cost = what the agency pays to deliver the work.
Standard calculation:
Cost = hours_logged × hourly_costWhere hourly_cost is the rate effective on the entry's date, looked up from Rate History.
Vendor with liability:
Cost = agreed_amount × LEAST(1, elapsed_days / total_days)Where elapsed_days = today − created_at and total_days = deadline − created_at. The agreed_amount accrues linearly from the liability creation date to the project deadline — cost is not recognised in full on day one. If total_days ≤ 0 (deadline on or before creation date), the full agreed_amount is recognised immediately. A cancelled liability contributes zero cost.
This is the same accrual shape as vendor implied hours — both cost and hours grow at the same rate from creation to deadline.
Vendor implied hours (hours budget)
agreed_amount governs cost. agreed_hours governs the hours budget. Because vendors don't log time, VERA accrues their contracted scope linearly from the liability creation date to the project deadline:
implied_hours = agreed_hours × LEAST(1, elapsed_days / total_days)
implied_cost = agreed_amount × LEAST(1, elapsed_days / total_days)Both figures use the same fraction: elapsed_days = today − created_at, total_days = deadline − created_at. Implied hours are added to the team's logged hours in all burn-down calculations — get_project_status, get_project_health, get_project_info, get_proactive_alerts, and the client dashboard. Implied cost is included in get_project_accrual_basis. A cancelled liability contributes zero in both cases. If the deadline has passed, both are used in full.
Rate History lookup
Rate History is append-only and effective-date versioned. When calculating cost for an entry dated 2026-01-15:
Look up 13. Rate History WHERE user_id = X
Find MAX(effective_from) WHERE effective_from ≤ 2026-01-15
Use that row's hourly_costThis means a salary increase on 2026-03-01 affects costs from that date forward, but leaves all historical entries unchanged.
Margin
Margin = Revenue − Cost
Margin % = Margin ÷ Revenue × 100Shown as:
- Dollar amount to Owners
- Percentage only to Managers
Contribution Margin
Contribution Margin (CM) is a tighter measure of profitability than overall profit. It strips out overhead so you can see what the client work itself is actually contributing.
CM = Revenue − Direct Labour − Vendor Cost
CM % = CM ÷ Revenue × 100Direct labour is the cost of time logged by non-vendor people (FTE and contractors) on client-category projects. Time on internal projects (admin, meetings, training, PTO, sick, leave) is not subtracted here — that overhead sits below the CM line.
Vendor cost is the pro-rata liability accrual for the period (same formula as the cost calculation above). It is subtracted in CM as COGS rather than rolled into the direct-labour figure.
Why CM > Profit most of the time: overhead labour (internal-category project time) is included in total cost but excluded from CM. So in months where the team spends meaningful time on admin or internal work, CM will be higher than profit. Both figures are correct — they answer different questions.
Reconciliation check: in a month where every hour is logged to client projects and there are no vendor liabilities, CM equals Profit exactly.
CM appears on the Reports → Monthly P&L tab (owner/admin only) as a column alongside Revenue, Cost, Profit, Margin, and LER. The Total row shows the trailing business-wide CM.
The billing_rate vs. hourly_cost distinction
These are two different rates that are easy to confuse:
billing_rate | hourly_cost | |
|---|---|---|
| What it is | What you charge the client | What the agency pays the team member |
| Used for | Revenue calculation | Cost calculation |
| Where | 22. Projects Master | 13. Rate History |
| Who sees it | Owner | Owner only |
| Typical values | $125–$200/hr | $50–$120/hr |
The gap between them is the gross margin per hour.
Financial access by role
| Data | Owner | Manager | User |
|---|---|---|---|
| Revenue ($) | ✅ | ❌ | ❌ |
| Cost ($) | ✅ | ❌ | ❌ |
| Margin ($) | ✅ | ❌ | ❌ |
| Contract value | ✅ | ❌ | ❌ |
| Billing rate | ✅ | ❌ | ❌ |
| Hourly cost (rates) | ✅ | ❌ | ❌ |
| Budget % used | ✅ | ✅ | ❌ |
| Hours logged | ✅ | ✅ (assigned projects) | ✅ (own only) |
Managers see percentages, not dollars. Users see their own hours only.
Monthly report
generate_monthly_report produces a portfolio view for the current (or specified) month:
- Per-project: hours logged, budget hours, utilisation %, burn status
- Revenue at risk: projects over budget
- Open unresolved items count
- Summary: over budget count, under-utilised count
Available to Owners (with dollar amounts) and Managers (with percentages, own projects only).
Variance
Variance appears in two contexts:
Hour variance (from 22. Projects Master formula columns):
hour_variance = estimated_hours − actual_hoursPositive = under budget. Negative = over budget.
Cost variance:
cost_variance = estimated_cost − actual_labor_costVariance %:
variance_pct = cost_variance ÷ contract_valueThese formula columns (I–O in Projects Master) are user-maintained — VERA writes the source data (cols A–H) but the variance calculations are Google Sheets formulas set up in the reporting sheet.
Segmentation
The Reports → Segmentation tab (owner/admin only) breaks down margin and LER by client, answering "which customers deliver better margins?"
By customer
Each client row shows recognised revenue, accrued cost, margin, LER, hours logged, and project count — aggregated across all client projects in scope (active only, or active + completed). Rows are sorted by margin descending so the highest-margin clients surface first. Clients with no project activity in scope are omitted.
The figures use the same revenue/cost basis as the dashboard and the PM comparison view — they reconcile to the same grand total. Vendor liability cost is included in the cost figure (pro-rata accrual).
The active/all scope toggle lets owners compare the in-flight portfolio against the full historical picture.
By discipline
The By discipline view answers two questions in one: "which type of work delivers better margins?" and "for disciplines where we use both internal staff and vendors, who wins by LER?" The second is the make-vs-buy question — higher LER means more revenue per unit of cost.
How revenue is attributed: each project's recognised revenue is split across all contributing sources (internal time entries and vendor implied hours) in proportion to each source's share of total hours on that project. This means internal discipline figures are slightly lower than they would appear in an internal-only calculation when a vendor also contributed — which is the correct picture, not a regression.
How cost is computed: internal cost = Σ entry hours × effective rate on the entry date (same per-entry rate rule as the project P&L). Vendor cost = pro-rata implied cost from active liabilities (same accrual formula as the main dashboard).
Internal vs outsourced comparison: when a discipline has both internal time entries and a vendor with that discipline, the table expands to show paired sub-rows — Internal and Outsourced — each with its own Revenue, Cost, Margin, and LER. The higher-LER source is marked ▲ better. Disciplines with only one source show a single row and no winner marker.
Discipline mapping (internal): time entries are attributed to a discipline by inverting disciplines.default_task_id — an entry with task "Design" maps to the discipline that has "Design" as its default task.
Discipline mapping (vendor): each vendor's liability is attributed to the discipline set on their user record.
Unmapped entries: a time entry whose task is not any discipline's default task, or a vendor with no discipline set, goes into an Unmapped bucket. The Unmapped row is always sorted last, regardless of its margin.
Setting up discipline mapping: go to /console/disciplines and set a default task for each discipline. Once a discipline has a default task, time entries logged against that task are attributed to the discipline automatically.
Estimates vs actuals by customer and by discipline
The Reports → Estimates vs actuals tab answers the same grouping questions as Segmentation, but on hours rather than money: how did our budgeted effort compare to what we actually logged, by client and by type of work?
A By project | By customer | By discipline client-side toggle (default: By project) re-groups the same hours without a page reload. The same Active projects / All projects scope toggle that controls the Segmentation tab now also filters Estimates vs actuals — flipping scope re-queries the server and returns you to the same tab.
By customer rolls each project's budgeted and actual hours up to its client. The grand total across all clients matches the grand total in the By project view.
By discipline maps each active envelope's allocated hours (budget) and each time entry's logged hours (actual) to a discipline via the same task→discipline default inversion used by the Segmentation tab. Work whose task is not any discipline's default — or entries with no task — falls into an Unmapped bucket, which is always sorted last. Budget and actual totals reconcile exactly to the By project view.
The figures are hours only — no revenue, no cost, no margin. For financial segmentation, use the Segmentation tab.