Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { roundDiv } from "./math_round_div.ts"; ← from math.round-div ^1.0.0 · built alongside by fune
import { type Money, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { sumMoney } from "./money_sum.ts"; ← from money.sum ^1.0.0 · built alongside by fune
import { type OvertimeBand, type OvertimeLine, type OvertimePay } from "./payroll_overtime_types.ts";
/** Price the minutes worked band by band, rounding once per payslip line. */
export function overtimePay(minutesWorked: number, hourlyRate: Money, bands: readonly OvertimeBand[]): OvertimePay {
if (!Number.isInteger(minutesWorked) || minutesWorked < 0) {
throw new RangeError(`minutesWorked must be a whole number of minutes, not negative, received ${minutesWorked}`);
}
if (bands.length === 0) throw new RangeError("overtime needs at least one band");
if (bands[0].fromMinutes !== 0) throw new RangeError("the first band must start at 0 minutes");
for (let i = 1; i < bands.length; i++) {
if (bands[i].fromMinutes <= bands[i - 1].fromMinutes) {
throw new RangeError("bands must be in strictly ascending order of fromMinutes");
}
}
const lines: OvertimeLine[] = [];
for (let i = 0; i < bands.length; i++) {
const from = bands[i].fromMinutes;
const to = i + 1 < bands.length ? Math.min(bands[i + 1].fromMinutes, minutesWorked) : minutesWorked;
const minutes = to - from;
if (minutes <= 0) break;
const multiplier = bands[i].multiplierBasisPoints;
if (!Number.isInteger(multiplier) || multiplier < 0) {
throw new RangeError(`multiplierBasisPoints must be a whole number, not negative, received ${multiplier}`);
}
const pay = money(roundDiv(hourlyRate.minor * minutes * multiplier, 60 * 10000, "half-up"), hourlyRate.currency);
lines.push({ minutes, multiplierBasisPoints: multiplier, pay });
}
return { lines, total: sumMoney(lines.map((l) => l.pay), hourlyRate.currency) };
}