From Tracked Hours to an Invoice Nobody Argues With

From Tracked Hours to an Invoice Nobody Argues With

September 9, 2026
Invoices generated from billable time

There is a particular week in every agency’s month that produces no value at all. Somebody chases timesheets. Somebody else opens a spreadsheet and starts matching hours to clients, applying rates from memory, guessing which of two similar projects a Tuesday belonged to. Then the invoices go out, and a client queries one, and the whole exercise repeats in reverse.

That week exists because the hours and the invoice live in different places. Close that gap and the week disappears, but only if you close it properly — a half-connected pipeline produces invoices that are fast and wrong, which is considerably worse than slow and right.

What the invoice actually needs

Strip it back and an invoice for time-and-materials work needs five things, and every one of them has to be unambiguous at the moment of generation.

  • Which client, and by extension which projects belong to them.
  • Which period, with the boundaries computed in a stated timezone.
  • Which hours are billable — not the same as which hours were worked.
  • At what rate, which may differ per person, per project, or both.
  • Rounded how, which sounds cosmetic and is not.

A system that can answer those five from data it already holds can generate an invoice. One that has to ask a human any of them has not removed the reconciliation, it has moved it.

Billable is a property of the work, not a decision at invoice time

The first thing we got wrong was treating billability as a filter applied at the end. It has to be decided when the work is recorded, because that is the only moment when anybody knows the answer.

So a project is billable or it is not. An activity is billable or it is not. An hour recorded against a non-billable project is never billable regardless of who generates the invoice, and an hour recorded against internal admin work does not become billable because it happened during a client project’s week.

Timesheets built from tracked time
Billability is decided when the time is recorded, not when the invoice is generated.

This removes an entire category of month-end argument. Nobody is deciding, three weeks later and under time pressure, whether a particular Tuesday was chargeable. The decision was made when the project was set up, by somebody who knew, and everything downstream follows.

Break time is excluded structurally rather than by a filter. Breaks are recorded as their own kind of entry and every total that means “worked” applies a scope that excludes them. There is no path by which a lunch break reaches an invoice, because the query that builds the invoice uses the same scope as the timesheet that shows the hours.

A billing total that disagrees with the timesheet it came from is not a discrepancy. It is a dispute. Use the same code path for both or expect to explain the difference forever.

Rates, and the order in which they resolve

Rates are where most billing systems become confusing, because there are legitimately several places a rate could come from and they conflict.

Ours resolves in a stated order: a rate attached to the person for this project, then the person’s default rate, then the rate given for the invoice run. The order is written down and the interface shows which rate was applied to each line, so a line at an unexpected rate can be traced without anyone reading code.

What we specifically avoided is inferring a rate. A missing rate is an error at generation time, not a silent zero and not a guess from a similar project. An invoice that quietly bills at zero because a rate was never set will be sent, and the error will be found by the client’s accounts department a month later, which is the most expensive place to find it.

Rounding is a policy with a visible effect on money

Somewhere in every billing system there is a rounding rule, and it is usually written by whoever wrote the display code.

Rounding rules that change the bill
Rounding is an explicit setting with its consequence stated, not a detail inside a formatter.

Six minutes rounded up to fifteen, across twenty people for a month, is not a rounding error. It is a material change to an invoice, and both parties are entitled to know the rule. We surfaced it as an explicit organisation setting with the consequence stated next to it, rather than leaving it in a helper function where it would eventually be adjusted by somebody who believed they were changing a display.

The related decision is where rounding is applied. Rounding each session and summing gives a different answer from summing and rounding once, and the difference compounds with the number of sessions. We round once, at the line level, and state that we do. Either choice is defensible; being unable to say which one you made is not.

Draft, sent, paid — and what may change in each

An invoice is not a document, it is a state machine, and the useful part is what each state permits.

Invoices generated from billable time
Line items are editable while the invoice is a draft and frozen once it is sent.

A draft is fully editable. Lines can be adjusted, removed, added. This matters because generation gets you ninety per cent of the way and the last ten per cent is human — a goodwill discount, an hour written off, a line description rewritten for a client who wants more detail.

Once an invoice is sent, the lines are frozen. Not hidden, not discouraged — refused. The reason is simple: the client has a copy. A system that lets an invoice change after it has been sent means the number in your system and the number in theirs can differ silently, which is the single worst property a billing system can have.

