Functional Weave
Code in TypeScript

subscriptions.invoice@1.0.0

README.md

2,662 bytes · view raw

# subscriptions.invoice

Builds the invoice for one renewal of a subscription. The plan, add-on and
usage lines are ordinary invoice lines (finance.invoice.calculate's
`InvoiceLine`, per-line discounts included); what this capability adds is the
subscription part - which coupons apply in this billing period, how much each
takes off, and how that reduction is taxed - and it hands the result to
finance.invoice.calculate for VAT and totals. It invents no arithmetic of its
own: line totals, percentages, splitting and VAT are all existing
capabilities.

**Which coupons apply.** Coupons follow Stripe's three durations, counted in
billing periods of this subscription: `once` applies only in `startPeriod`;
`repeating` applies in `durationPeriods` periods starting at `startPeriod`
(so a 3-period coupon starting in period 2 covers periods 2, 3 and 4, not
5); `forever` applies from `startPeriod` on. Stripe counts repeating coupons
in months; for a monthly plan that is the same thing.

**How much.** The coupons that apply are taken in the order given, each from
what is left after the ones before it. A percentage coupon takes that
percentage of the remaining net, rounded half-up (money.apply-rate); an
amount coupon takes its amount, but never more than what is left, so an
invoice is never driven below zero by coupons (the unused part of an amount
coupon is lost, as in Stripe). Order matters: 50% off then 10.00 off 90.00 is
35.00, the other way round it is 40.00.

**How it is taxed.** Coupons reduce the taxable amount, as a discount given
at the time of supply does for UK VAT. Each coupon becomes a negative line,
one per VAT category on the invoice, with its amount split between categories
in proportion to their net (money.allocate, so the split adds up exactly), and
each line is taxed at its category's rate like any other line. On an invoice
where everything is standard-rated, which is most SaaS, that is one discount
line per coupon.

Proration credits and charges (subscriptions.proration) go in as add-on
lines with negative or positive prices. They are discounted along with
everything else; Stripe instead marks proration lines as not discountable,
so leave coupons off an invoice that should match Stripe exactly, or apply
them before prorating.

Errors: a period number below 1; a coupon with both or neither of
basisPoints and amountOff, a percentage outside 0 to 10000, a negative or
foreign-currency amount, a start period below 1, a repeating coupon without
durationPeriods or another kind with one; and everything
finance.invoice.calculate refuses (mixed currencies, unknown tax categories).
The invoice's currency is the plan's.