SPB Git

spb/air Public MIT

AIR — The Language of Accounting.

Python 100%
1.9 KB

# ADR 0003 — Monetary amounts: exact decimals everywhere, rounding is jurisdiction policy

  • Status: Accepted — 2026-08-05
  • Context: docs/research/ledger-engines.md (float hazards, TigerBeetle fixed-point design) and docs/research/canada-gst-qst.md (statutory rounding).

# Decision

  1. No floats, anywhere, ever. In JSON/YAML, every amount, quantity, and rate is a string ("19.99"); in code it is decimal.Decimal wrapped in the Money type (core/money.py). Construction from float raises at every boundary: Money, the Pydantic schema (ExactDecimal), and the ALSL loader all reject floats independently.
  2. Rounding is a named, per-jurisdiction ALSL policy — not an engine default. Research falsified the assumption that banker's rounding is universal: GST/QST rounding is half-up by statute (Excise Tax Act s. 165.2(2): fractions < $0.005 disregarded, ≥ $0.005 deemed one cent; Revenu Québec IN-203-V states the same for QST). RoundingMode therefore supports half_up and half_even, and the ca-qc policy set selects half_up with a source citation.
  3. Round late. Intermediate computations (qty × unit price, base × rate, amount × fx rate) keep full precision; quantization to the currency's ISO 4217 minor unit happens once, when a figure becomes a posted amount — and each quantization is recorded in the provenance graph's operation string (e.g. tax:QST@0.09975~half_up).

# Consequences

  • Cross-currency arithmetic is a hard error (CurrencyMismatchError); conversion only happens in the FX pass, explicitly, with a cited rate.
  • Determinism holds bit-for-bit: same document + same policies = identical journal (property-tested in tests/property/).