Paid is a terminal state that records when. Corrections after sending happen as a credit note or a new invoice, which is what accountants expect and what leaves an auditable trail.

The tax field that is not one field

Tax looks like a percentage and is not. It needs a name, because the invoice must state what the tax is. It needs a rate, and the rate has to be stored with the invoice rather than read from settings at display time — otherwise changing your tax rate next year silently rewrites the history of every invoice you have ever issued.

This is a specific case of a general rule for anything financial: an invoice is a record of what was agreed at a moment, not a live view over current settings. Every value that appears on it — rates, tax, the client’s name and address, the rounding rule — is copied onto the invoice at generation. Referencing them means a customer who renames a client in March finds that their January invoices have retrospectively changed, which is not a bug anybody will find quickly and is a serious one when they do.

What generation actually does

The whole pipeline, once the above is decided, is short:

  1. Take a client and a period. Resolve the period boundaries in the organisation’s timezone.
  2. Find that client’s projects, and within them the completed, billable work entries in the period. Breaks are excluded by the scope, not by a condition.
  3. Group by project, and within project by person where rates differ.
  4. Resolve a rate for each group, in the stated order, and fail loudly if there is none.
  5. Apply the rounding policy once, at the line.
  6. Copy the client details, the tax name and rate, and the rounding rule onto the invoice itself.
  7. Write it as a draft. Nothing is sent by generating.

Step seven is the one people are tempted to skip. Generating straight to sent saves a click and removes the review that catches the month somebody logged forty hours to the wrong project.

What this changes in practice

The obvious change is the week that disappears. The more interesting one is that disputes get shorter.

When a client asks why a line is twenty-two hours, the answer is not a defence. It is a list of sessions with dates and durations, and if one of them is wrong, it is corrected and the invoice is regenerated. The conversation lasts ten minutes instead of a week, and it ends with a corrected number rather than a compromise that leaves both parties unhappy.

The other change is quieter and worth more. Once billing comes from tracked time, the cost of not tracking becomes immediately visible — an untracked hour is an unbilled hour, and it shows up as a smaller invoice rather than as a missing form. Teams that had spent years being asked to fill in timesheets for compliance reasons start tracking properly within a month, because the incentive finally points the same way as the instruction.

Approval is what makes the number safe to bill

Generating an invoice from tracked hours only works if the hours have stopped moving. An invoice built from a week that somebody edits the following day is a document that was true once.

Approving a timesheet week
Approving a week freezes it. Entries inside it reject edits at the API, not just in the interface.

Approval in our system locks a range of dates for a person. Once locked, the API refuses to create, edit or delete any entry inside it, and refuses at the server rather than by hiding a button. Clocking in to a locked week fails. Unlocking is possible for somebody with the authority, and unlocking is itself audited.

That turns the billing sequence into something with a defensible order: the week is worked, the week is approved, the invoice is generated from approved time. If a correction is needed afterwards, the week is explicitly unlocked, the correction is made, and both actions are in the audit log with names against them.

Without the lock, the sequence is: the week is worked, the invoice is generated, and the underlying data continues to drift. Nobody notices until a client queries a line and the figures no longer reconcile — and by then there is no way to tell whether the invoice was wrong or the data changed after it.

Multiple currencies, and the one we did not build

An agency with clients in more than one country will eventually ask to bill in more than one currency, and there is a right amount of this to build.

The organisation has a currency. Invoices are issued in it. That covers the large majority of customers and it is honest about what the system does. What we deliberately did not build is conversion — taking hours recorded against one currency and presenting them in another at a rate fetched from somewhere.

The reason is that currency conversion is not a display concern. Which rate, from which source, on which date — the date of the work, the date of the invoice, the date of payment — are commercial decisions with real money attached, and a system that picks one silently is making that decision on the customer’s behalf. Getting it wrong by two per cent on a large invoice is a conversation nobody wants to have with a client.

The version that is honest and cheap is to let a client carry its own currency, issue that client’s invoices in it, and never convert between them in a total. A report that adds rupees to dollars and shows one number is worse than a report that shows two numbers, however much tidier it looks.

Editing somebody else’s time, and why it has to be allowed

A strict system would say only the person who worked the hours may record or change them. It is a clean rule and it does not survive contact with a real office.

