Overview
What the payroll engine does
The Tidex payroll engine computes wage earnings for work shifts. It takes shift data (date, start/end time) and wage settings (hourly rate, supplements, tax, break deductions) to produce deterministic earnings values.
Inputs
- Shift data: shift_date (ISO), start_time (HH:MM), end_time (HH:MM), optional custom pause windows, optional custom supplements, job_id
- Wage snapshot: Hourly wage, supplement rules, tax settings, break deduction settings — scoped to a job
- Job: Name, color, immutable currency, payroll_day, half_tax_month, monthly_goal — primary source for payroll configuration
- Payroll adjustments: Manual payout-level additions or corrections with amount, payout date, job scope, and tax treatment
- User settings: Global preferences; payroll_day/half_tax_month/monthly_goal kept as legacy fallback during compatibility window
Outputs
- computeShift output: durationHours, paidHours, basePay, supplementPay, gross, wagePeriods, originalWagePeriods, breakAudit
- breakAudit output: method, thresholdHours, deductedHours, source, appliedPauseWindows, notes
- Downstream totals output: taxAmount and net (calculated outside computeShift), plus payroll adjustment gross/net totals for payout cards
Key invariants
- Deterministic: Same inputs always produce same outputs
- Pure computation: Zero I/O, all inputs explicit
- Cross-midnight support: Shifts spanning midnight are calculated as continuous time
- Dual-date snapshot logic: Wage/supplements/breaks use shift date; tax uses payout date
- Precision: Exact minute-based hours; round each shift pay component once to 2 decimal places
- Job-scoped snapshots: Each shift uses wage snapshots belonging to the same job; local rollout fallbacks may use default-job or legacy job-less snapshots when no scoped rows exist
- Job-scoped payroll day: payroll_day is resolved from the shift's job first, then the default job, then user settings
Definitions
| Term | Definition |
|---|---|
| Shift | A stored work period with date, start time, end time |
| Virtual shift | A computed occurrence from a recurring shift template (not persisted) |
| Wage snapshot | Point-in-time capture of wage, supplement, tax, and break settings — scoped to a job |
| Baseline snapshot | Snapshot with from_date = NULL, serves as fallback within its job bucket |
| Supplement window | Time-of-day range when a supplement rate applies |
| Pause window | An exact unpaid interval clipped out of a shift before automatic break rules are considered |
| Payout date | Date when wages are paid (typically month after work + payroll day) |
| Payroll period | The calendar month whose earnings are grouped for a payout |
| Month grouping | Shifts worked in month M are paid in month M+1 |
| Payroll adjustment | A manual payout-level bonus, retro pay, correction, or other adjustment included in payroll totals |
| Job | An employer/workplace entity that groups shifts and wage snapshots; owns payroll_day, half_tax_month, monthly_goal |
| Default job | Each user has exactly one active default job; shifts without an explicit job_id are assigned here |
| Legacy snapshot | A wage_snapshot with job_id = NULL; used only as rollout fallback, primarily for the default job |
Entry points
The active payroll stack is split between the iOS app and the shared Supabase TypeScript module. The legacy Effect wrapper and ShiftsService wording no longer describe the live product.
The Swift iOS layer orchestrates month-level loading and tax/snapshot selection, while the shared TypeScript calculator is still used by Wagey and server-side tooling. In the TypeScript compatibility signature, settings and job are retained for compatibility but are not required for the current core calculation path.
// iOS month orchestration (active app)
PayrollEngine.computeShiftsForMonth(request)
// ios/TidexApp/Services/Payroll/PayrollEngine.swift
// iOS per-shift calculator (active app)
PayrollCalculator.computeShift(shift, snapshot: wageSnapshot)
// ios/TidexApp/Services/Payroll/PayrollCalculator.swift
// Shared TypeScript calculator (Wagey / server-side tools)
computeShift(shift, settings, presetRules, snapshot, job?)
// supabase/functions/_shared/wagey/payroll/calc.ts