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)]