Adding a manual time entry
An administrator can log or correct time for a member, and every such action is attributed in the audit log.

Somebody is on leave when the correction is discovered. Somebody has left the company and their last week needs fixing before it can be billed. Somebody worked at a client site with no laptop and phoned their hours in. If the system cannot represent these, the finance team will keep a spreadsheet of adjustments, and the spreadsheet will be the real source of truth while your database holds a tidy fiction.

So owners, administrators and managers can log and correct time for a member. What makes that safe is not restricting it further but recording it fully: the entry knows who created it as distinct from whose time it is, and the audit log carries the action, the actor and the record. A member can see that their Tuesday was edited, by whom, and when.

The general principle, which applies well beyond timesheets: when a capability is genuinely needed, the safe design is to permit it and attribute it, not to forbid it and watch it happen outside the system where nothing is recorded at all.

The unbilled hour is the one worth finding

Most of the value in connecting time to billing is not the invoice that gets sent. It is the work that would otherwise never have reached an invoice at all.

Every agency has some. An hour spent on a client call that nobody logged. A support request handled between other tasks. A scope change that was absorbed rather than quoted. Individually each is trivial and collectively, in every company we have looked at, it is a meaningful share of the month.

When the pipeline runs from tracked time to invoice, that work becomes visible in a way it never was on a spreadsheet: it appears as billable time against a client, in a period, attached to a task, and the only decision left is whether to charge for it. Frequently the answer is no, and that is a fine answer — deciding to absorb an hour is completely different from never knowing it happened.

That is the argument we would make to any agency weighing this up. Not that invoicing becomes faster, though it does. That the hours you were giving away without meaning to stop being invisible, and you get to choose.

Partial months, and the invoice that arrives before the work stops

Billing periods rarely line up with the work. A client is invoiced monthly, a sprint runs across the month boundary, and somebody is mid-task when the invoice is generated.

The mistake is to make the invoice wait for the work. Time is billed by the period it happened in, not by whether the task it belongs to is finished. An hour worked on the twenty-ninth belongs on that month’s invoice regardless of whether the task closes in the following week — otherwise a long-running task quietly defers revenue and the finance team ends up tracking a separate list of what has not yet been billed.

What this needs is that a session belongs to exactly one period, decided by its start time in the organisation’s timezone. A session that begins at half past eleven at night on the last day of the month belongs to that month, in full, even though most of it happened in the next one. Splitting sessions across boundaries is technically possible and produces line items no client has ever wanted to read.

Credit notes rather than edits

When a sent invoice turns out to be wrong, the temptation is to unlock it and fix it. Resist. The client has a copy; changing yours means two documents with the same number and different contents, which is the situation every accounting standard exists to prevent.

A credit note against the original, and a new invoice if needed, leaves a trail that both sides can follow. It feels heavier and it is the reason nobody ends up in a meeting comparing two versions of the same PDF.

Write-offs are data too

When an hour is worked and deliberately not charged, that decision is worth recording rather than achieving by deleting the line. A written-off hour and an hour that never existed look identical on the invoice and mean completely different things to whoever is deciding next quarter’s rates.

Over a year, the pattern of write-offs by client is one of the more useful numbers a services business can have. It is also the one that is systematically destroyed by the habit of tidying invoices before sending them, which is why we keep the adjustment visible on the draft rather than silently removing the line.

What we would tell anyone building this

  1. Decide billability when the work is recorded, never at invoice time.
  2. Use the same scope for billing as for timesheets. Two paths to one number will diverge.
  3. Write down the rate resolution order and show which rate was used on each line.
  4. Fail on a missing rate. Never bill zero silently.
  5. Make rounding an explicit setting with its effect stated, and round in one place.
  6. Freeze on send. The client has a copy.
  7. Copy every value onto the invoice, never reference live settings.
  8. Always generate a draft. The review catches the wrong-project month.

None of this is difficult once the hours underneath are trustworthy. That is the actual prerequisite, and it is why the invoicing feature was the last thing we built rather than the first — an invoice generated from numbers somebody typed from memory is a faster way to send a wrong invoice, which is not an improvement.

Happy Tracker generates invoices directly from billable tracked time, and is free for up to five users with no time limit at happytracker.happycoders.in.