Functional Weave
Code in TypeScript

telecoms.roaming-charge@1.0.1

README.md

2,382 bytes · view raw

# telecoms.roaming-charge

Prices a trip abroad against a roaming tariff: which zone the country is in
on that date, and what the usage there costs.

**The tariff in this package is illustrative. It is not any operator's
tariff**, and its prices and dates (including the 2026 price rise in the
EUROPE zone) are invented to exercise the calculation. It exists so the
function, its vectors and its dated-data handling can be shown working. To
bill real customers, publish your own version of this capability with your
tariff rows in `data/roaming-tariffs.json`, or add rows under your own tariff
name; the code does not change.

A tariff is rows of zones. Each row names the countries in the zone
(space-separated ISO 3166-1 alpha-2 codes), the currency, and five prices in
minor units: a fee per day of use, and prices per MB of data, per minute of
calls made, per minute of calls received and per text sent. A daily-pass zone
has a daily fee and zero unit prices; a pay-as-you-go zone the opposite. The
charge is simply

    dailyFees = days x dailyFee
    data      = dataMb x dataPerMb
    calls     = callOutMinutes x callOutPerMinute + callInMinutes x callInPerMinute
    sms       = smsOut x smsOut price
    total     = the sum of the four

all exact, with no rounding. Minutes are whole minutes already rounded up per
call (telecoms.call-rating does per-call rounding), and data is whole MB, so a
usage record is priced by one country and one date at a time.

Rows are dated with `validFrom` and `validTo` (inclusive; null while in
force). The row in force on `onDate` wins, and if two overlap the later
`validFrom` wins. A project that installs with `history=current` keeps only
the rows still in force; asking such a build about an older date fails with
an explanation instead of silently pricing it at today's rates.

Home-country use, a country not in any zone of the tariff, an unknown tariff
and a date before the tariff's first row are all errors ("no roaming zone"),
because pricing unknown usage at zero is how roaming revenue leaks. Other
errors: a country that is not two uppercase letters, a malformed or impossible
date, and negative usage.

What this does not model: fair-use limits inside daily passes, spend caps
(see telecoms.data-allowance), and per-country exceptions inside a zone.

1.0.1 fixes Python accepting a trailing newline in country; adds tests.