spb/air Public MIT
AIR — The Language of Accounting.
Python 100%
1<!--2Projet : AIR — Accounting Intermediate Representation3Auteur : Simon-Pierre Boucher4Contact : contact@spboucher.ai5Fichier : 0003-monetary-amounts.md6-->78# ADR 0003 — Monetary amounts: exact decimals everywhere, rounding is jurisdiction policy910- **Status**: Accepted — 2026-08-0511- **Context**: docs/research/ledger-engines.md (float hazards, TigerBeetle12 fixed-point design) and docs/research/canada-gst-qst.md (statutory13 rounding).1415## Decision16171. **No floats, anywhere, ever.** In JSON/YAML, every amount, quantity, and18 rate is a **string** ("19.99"); in code it is `decimal.Decimal` wrapped in19 the `Money` type (core/money.py). Construction from `float` raises at20 every boundary: `Money`, the Pydantic schema (`ExactDecimal`), and the21 ALSL loader all reject floats independently.222. **Rounding is a named, per-jurisdiction ALSL policy — not an engine23 default.** Research falsified the assumption that banker's rounding is24 universal: GST/QST rounding is **half-up** by statute (Excise Tax Act25 s. 165.2(2): fractions < $0.005 disregarded, ≥ $0.005 deemed one cent;26 Revenu Québec IN-203-V states the same for QST). `RoundingMode` therefore27 supports `half_up` and `half_even`, and the ca-qc policy set selects28 `half_up` with a source citation.293. **Round late.** Intermediate computations (qty × unit price, base × rate,30 amount × fx rate) keep full precision; quantization to the currency's31 ISO 4217 minor unit happens once, when a figure becomes a posted amount —32 and each quantization is recorded in the provenance graph's operation33 string (e.g. `tax:QST@0.09975~half_up`).3435## Consequences3637- Cross-currency arithmetic is a hard error (`CurrencyMismatchError`);38 conversion only happens in the FX pass, explicitly, with a cited rate.39- Determinism holds bit-for-bit: same document + same policies = identical40 journal (property-tested in tests/property/).41