impl/typescript.ts
1,963 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { addDays, daysBetween } from "./dates_add_days.ts"; ← from dates.add-days ^1.0.0 · built alongside by fune
import { businessDaysBetween } from "./dates_business_days_between.ts"; ← from dates.business-days-between ^1.0.0 · built alongside by fune
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 { type ProRata, type ProRataMethod } from "./payroll_pro_rata_salary_types.ts";
function countDays(from: string, to: string, method: ProRataMethod, holidays: readonly string[]): number {
if (method === "calendar-days") return daysBetween(from, to) + 1;
if (method === "working-days") return businessDaysBetween(from, addDays(to, 1), holidays);
throw new RangeError(`unknown pro-rata method "${method}"`);
}
/** Pro-rate a period's salary by time employed and FTE, rounding once. */
export function proRataSalary(
periodSalary: Money,
fteBasisPoints: number,
periodStart: string,
periodEnd: string,
workedFrom: string,
workedTo: string,
method: ProRataMethod,
holidays: readonly string[],
): ProRata {
if (!Number.isInteger(fteBasisPoints) || fteBasisPoints < 1 || fteBasisPoints > 10000) {
throw new RangeError(`fteBasisPoints must be a whole number from 1 to 10000, received ${fteBasisPoints}`);
}
if (daysBetween(periodStart, periodEnd) < 0) throw new RangeError("periodEnd is before periodStart");
if (daysBetween(workedFrom, workedTo) < 0) throw new RangeError("workedTo is before workedFrom");
if (workedFrom < periodStart || workedTo > periodEnd) {
throw new RangeError("the worked dates must fall inside the pay period");
}
const daysInPeriod = countDays(periodStart, periodEnd, method, holidays);
if (daysInPeriod === 0) throw new RangeError("the pay period has no working days to pro-rate over");
const daysCounted = countDays(workedFrom, workedTo, method, holidays);
const minor = roundDiv(periodSalary.minor * fteBasisPoints * daysCounted, 10000 * daysInPeriod, "half-up");
return { pay: money(minor, periodSalary.currency), daysCounted, daysInPeriod };
}