Functional Weave
Code in TypeScript

subscriptions.mrr-arr@1.1.0

impl/python.py

2,299 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 Dict, Sequence

from .math_rational import add_rational, multiply_rational, rational, rational_to_integer  ← from math.rational ^1.0.0 · built alongside by fune
from .money_amount import assert_same_currency, money  ← from money.amount ^1.0.0 · built alongside by fune
from .subscriptions_mrr_arr_types import MrrArr, RecurringCharge

# 1.0.0 declared its own BillingInterval, which callers imported from this
# module; it is now subscriptions.next-billing-date's, imported here so they
# still can.
from .subscriptions_next_billing_date import BillingInterval  ← from subscriptions.next-billing-date ^1.0.0 · built alongside by fune

PER_YEAR: Dict[BillingInterval, int] = {"day": 365, "week": 52, "month": 12, "quarter": 4, "year": 1}


def mrr_arr(subscriptions: Sequence[RecurringCharge], currency: str) -> MrrArr:
    """MRR and ARR from active and past-due subscriptions. Every charge becomes
    an exact annual amount first, and each total is rounded once at the end,
    so a list of annual plans does not lose a penny per plan.
    """
    zero = money(0, currency)
    annual = rational(0, 1)
    counted = 0
    excluded = 0
    for s in subscriptions:
        assert_same_currency(zero, s.amount)
        if s.amount.minor < 0:
            raise ValueError("recurring amount must not be negative, received %d" % (s.amount.minor,))
        if isinstance(s.interval_count, bool) or not isinstance(s.interval_count, int) or s.interval_count < 1:
            raise ValueError("interval count must be 1 or more, received %r" % (s.interval_count,))
        if s.interval not in PER_YEAR:
            raise ValueError(
                'unknown billing interval "%s": expected day, week, month, quarter or year' % (s.interval,)
            )
        if s.status in ("active", "past-due"):
            counted += 1
            annual = add_rational(annual, rational(s.amount.minor * PER_YEAR[s.interval], s.interval_count))
        elif s.status in ("trialing", "paused", "canceled"):
            excluded += 1
        else:
            raise ValueError(
                'unknown subscription status "%s": expected active, past-due, trialing, paused or canceled'
                % (s.status,)
            )
    return MrrArr(
        mrr=money(rational_to_integer(multiply_rational(annual, rational(1, 12)), "half-up"), currency),
        arr=money(rational_to_integer(annual, "half-up"), currency),
        counted=counted,
        excluded=excluded,
    )