Functional Weave
Code in Rust

subscriptions.mrr-arr@1.1.0

README.md

2,269 bytes · view raw

# subscriptions.mrr-arr

MRR (monthly recurring revenue) and ARR (annual recurring revenue) from the
subscriptions a business has on one day. Every subscription is first turned
into what it earns in a year, exactly:

| interval | a year is      |
|----------|----------------|
| year     | 1 interval     |
| quarter  | 4 intervals    |
| month    | 12 intervals   |
| week     | 52 intervals   |
| day      | 365 intervals  |

divided by `intervalCount` (a 6-monthly plan earns its price twice a year).
ARR is the sum of those, and MRR is the same sum divided by 12. Both are
rounded once, half-up to the minor unit, from the exact fraction
(math.rational). Rounding each subscription's monthly share first and adding
up is the naive way and it drifts: three annual plans of 100.00 are 25.00 of
MRR, but 8.33 + 8.33 + 8.33 is 24.99. Because each figure is rounded
separately, MRR x 12 can differ from ARR by a few minor units; both are right.

Weeks and days are conventions, not calendar facts: 52 weeks and 365 days to
the year. Businesses that use 52.14 weeks or 365.25 days get slightly
different figures for weekly and daily plans; monthly, quarterly and annual
plans are exact under any convention.

What counts. `active` and `past-due` subscriptions count: a failed payment
still in dunning has not churned yet, so it stays in MRR until it is
cancelled. (Metrics tools differ on this; filter past-due out before calling
if yours does.) `trialing` subscriptions are left out (no revenue
yet), as are `paused` and `canceled` ones. The amount should be the recurring
price after recurring discounts and before tax; one-off charges, usage and
setup fees are not recurring revenue and do not belong here.

Errors: an amount in a currency other than `currency`, a negative amount, and
an interval count below 1.

Since 1.1.0 the `interval` of a `RecurringCharge` is the `BillingInterval`
declared by `subscriptions.next-billing-date` (the same five values, `day`,
`week`, `month`, `quarter` and `year`), rather than a copy declared here, so
a subscription record typed for one capability is accepted by the other.
`BillingInterval` can still be imported from this capability's module in
TypeScript and Python, which re-export it. The answers are the same as
1.0.0's.