from typing import List, Sequence from .math_round_div import round_div from .money_amount import Money, money from .money_sum import sum_money 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))