Functional Weave
Code in Rust

property.service-charge-apportion@1.0.0

README.md

2,146 bytes · view raw

# property.service-charge-apportion

Shares a building's service charge out between its units the way their leases
say, and makes the pennies add up: the lines always total the service charge
exactly, never a penny over or under.

Leases state a unit's share in one of three ways, chosen by `basis`:

- **fraction**: "one-third", "3/20ths". Each unit gives `share` over `of`.
  The fractions must add up to exactly 1, since a lease scheme that does not
  recover the whole cost (or recovers more) is something to raise with the
  landlord, not to paper over.
- **floor-area**: each unit's area, in any one integer unit (square feet,
  square metres, or square metres × 100 for 0.01 m² precision). The share is
  the unit's area over the total area.
- **percentage**: basis points (2500 = 25%), adding up to exactly 10,000.

## Exact to the penny

Each unit's exact share is total × share, and the pennies left over after
rounding every share down are given one at a time to the units with the
largest remainders (ties to the earlier unit), by `money.allocate`. So a
£100.01 charge split 1/3, 1/6, 1/2 is £33.34, £16.67 and £50.00. Rounding each
share to the nearest penny separately would give £33.34, £16.67 and £50.01:
£100.02, a penny that was never spent.

Fractions are brought to a common denominator exactly (with
`math.gcd-lcm`), never converted to decimals.

## Edge cases

- A unit with a zero share pays nothing but still gets a line.
- A negative total (a surplus refunded, a credit) is shared the same way.
- `of` must be null unless the basis is fraction, and must be given (and be
  positive) when it is.
- The shares' total, times the service charge in minor units, must stay below
  2^53, so that every language computes the same split exactly. Denominators
  whose lowest common multiple is too large to share a charge that precisely
  are refused rather than approximated.

## What it does not do

It does not weight different cost heads differently (a lift schedule that
excludes ground-floor flats is a second call with its own units), cap any
unit's contribution, or handle reserve fund contributions separately.