Key Features
Budget by Project & Phase
- Planned labor, material, subcontractor and equipment cost lines
- Budget per project or per phase (project task)
- Change orders tagged and tracked separately from the original budget
Live Budget vs. Actual
- Rolls up actual timesheet, purchase and bill cost per phase
- Separate "committed" (open POs) vs. "actual" (posted bills) view
- Dashboard with KPI cards, budget-vs-actual and cost-breakdown charts
Forecast & Reporting
- Simple cost-to-complete forecast from % complete vs. cost incurred
- Printable/exportable Job Cost Report per project
- Builds on core Project, Purchase, Timesheets and Accounting -- no parallel data model
Description
What's included
Link Costs to Projects -- purchase orders and bills tagged to a project phase
Live Budget vs Actual -- KPI dashboard, per-phase list and pivot views
Track Change Orders -- flagged separately from the original budget
Export Job Cost Reports -- printable PDF per project for client billing
Cost-to-complete forecast based on % complete vs. cost incurred
Job Costing menu with Dashboard, Phase Overview and Budget Lines
Works with Community and Enterprise -- no Enterprise-only dependency
Automated tests covering the budget-vs-actual and forecast logic
How it works -- full walkthrough
Every screenshot below is from a real, freshly-installed instance, in the order a new user would actually click through: create a project, set up its budget, tag real purchases/bills to a phase, then watch the numbers update live.
1. Start in the Project app
Job costing is not a separate app -- it lives inside every project you already manage.

2. Create a project
The normal Odoo "New Project" dialog -- nothing job-costing-specific to configure yet.

3. The Job Costing tab starts empty
A new tab appears on every project. Everything reads zero until you add a budget line -- "Add a line" is right there.

4. After budgeting: the KPIs light up
Once budget lines exist and real costs start coming in from timesheets/purchases/bills, Planned, Actual, Committed and the forecast all populate live -- no manual recalculation.

5. Budget lines: split by phase and cost type
Each line is Labor, Material, Subcontractor or Equipment, optionally tied to a phase (a project task) and optionally flagged as a Change Order.

6. A phase with real Actual Cost (Foundation)
Logged timesheets plus a posted vendor bill for this phase -- Actual Cost is $8,200, Committed is still $0.

7. A phase with real Committed Cost (Framing)
This phase has a confirmed purchase order that hasn't been billed yet -- $3,600 shows as Committed, not Actual.

8. Where Committed Cost comes from: the purchase order
This module adds a "Job Phase" column right on the PO line. Confirm the order and its cost becomes Committed on that phase.

9. Where Actual Cost comes from: the vendor bill
Same Job Phase column on the bill. The moment it's posted, that cost moves from Committed to Actual.

10. The Job Costing dashboard
One page across every project: KPI cards, Budget vs. Actual by phase, a cost-type breakdown, and a projects table flagged On Track / At Risk.

11. Phase Overview -- every phase, one list
Useful when you manage several jobs at once and want every phase's numbers side by side without opening each project.

12. Budget Lines -- the raw list
Every planned-amount line you've entered, across every project, filterable and exportable like any other Odoo list.

13. Hand it to a client: the printable Job Cost Report
Budget vs. Actual by cost category, with variance and % used -- the end point of the whole flow.

Actual Cost vs. Committed Cost, explained
These are the two numbers people ask about most, so here's exactly what feeds each one -- no guessing.
Actual Cost -- money already spent
Two sources, added together:
- Labor: every logged timesheet entry on the project/phase.
- Bills: posted vendor bills (and refunds) whose lines are analytic-distributed to the project, apportioned by that distribution %.
A draft bill doesn't count. A cancelled or unposted bill doesn't count. Only work that's logged and money that's actually been billed shows up here -- it's the ground truth of what a phase has cost so far.
Committed Cost -- money promised, not billed yet
One source:
- Open purchase orders: confirmed POs (state = Purchase Order) that still have quantity left to invoice, apportioned by the un-invoiced share of each line.
A draft RFQ isn't committed -- you haven't ordered anything yet. Once you confirm the PO, the vendor is expecting payment even before the bill arrives, so that amount is "committed" but not yet "actual." As soon as it's billed and posted, it moves out of Committed and into Actual automatically -- nothing to re-enter.
Worked example, from the screenshots above
The Foundation phase had 80 hours of logged labor plus a fully-billed $3,000 rebar order → its Actual Cost is $8,200, Committed is $0. The Framing phase has a confirmed $3,600 lumber order that hasn't been billed → its Committed Cost is $3,600, and its Actual Cost (from framing labor timesheets alone) is $2,600.
Add both together and you get Incurred Cost (Actual + Committed) -- the number the module uses for Budget Variance, the Cost-to-Complete forecast, and the "at risk" flag on the dashboard. Actual tells you what's already gone; Committed tells you what's already promised. Neither alone answers "are we still on budget?" -- you need both.
Specifications
| Versions | 19.0, 20.0 |