Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Optional, Sequence
from .dates_add_days import parse_iso_date ← from dates.add-days ^1.0.0 · built alongside by fune
from .math_round_div import round_div ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, money ← from money.amount ^1.0.0 · built alongside by fune
from .payroll_holiday_pay_accrual_data import HOLIDAY_PAY_RULES, HOLIDAY_PAY_RULES_HISTORY, HolidayPayRule ← this capability’s own data, compiled from data/holiday-pay-rules.json into the same file by fune build
from .payroll_holiday_pay_accrual_types import HolidayPay, HolidayPayMethod
def _rule_for(method: str, on_date: str) -> HolidayPayRule:
best: Optional[HolidayPayRule] = None
for rule in HOLIDAY_PAY_RULES:
if rule.method != method or on_date < rule.valid_from:
continue
if rule.valid_to is not None and on_date > rule.valid_to:
continue
if best is None or rule.valid_from > best.valid_from:
best = rule
if best is None:
if HOLIDAY_PAY_RULES_HISTORY != "full":
raise ValueError(
"no %s holiday pay rule for %s: this build was installed with history=%s. "
"Reinstall with history=full for older dates." % (method, on_date, HOLIDAY_PAY_RULES_HISTORY)
)
raise ValueError("no %s holiday pay rule for %s" % (method, on_date))
return best
def holiday_pay(method: HolidayPayMethod, period_pay: Money, weekly_pay: Sequence[Money], on_date: str) -> HolidayPay:
"""Holiday pay under the Working Time Regulations 1998, by one of two methods.
Rolled-up: a 12.07% uplift on pay for work done, paid with it, allowed only
for irregular-hours and part-year workers in leave years from 1 April 2024.
Average week: a week's pay as the average over the most recent 52 weeks in
which the worker was paid, skipping unpaid weeks but never looking back more
than 104 weeks.
"""
parse_iso_date(on_date)
if method == "rolled-up":
rule = _rule_for(method, on_date)
if period_pay.minor < 0:
raise ValueError("periodPay must not be negative")
return HolidayPay(amount=money(round_div(period_pay.minor * rule.accrual_basis_points, 10000, "half-up"), period_pay.currency), weeks_counted=0)
if method != "average-week":
raise ValueError('unknown holiday pay method "%s"' % (method,))
rule = _rule_for(method, on_date)
total = 0
counted = 0
currency: Optional[str] = None
window = min(len(weekly_pay), rule.lookback_weeks)
for i in range(window):
if counted >= rule.reference_weeks:
break
week = weekly_pay[i]
if currency is None:
currency = week.currency
elif week.currency != currency:
raise ValueError("currency mismatch: %s and %s" % (currency, week.currency))
if week.minor < 0:
raise ValueError("weekly pay must not be negative")
if week.minor == 0:
continue
total += week.minor
counted += 1
if counted == 0 or currency is None:
raise ValueError("no week with pay in the last %s weeks" % (rule.lookback_weeks,))
return HolidayPay(amount=money(round_div(total, counted, "half-up"), currency), weeks_counted=counted)