How the overtime balance is calculated
The dashboard's overtime number for any staff member, year, and
month follows a single recipe — applied month by month from the
staff member's first logged WFM entry through today (or through
disabled_at, if overtime tracking was turned off).
Per-month diff
For each calendar month M:
working_days = Mon–Fri days in M
public_holiday_days = Zurich public holidays falling on Mon–Fri in M
daily_hours = weekly_hours ÷ 5 (weekly_hours defaults to 40 → 8 h)
target_hours = working_days × daily_hours × FTE% − public_holiday_days × daily_hours × FTE%
↑ ↑
full month, FTE-weighted PH days subtracted
actual_hours = sum of all WFM timesheet entries in M
(worked time + paid absences: vacation, sick leave, etc.)
diff = actual_hours − target_hoursdaily_hours is the full-time hours-per-working-day baseline. Most
staff are on the standard 40 h week (8 h/day). Someone contracted for
38 h/week at 100 % FTE uses weekly_hours = 38 → 7.6 h/day. Set this
per person in Staff Admin → Overtime tracking. It composes with
FTE %: a 38-hour, 80 %-FTE employee = 7.6 × 0.8 per working day.
diff > 0→ overtime that monthdiff < 0→ undertime that month
Running balance
The dashboard shows running_balance per row — the cumulative sum
of diff from the first logged month onwards.
For the personal page, "Previous years" is the running balance at Dec 31 of (selected year − 1). The Total at the bottom is Previous years + sum of this year's diffs.
Three overrides change this baseline
All three live in Staff Admin and only affect the overtime calc (every other metrics page is unaffected).
FTE history
If a staff member's FTE % changed over time, an FTE history entry
per change makes the target_hours for each historical month use
the right percentage instead of today's.
Resolution rule: for any month M, FTE % is the entry with the
greatest effective_from ≤ first day of M. Months before the
earliest entry use the earliest entry's FTE. Staff with no entries
fall back to today's WFM FTE — no behavioural change.
Base
A closing balance at the end of a chosen month. Months ≤ that month are discarded from the overtime calc (the underlying WFM data is untouched and still feeds every other report).
The running balance at the end of the base's month is the entered value; subsequent months accumulate on top. The latest base whose date is on or before today wins.
Correction
A signed-hours add/subtract on a specific date, applied at the end of its effective month after that month's diff.
Common uses:
- Overtime payout — enter the paid-out hours as a negative value.
- Contract change — a one-off positive or negative bump.
- Reconciliation — manual adjustment for missed entries or deviations from prior tools.
Multiple corrections in the same month sum. Corrections in months a Base already covers are ignored.
Worked example: Dario, 2026
Suppose Dario's setup is:
- Eligibility: enabled since Jan 2024.
- FTE history: none (he's been 100 % the whole time).
- Base: Dec 2025, 56.10 h (closing balance carried over from the manual xlsx).
Year 2026:
| Month | Target | Actual | Diff | Running |
|---|---|---|---|---|
| Previous years | 56.10 ← from base | |||
| January | 160.00 | 181.25 | +21.25 | 77.35 |
| February | 160.00 | 106.00 | −54.00 | 23.35 |
| March | 176.00 | 189.00 | +13.00 | 36.35 |
| April | 160.00 | 160.50 | +0.50 | 36.85 |
"Previous years" = 56.10 because the Base discards earlier months for overtime reporting and seeds the running balance at the Base's closing value.
If a manager then adds a Correction of −20 h on 2026-03-10
(overtime payout), the March row becomes:
| March | 176.00 | 189.00 | +13.00 (−20 adj) | 16.35 |
and April's running balance drops by 20 accordingly.
All three overrides are pure — no precomputed value is stored. Each page load walks the full WFM timesheet history, applies the overrides on top, and renders. Editing an override and pressing Load again reflects the new numbers immediately.