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,
)