Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from .finance_proration_types import ProrationSplit
from .money_allocate import allocate ← from money.allocate ^1.0.0 · built alongside by fune
from .money_amount import Money ← from money.amount ^1.0.0 · built alongside by fune
def prorate(amount: Money, total_days: int, used_days: int) -> ProrationSplit:
"""Split a period's charge into the part used and the part not used.
The mid-cycle upgrade, downgrade and cancellation calculation. Written the
obvious way - one rounding for the charge and another for the credit - the
two halves do not reliably add back up: 9.99 over two days is 4.995 each
way, and two half-up roundings produce 5.00 and 5.00, a penny invented on a
line the customer can see.
One call to ``money.allocate`` does the whole split instead, so
``used + unused`` is the original amount for every input, credits included.
"""
if isinstance(total_days, bool) or not isinstance(total_days, int) or total_days <= 0:
raise ValueError("total days must be greater than zero, received %r" % (total_days,))
if isinstance(used_days, bool) or not isinstance(used_days, int) or used_days < 0:
raise ValueError("used days must not be negative, received %r" % (used_days,))
if used_days > total_days:
# Clamping here would turn a bug in the caller's period arithmetic into
# a plausible invoice, which is far more expensive to find later.
raise ValueError(
"used days must not exceed total days, received %d of %d" % (used_days, total_days)
)
# total_days > 0 guarantees the ratios do not sum to zero, so allocate is
# safe even when one side is zero: [0, n] and [n, 0] are both well defined.
used, unused = allocate(amount, [used_days, total_days - used_days])
return ProrationSplit(total=amount, used=used, unused=unused)