Functional Weave
Code in TypeScript

payroll.pro-rata-salary@1.0.1

impl/python.py

2,014 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 Sequence

from .dates_add_days import add_days, days_between  ← from dates.add-days ^1.0.0 · built alongside by fune
from .dates_business_days_between import business_days_between  ← from dates.business-days-between ^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_pro_rata_salary_types import ProRata, ProRataMethod


def _count_days(start: str, end: str, method: str, holidays: Sequence[str]) -> int:
    if method == "calendar-days":
        return days_between(start, end) + 1
    if method == "working-days":
        return business_days_between(start, add_days(end, 1), holidays)
    raise ValueError('unknown pro-rata method "%s"' % (method,))


def pro_rata_salary(
    period_salary: Money,
    fte_basis_points: int,
    period_start: str,
    period_end: str,
    worked_from: str,
    worked_to: str,
    method: ProRataMethod,
    holidays: Sequence[str],
) -> ProRata:
    """Pro-rate a period's salary by time employed and FTE, rounding once."""
    if isinstance(fte_basis_points, bool) or not isinstance(fte_basis_points, int) or not 1 <= fte_basis_points <= 10000:
        raise ValueError("fteBasisPoints must be a whole number from 1 to 10000, received %s" % (fte_basis_points,))
    if days_between(period_start, period_end) < 0:
        raise ValueError("periodEnd is before periodStart")
    if days_between(worked_from, worked_to) < 0:
        raise ValueError("workedTo is before workedFrom")
    if worked_from < period_start or worked_to > period_end:
        raise ValueError("the worked dates must fall inside the pay period")
    days_in_period = _count_days(period_start, period_end, method, holidays)
    if days_in_period == 0:
        raise ValueError("the pay period has no working days to pro-rate over")
    days_counted = _count_days(worked_from, worked_to, method, holidays)
    minor = round_div(period_salary.minor * fte_basis_points * days_counted, 10000 * days_in_period, "half-up")
    return ProRata(pay=money(minor, period_salary.currency), days_counted=days_counted, days_in_period=days_in_period)