Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from .dates_days_between import days_between ← from dates.days-between ^1.0.0 · built alongside by fune
from .math_round_div import RoundingMode, round_div ← from math.round-div ^1.0.0 · built alongside by fune
from .money_add import add_money ← from money.add ^1.0.0 · built alongside by fune
from .money_amount import Money, assert_same_currency, money ← from money.amount ^1.0.0 · built alongside by fune
from .subscriptions_proration_types import PlanChangeProration
def prorate_change(
old_price: Money,
new_price: Money,
period_start: str,
period_end: str,
change_date: str,
mode: RoundingMode,
) -> PlanChangeProration:
"""Stripe-style proration for a mid-cycle plan change, by whole days: a
credit for the unused remainder of the old plan and a charge for the same
remainder on the new plan, each rounded once from the exact fraction.
"""
total_days = days_between(period_start, period_end)
if total_days <= 0:
raise ValueError(
"period end must be after period start, received %s to %s" % (period_start, period_end)
)
elapsed = days_between(period_start, change_date)
if elapsed < 0 or elapsed > total_days:
raise ValueError(
"change date must be within the billing period, received %s for %s to %s"
% (change_date, period_start, period_end)
)
assert_same_currency(old_price, new_price)
remaining_days = total_days - elapsed
credit = money(-round_div(old_price.minor * remaining_days, total_days, mode), old_price.currency)
charge = money(round_div(new_price.minor * remaining_days, total_days, mode), new_price.currency)
return PlanChangeProration(
total_days=total_days,
remaining_days=remaining_days,
credit=credit,
charge=charge,
net=add_money(charge, credit),
)