Skip to main contentGBOSSTABTime and Billing

Rate cards and how rates are picked

AdminEffective dates and the rate resolution chain, with a worked example.

Rate cards and how rates are picked

When you log an entry, TAB has to decide what hourly rate to bill it at. Rate cards are the tool for that: each card pins a rate to a scope (this user, this client, both, or neither) and a date window.

You don't need a rate card for every entry. TAB falls back through a chain of defaults if no card matches. Rate cards are for the overrides, Sarah's special rate for Acme, or "all advisory work bills at $300/hr from Jan 1."

The resolution chain

When an entry needs a rate, the backend (RateResolutionService) checks these in order and stops at the first match:

  1. Active rate card. All currently-effective rate cards are scanned; the most-specific match wins. "Specificity" is the count of non-null filters (user_id + project_id + client_id) that match the entry. A user+client card beats a client-only card beats a firm-wide-default card.
  2. Project's default rate (projects.default_hourly_rate_cents).
  3. Client's default rate (clients.default_hourly_rate_cents).
  4. User's default rate (app_users.default_hourly_rate_cents).
  5. Nothing matches, the entry shows , in the rate column on reports and null on the JSON.

"Active" means today's date falls inside the card's Effective from / Effective to window. A card with Effective to in the past won't match. A card with Effective from in the future is also skipped until the date arrives.

That's four real lookup levels, not eight, there's a single rate-card query with specificity scoring, not one query per scope.

Worked example

Sarah is logging 2 hours on Acme Tax in March 2026.

  • There's a rate card with user_id = Sarah and client_id = Acme Tax at $250/hr, effective Jan 1, Dec 31 2026.
  • There's also a firm-wide card (user_id, project_id, client_id all null) at $200/hr.
  • Sarah's user default is $225/hr.

The rate-card query returns both cards (both effective). The user+client card has specificity 2; the firm-wide card has specificity 0. The user+client card wins. The entry bills at $250/hr.

If only the firm-wide card existed, that would match at specificity 0, the user default of $225 wouldn't be reached, because rate cards short-circuit the chain at step 1.

Creating a rate card

  1. Open Admin → Rate cards.
  2. Click New rate card.
  3. The modal asks for:
    • Client, dropdown of clients, or Any client (leave it blank for a firm-wide card).
    • User, dropdown of users, or Any user.
    • Hourly rate (USD), entered in dollars, e.g. 250.00. The backend stores it as cents.
    • Effective from, required.
    • Effective to, optional. Leave blank for an indefinite card.
  4. Click Add.

The current UI doesn't expose a Project (project) dropdown, even though the backend supports a project filter, you can't set one from the web today. (Coming soon.) For now, project-specific rates come from the project's own default_hourly_rate_cents, set when the project is created from the desktop app.

The card is live as soon as it saves. Existing entries aren't repriced, rate resolution runs at the moment an entry is rendered on a report, against the entry's started_at date. Future entries pick up the new card from the Effective from date forward.

Deleting a rate card

Each row on the list has a Delete button. A native browser confirm() dialog asks you to confirm with the warning: "Past entries already billed at this rate keep their amount; future entries reroll."

There's no in-place edit modal today. If you want to change a rate mid-year, the clean pattern is to set the old card's Effective to to the boundary, then create a new card with Effective from the next day. Doing this means a report run across the boundary shows the correct rate on each side.

(Today there's also no edit-effective-to button, you'd delete the old card and recreate it with the cutover dates. That works but loses the historical card row. A proper "expire" action is on the polish list.)

Tips

  • Most firms don't need many cards. A handful of user-or-client overrides on top of per-user defaults usually covers it.
  • Use scopes that match how you actually price work. If every client bills at the staffer's standard rate, you barely need cards at all.
  • Keep effective dates honest. Don't just edit a card to a new rate mid-year, expire it and create a new one, so the audit trail shows what rate applied when.

Related