Functional Weave
Code in TypeScript

telecoms.call-rating@1.0.0

README.md

1,936 bytes · view raw

# telecoms.call-rating

Rates one call record (CDR) against a rate deck the way telecoms billing does
it, in four steps:

1. **Destination by longest prefix.** The dialled number is matched against
   every prefix in the deck and the longest match wins, so 447700900123
   matches `447` (UK mobile) rather than `44` (UK fixed). Numbers are in
   international format, E.164 digits with or without a leading `+`;
   convert national numbers (07700 900123) before rating. No match is an
   error: silently rating an unknown destination at zero is how fraud
   traffic goes unbilled.
2. **Billing increments.** An increment is written first/next: 60/60 is per
   minute, 30/6 is a 30-second minimum then 6-second blocks, 1/1 is
   per second. The duration is rounded up to them: 61 seconds on 60/60 is
   120 billed seconds; 125 seconds on 30/6 is 126.
3. **Charge.** perMinute x billedSeconds / 60, rounded once to a whole minor
   unit by the caller's `mode` (math.round-div), plus the connection fee.
   Operators differ on how they round fractions of a penny, so it is an
   argument rather than a hidden choice.
4. **Minimum charge.** If the result is below the minimum call charge, the
   minimum is charged. The minimum includes the connection fee.

An unanswered call (0 seconds) is not charged at all: no connection fee, no
minimum. Prices are per minute in minor units, so a rate of 0.5p a minute
cannot be written; use a deck in a smaller currency unit if you need one.

The rate deck is an argument because every operator has its own and changes
it often; this capability holds no rates of its own. Deck rows are checked on
every call: prefixes must be digits and unique, increments at least 1 second,
and every price in one currency.

Errors: no matching prefix, a dialled number that is not digits, a negative
duration, a duplicate or non-numeric prefix, an increment below 1, mixed
currencies, and an unknown rounding mode.