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 .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_apply_rate import apply_rate ← from money.apply-rate ^1.0.0 · built alongside by fune
from .professional_wip_valuation_types import WipBasis, WipEntry, WipValuation
def wip_valuation(entries: Sequence[WipEntry], basis: WipBasis, write_down_basis_points: int, currency: str) -> WipValuation:
"""Work in progress at cost or charge-out rates, less a write-down."""
if basis not in ("cost", "charge-out"):
raise ValueError('unknown WIP basis "%s": expected cost or charge-out' % (basis,))
if (
isinstance(write_down_basis_points, bool)
or not isinstance(write_down_basis_points, int)
or write_down_basis_points < 0
or write_down_basis_points > 10000
):
raise ValueError("writeDownBasisPoints must be between 0 and 10000, received %s" % (write_down_basis_points,))
minutes = 0
gross = 0
for entry in entries:
if isinstance(entry.minutes, bool) or not isinstance(entry.minutes, int) or entry.minutes < 0:
raise ValueError("minutes must be a non-negative integer, received %s" % (entry.minutes,))
rate: Money = entry.cost_rate if basis == "cost" else entry.charge_rate
if rate.currency != currency:
raise ValueError("currency mismatch: %s and %s" % (currency, rate.currency))
if rate.minor < 0:
raise ValueError("hourly rates must not be negative, received %s" % (rate.minor,))
minutes += entry.minutes
# Per entry, as the time would be billed, so WIP reconciles with the bill.
gross += round_div(entry.minutes * rate.minor, 60, "half-up")
gross_money = money(gross, currency)
write_down = apply_rate(gross_money, write_down_basis_points, "half-up")
return WipValuation(minutes=minutes, gross=gross_money, write_down=write_down, net=money(gross - write_down.minor, currency))