Skip to content

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 = $0

Always. Hours are tracked for cost purposes only.

T&M (Time & Materials)

Revenue = hours_logged × billing_rate

Accrues 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_cost

Where 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_cost

This 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 × 100

Shown 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 × 100

Direct 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_ratehourly_cost
What it isWhat you charge the clientWhat the agency pays the team member
Used forRevenue calculationCost calculation
Where22. Projects Master13. Rate History
Who sees itOwnerOwner only
Typical values$125–$200/hr$50–$120/hr

The gap between them is the gross margin per hour.


Financial access by role

DataOwnerManagerUser
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_hours

Positive = under budget. Negative = over budget.

Cost variance:

cost_variance = estimated_cost − actual_labor_cost

Variance %:

variance_pct = cost_variance ÷ contract_value

These 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.