Functional Weave
Code in Rust

hospitality.tronc-allocation@1.0.0

impl/python.py

2,336 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 .hospitality_tronc_allocation_types import TroncMember, TroncMethod, TroncShare
from .money_allocate import allocate  ← from money.allocate ^1.0.0 · built alongside by fune
from .money_amount import Money, money  ← from money.amount ^1.0.0 · built alongside by fune


def _whole(value: object) -> bool:
    return isinstance(value, int) and not isinstance(value, bool)


def _weight_of(member: TroncMember, method: str) -> int:
    if method == "hours":
        return member.minutes_worked
    if method == "points":
        return member.points
    if method == "hours-and-points":
        return member.minutes_worked * member.points
    raise ValueError('unknown tronc method "%s"' % (method,))


def tronc_allocation(pool: Money, staff: Sequence[TroncMember], method: TroncMethod) -> List[TroncShare]:
    """Share a tips pool between staff in proportion to a weight.

    Time is in minutes so a 7.5-hour shift is exact. The pool is split with
    money.allocate, so the shares add up to the pool exactly: the leftover
    pennies go to the largest remainders, ties to whoever is listed first.
    """
    if len(staff) == 0:
        raise ValueError("staff must have at least one member")
    if pool.minor < 0:
        raise ValueError("pool must not be negative, received %d" % (pool.minor,))
    ids = set()
    for member in staff:
        if member.id == "":
            raise ValueError("every member needs an id")
        if member.id in ids:
            raise ValueError('staff id "%s" appears twice' % (member.id,))
        ids.add(member.id)
        if not _whole(member.minutes_worked) or member.minutes_worked < 0:
            raise ValueError(
                'minutesWorked must not be negative, received %s for "%s"' % (member.minutes_worked, member.id)
            )
        if not _whole(member.points) or member.points < 0:
            raise ValueError('points must not be negative, received %s for "%s"' % (member.points, member.id))
    weights = [_weight_of(member, method) for member in staff]
    if sum(weights) == 0:
        if pool.minor != 0:
            raise ValueError("nobody in the pool has any weight to share it by")
        return [TroncShare(id=member.id, weight=0, share=money(0, pool.currency)) for member in staff]
    shares = allocate(pool, weights)
    return [TroncShare(id=member.id, weight=weights[i], share=shares[i]) for i, member in enumerate(staff)]