Functional Weave
Code in Rust

subscriptions.proration@1.0.0

impl/python.py

1,650 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 .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),
    )