Functional Weave
Code in TypeScript

payroll.overtime@1.0.1

impl/python.py

1,868 bytes · the Python 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.

from typing import List, Sequence

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 .money_sum import sum_money  ← from money.sum ^1.0.0 · built alongside by fune
from .payroll_overtime_types import OvertimeBand, OvertimeLine, OvertimePay


def overtime_pay(minutes_worked: int, hourly_rate: Money, bands: Sequence[OvertimeBand]) -> OvertimePay:
    """Price the minutes worked band by band, rounding once per payslip line."""
    if isinstance(minutes_worked, bool) or not isinstance(minutes_worked, int) or minutes_worked < 0:
        raise ValueError("minutesWorked must be a whole number of minutes, not negative, received %s" % (minutes_worked,))
    if len(bands) == 0:
        raise ValueError("overtime needs at least one band")
    if bands[0].from_minutes != 0:
        raise ValueError("the first band must start at 0 minutes")
    for i in range(1, len(bands)):
        if bands[i].from_minutes <= bands[i - 1].from_minutes:
            raise ValueError("bands must be in strictly ascending order of fromMinutes")
    lines: List[OvertimeLine] = []
    for i, band in enumerate(bands):
        start = band.from_minutes
        end = min(bands[i + 1].from_minutes, minutes_worked) if i + 1 < len(bands) else minutes_worked
        minutes = end - start
        if minutes <= 0:
            break
        multiplier = band.multiplier_basis_points
        if isinstance(multiplier, bool) or not isinstance(multiplier, int) or multiplier < 0:
            raise ValueError("multiplierBasisPoints must be a whole number, not negative, received %s" % (multiplier,))
        pay = money(round_div(hourly_rate.minor * minutes * multiplier, 60 * 10000, "half-up"), hourly_rate.currency)
        lines.append(OvertimeLine(minutes=minutes, multiplier_basis_points=multiplier, pay=pay))
    return OvertimePay(lines=lines, total=sum_money([l.pay for l in lines], hourly_rate.currency))