Forecasting revenue on contracts that don't behave.
Four fee types, two invoicing models, and project timelines that move. I built the Power BI model leadership and finance use to track revenue and invoicing in real time, project month-end totals, and generate the detail behind 179D invoices.
01 — Problem
Nobody could say what was going to bill next.
Revenue and invoicing weren't the same number, and neither was straightforward to project. Revenue depended on hours already worked and what teams still planned to deliver that month. Invoicing depended on the client's fee type, project milestones, planned hours, and when those milestones were expected to occur.
Leadership needed to know not just what had been invoiced so far, but where the month was likely to finish — by fee type, client, and project. Finance also needed enough detail behind those numbers to explain an invoice when a client questioned it.
02 — Messy data
Four fee types. Two invoicing models.
The first discovery was that the four contract types don't vary by degree. They recognise revenue on fundamentally different logics:
- Trigger-then-monthly — hourly and fixed fee. Nothing bills until a project reaches its launch phase. At that point one catch-up invoice covers everything done to date, and the contract bills monthly from then on. The uncertainty isn't the rate, it's the launch date.
- Milestone-percentage — fixed percent. A defined share of the contract bills at each milestone. Here the uncertainty is which milestones land in which month.
- Flat — retainer. Near-deterministic. The only real risk is non-renewal.
On top of that, the source data fought back: milestone dates buried at the project level of a Salesforce account → opportunity → project hierarchy, duplicate records, missing dates, and contracts amended mid-flight without the original terms being versioned.
03 — Approach
Model the business rules, then roll them up.
- Separate revenue from invoicing. Revenue follows actual and planned hours; invoicing follows fee rules and project progress.
- Combine actuals with what's ahead. Actual activity to date is paired with planned hours and milestone dates to project where the month will finish.
- Calculate first, aggregate second. Logic runs at the project level before rolling up by client, fee type, and month.
- Keep it traceable. Every company-level number can be drilled back to the projects producing it.
04 — Architecture
How it fits together.
05 — The report
What leadership can see before the month is over.
A recreation with synthetic clients and numbers, same structure as production. Switch pages, filter by contract type, and click any client to drill through to their milestones.
Invoiced, June
$296K
Forecast, June
$310K
Variance
−4.5%
two milestones slipped to July
| Client | Contract type | Contract | Invoiced to date | June invoice |
|---|
Showing six of 100+ active clients.
06 — Outcome
Finance bills from this now.
100+
active clients billed from the engine monthly
4
contract types forecast under their own rules
Manual → generated
invoice detail produced by the report
The executive team and Finance now use the report to plan month-end numbers, investigate billing questions, and trace totals back to individual projects. The added visibility has also surfaced work that may otherwise have gone uninvoiced.
07 — Lessons
What I'd tell myself at the start.
- Business rules were the model. This problem didn't need ML; the useful signal already existed in hours, milestones, project progress, and fee structures.
- Traceability creates trust. A forecast became much more useful once Finance could move from a company total to the projects behind it.
- Reporting can replace process. The same system built to forecast the month now also supports invoice preparation, billing checks, and identifying potentially missed invoices.
Next project
Fashion demand notes