One job ID that revenue, hours, subcontractors, materials and travel attach to: required job-record fields, six leak points and a worked margin example.
Key takeaways
- The job ID is a short, stable technical key - not a client name and not a project name. Names change, keys do not.
- Revenue enters at one point. Cost enters at six or more, and each is a place where the identifier can fall off.
- Work with no ID goes into an exceptions queue with an owner, never into a "general" bucket. A general bucket that grows is the signal that measurement has stopped working.
- Reported margin and real margin usually diverge because of work done and never billed, not because the price was set too low.
Project profitability can only be measured when every money row and every time row carries the same job ID. An employee logging hours against a client name, a supplier invoicing against a purchase order number, and a fuel charge landing on a company card are each individually correct and cannot be added together. The identifier is what joins them.
The job ID, and the six places a cost tries to enter without one
A job ID is a short string created once and never changed - J-2026-0412, for example. It is not the client name, because a client orders several jobs; not the quote number, because a quote can split into two projects; and not a folder in a drive, because you cannot attach a folder to a row in a report.
Cost arrives from six directions, and each needs a field that holds the ID:
- Employee hours - entered on a form or a clock, with a mandatory job field.
- Subcontractors - an invoice arriving by email with no technical link to the project unless somebody types the ID.
- Materials and purchasing - a purchase order raised against a supplier, usually carrying its own number instead.
- Travel and card expenses - a company card charge that arrives with no job context at all.
- Licences and monthly services - a recurring charge that needs splitting across projects or classifying as overhead.
- Credits and discounts - a credit note recorded at customer level rather than job level, distorting the margin of the wrong project.
The simple rule: if a document can enter the system without a complete job field, it will. The field has to be required on the intake form, not politely requested.
What the job record must hold before a single hour is logged against it
| Field | Example | Why it is needed |
|---|---|---|
job_id | J-2026-0412 | The one key every row joins to |
customer_id | C-0187 | The client, kept separate from the job |
type | fixed_price | Decides whether an hours overrun hits margin or gets billed |
budget_hours | 160 | The base for planned versus actual |
budget_cost | 42,000 | A cost budget, not a sell price |
sell_value | 68,000 | Contracted revenue, before change orders |
owner | Dana | Who approves an overrun |
status | active | Open, frozen, closed to time entry |
billable_default | true | The default for a newly entered hour |
type is the field most often skipped and the one that matters most. On a fixed-price job, an extra hour comes straight out of margin. On a time-and-materials job, the same hour also raises revenue. Identical time entry, opposite meaning, and without the field the report shows both in one column.
Where does the money actually leak on a project that looks profitable?
A website branding project: quoted at 68,000, invoiced at 68,000, therefore shown as profitable. Here is everything that attaches to that job ID by the end of the quarter:
| Component | Amount | Note |
|---|---|---|
| Revenue | 68,000 | One invoice |
| Internal hours, 214 at a cost rate of 180 | -38,520 | Budget was 160 hours |
| External designer | -9,500 | Invoice carried only a PO number |
| Licences and stock content | -2,300 | Company card charge |
| Travel to meetings | -1,150 | Never logged at all |
| Credit note issued to the client | -3,000 | Recorded at customer level |
| Actual margin | 13,530 | 19.9%, not 43% |
The distance between 43% and 19.9% is not a pricing error. The 54 hours over budget are 9,720 nobody ever asked to be paid for, and the 3,000 credit was posted against the customer, so it damaged a different project's margin. Both fixes are process, not accounting: an alert when the budget is crossed, and a rule that credits post to a job rather than to a customer. Keeping the quote, the order and the invoice on one identifier is covered separately in the quote-to-order-to-invoice chain under a single ID.
How do you handle work with no client to bill?
You give it its own ID. Internal work is still cost, and when it has nowhere to land it gets pushed onto whichever client project is open, or disappears. Four internal ID types cover almost everything:
- Pre-sales - writing proposals and first meetings. A genuine cost of selling, not a cost of the client who eventually signed.
- Warranty and rework - work on a job already closed. It stays linked to the original through a
parent_job_idfield, otherwise historical margins lie. - Internal development - tooling, templates, your own website.
- Overhead - meetings, training, management. Never split across clients, or no number is comparable between quarters.
The rule that holds this together: an hour goes to overhead only when it cannot be attributed to any of the other three. Overhead growing from 12% to 30% is not a change in the work, it is a sign that people have stopped logging.
What you look at every week
- Actual hours against budget, as a percentage, for every active job.
- Work delivered and not yet invoiced, in currency.
- Expenses captured with no job ID, as a count and a total.
- Jobs past 80% of budget and still open.
- Hours logged against a job whose status is closed.
- Credits posted to a customer rather than to a job.
The last two are quality metrics for the measurement itself. When they grow, every other number loses value - the same logic that drives hours and milestone budget control in a software studio.
What actually breaks
- Attribution by name. "Ramat Gan Council - phase 2" is typed three ways and splits into three projects in the report.
- End-of-month time entry. Reconstructing a month from memory smooths out overruns and erases exactly the information you wanted.
- A supplier invoice with no job number. It lands in general expenses and never comes back out.
- Closing a job too early. Warranty hours arriving afterwards fall onto some other job.
- Scope change with no document. The budget is never updated, so every report from then on shows an overrun that does not exist.
- A budget set at sell price. Comparing actual cost to sell price inflates margin by exactly the planned profit.
What to have ready before building
- A decision on who opens a new job and who may close one.
- A mandatory ID field on every intake form: hours, expenses, supplier invoices.
- A cost rate per role, stored with a date range rather than as a single value that gets overwritten.
- One rule for scope change: what updates a budget and what does not.
Sources
Frequently asked questions
What is the difference between a cost rate and a billing rate?
A cost rate is what an hour of work costs you, including salary and loadings. A billing rate is what the client pays. Calculating profitability against the billing rate inflates margin by exactly the planned profit, which is why both have to live as separate fields on the job record.
How many open jobs is too many?
There is no right number, but there is a signal: if jobs sit in active status two months after delivery, the status no longer reflects reality and the job list stops being useful. An automatic close rule after a period with no activity, with the option to reopen, works better than discipline.
Do hours have to be logged to the quarter hour?
Usually not. Half-hour precision is enough to detect a 30% budget overrun, which is what you are looking for. Higher precision lowers the logging rate, and missing entries damage the measurement far more than a twenty-minute rounding error ever will.
What do we do when a client asks for a small extra with no new quote?
Raise a documented scope change even when it is not billed, and update the budget. Small unrecorded additions are the single most common explanation for a project that ran 25% over with nobody able to point at when it happened.
Keep reading
automation
Small-Business Bank Reconciliation: The Daily Process That Separates Balance From Available Cash
automation
Digital Signatures on Quotes: Evidence of a Process, Not Just an Approve Button
automation
Orders Arriving on WhatsApp: Turning a Conversation Into a Work Queue Without Losing a Commitment
Related service
Business Automation
I build custom automations that remove repetitive work end to end.
About the author
Yehonatan Saadia
Freelance automation, web & MVP developer
I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.
