Skip to content

Overtime: gate, visibility, separate per-date grants, and RD/night classification#1

Merged
KVC86 merged 5 commits into
mainfrom
overtime-gate-and-visibility
Jul 6, 2026
Merged

Overtime: gate, visibility, separate per-date grants, and RD/night classification#1
KVC86 merged 5 commits into
mainfrom
overtime-gate-and-visibility

Conversation

@KVC86

@KVC86 KVC86 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Overtime becomes a first-class, separately-managed feature, with accurate classification and correct premium pay.

Three commits:

  1. Extend clock-in window to cover approved overtime
  2. Employee overtime visibility (confirm-to-dismiss + My schedule view)
  3. Separate per-date OT grants + rest-day/night classification

What changed

  • Schedule-gated clock-in now honours approved overtime - the allowed window widens to shift + OT and auto-expiry follows the OT end. Includes an overnight-OT storage fix.
  • Overtime managed separately: grant OT per employee for a specific date as a start time + N hours, from a dedicated panel. OT was removed from the shift builder, and shift edits no longer clobber a day''s OT. New endpoints POST/GET/DELETE /schedules/overtime.
  • Classification from two dimensions of the scheduled date/time - rest day (from the schedule, not "no shift") and night hours (22:00-06:00): OT / NDOT / RDOT / RDNDOT, splitting windows that straddle the boundary.
  • Pay: rest-day OT multiplier (rdOtMultiplier, migration) and the night differential stack additively; payroll itemises the four categories as distinct payslip lines. Totals unchanged.
  • Visibility: classification shown in the WFM overtime panel, My Schedule, and the employee banner; plus an accidental-tap guard on dismissing the OT reminder.

Testing

  • 30/30 jest tests pass locally (classifier units, an RDNDOT payroll case, OT-grant/rest-day/clock-in-gate cases).
  • Typecheck clean; verified end-to-end in the running app.

Notes

  • One DB migration (rdOtMultiplier) - migrate deploy applies it automatically.
  • The night window is computed in server-local time to match the existing payroll night-differential calc; making it Manila-explicit is a separate follow-up.

?? Generated with Claude Code

KVC86 and others added 5 commits July 6, 2026 17:07
Floor-employee clock-in was gated strictly to the assigned shift, so an
employee granted overtime could not clock in past their scheduled end and
was auto-clocked-out at that end. Widen the allowed window to the envelope
of shift + granted OT, and drive the shift auto-expiry off that widened
end. A rest day with granted OT now opens the OT window instead of blocking.
(Payroll already pays OT only inside the authorized window; unchanged here.)

Also fix OT storage for overnight shifts: OT entered in next-day wall-clock
(e.g. 06:00-08:00 after a 22:00-06:00 shift) was anchored to the shift's
calendar date and stored a day early. Roll it forward when the shift is
overnight and the OT parses before the shift start; pure day shifts keep
any pre-shift OT intact.

Add an integration test covering the schedule/OT clock-in gate: too-early,
post-shift-no-OT, post-shift-with-OT (allowed, expiry follows OT end), and
rest-day with/without OT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dismissing the "you've been given overtime" banner was a single tap with
no undo, and there was no employee-facing way to look the OT up again
(the schedule view is WFM/Admin-only). An accidental tap hid the reminder
for good.

- Guard the banner's "Got it" with a confirmation dialog that reassures
  the OT is still scheduled and payable, and points to My schedule.
- Add a read-only GET /me/schedule endpoint (self-scoped) returning the
  employee's upcoming shifts, rest days, and granted OT windows.
- Add a "My schedule" tab that renders those rows and highlights any
  granted overtime, so it stays visible even after the banner is dismissed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework overtime into a first-class, separately-managed feature:

- Grant OT per employee for a specific date as a start time + N hours,
  from a dedicated Overtime panel. Removed the OT fields from the shift
  builder, and shift edits no longer touch a day's OT (upsert leaves it
  alone). New endpoints POST/GET/DELETE /schedules/overtime.
- Classify each grant from two dimensions of the scheduled date/time —
  rest day (taken from the schedule, not "no shift") and night hours
  (22:00-06:00) — into OT, NDOT, RDOT, or RDNDOT, splitting windows that
  straddle the boundary. Fixes prior behavior that labeled any no-shift
  OT as rest-day OT.
- Pay: rest-day OT multiplier (rdOtMultiplier, migration) and the night
  differential stack additively; payroll itemizes the four categories as
  distinct payslip lines. Totals are unchanged.
- Surface the classification in the WFM overtime panel, My Schedule, and
  the employee overtime banner.

Shared classifier in src/common/overtime.ts (night logic shared with
payroll so labels and pay always agree). Tests: classifier units, an
RDNDOT payroll case, and OT-grant/rest-day cases (30 total, all green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI (UTC) failed the overtime classification and payroll night-differential
tests: OT windows are Manila wall-clock instants, but the 22:00-06:00 night
window was computed in the server's local time, so the same window classified
differently on a UTC runner vs a Manila one (10:00 Manila read as 02:00 UTC
and wrongly counted as night).

Compute the night window in Philippine time (UTC+8) explicitly so it's
correct and identical on any server. Anchor the timezone-naive date literals
in the payroll test to +08:00 so its assertions hold regardless of runner tz.

Also fixes a latent server-timezone bug the payroll night differential
already had.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WFM-Features.pptx was a presentation artifact that doesn't belong in the
repository; it was swept in by git add -A.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@KVC86
KVC86 merged commit c366ca6 into main Jul 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant