How money works
A money value is an exact decimal amount interpreted in one ISO currency. It is never handled as a floating-point number.
The money model
Section titled “The money model”Currency belongs to the column, not to each row. A money column is defined once with:
| Column setting | Meaning |
|---|---|
| Currency | One canonical uppercase ISO-4217 code, such as EUR, USD, or JPY |
| Rounding | Preserve all precision, or round half-up to the currency’s normal decimal places |
Rows then contain only the decimal amount. For example, every value in a total_usd column declared as USD is USD; callers do not send a currency with each value.
One table may contain several money columns with different currencies. A table’s money currencies are also independent of the profile’s accounting currency.
From input to stored value
Section titled “From input to stored value”- The caller sends the amount as a decimal string.
- The server parses it directly as base-10 decimal.
- The column’s rounding rule is applied.
- The result is stored as an exact decimal number.
- Reads return the amount as a decimal string; the currency comes from the column definition.
This avoids a hidden conversion through binary floating point.
| Accepted | Rejected |
|---|---|
1200 |
1,200 |
1200.50 |
1_200.50 |
-0.01 |
+0.01 |
0.0001 |
1e-4 |
0 |
values with surrounding spaces |
Money columns are optional unless a particular managed workflow makes them required. A generic money column may contain zero or a negative amount; business workflows such as journal posting can impose stricter rules.
Rounding
Section titled “Rounding”Half-up columns round on insert and update. Other columns do not.
No rounding
Section titled “No rounding”The written precision is preserved within the server’s exact-decimal range. A USD column can therefore store 12.345678 even though USD normally has two decimal places.
Half-up
Section titled “Half-up”The amount is rounded to the number of decimal places defined by its ISO currency. A tie rounds away from zero.
| Currency | Input | Stored |
|---|---|---|
| EUR — 2 decimals | 12.345 |
12.35 |
| EUR — 2 decimals | -12.345 |
-12.35 |
| JPY — 0 decimals | 12.5 |
13 |
| KWD — 3 decimals | 12.3455 |
12.346 |
Arithmetic itself does not automatically round. Intermediate results retain their available decimal precision until a value is written to a column with a rounding rule.
Arithmetic and comparison
Section titled “Arithmetic and comparison”Money operations preserve currency:
- money can be added to, subtracted from, or compared with money in the same currency;
- money can be multiplied or divided by an ordinary decimal scalar;
- absolute value preserves the currency;
- division by zero and results outside the exact-decimal range are refused.
EUR 10 + EUR 2.50 is EUR 12.50. EUR 10 + USD 10 is invalid. The server never treats equal-looking amounts in different currencies as interchangeable and never performs an implicit exchange.
Money in scripts and aggregates
Section titled “Money in scripts and aggregates”Scripts receive money as a currency-aware value rather than a plain decimal. They must use explicit money operations, and every branch that returns money must return the same currency.
A script writing into a money column must produce that column’s currency. The destination column then applies its rounding rule. Related sums, minimums, and maximums keep the source column’s currency; an empty money sum is zero in that currency.
Money and accounting
Section titled “Money and accounting”A profile has one accounting currency, while its ordinary tables may contain money in any supported currency.
When a journal line already matches the accounting currency, its amount is posted directly. When it differs and the profile’s accounting currency is EUR, the accounting workflow converts it using the explicitly selected ECB or custom-rate path. Foreign-currency posting to a non-EUR accounting profile is refused. The journal stores the rounded accounting amount while retaining the original amount, original currency, and conversion evidence.
See ECB conversion by example for rate-date and exchange-rate selection.
Example
Section titled “Example”Suppose a EUR-accounting profile has an invoice table with these columns:
| Column | Currency | Rounding | Written | Stored |
|---|---|---|---|---|
net |
EUR | Half-up | 19.995 |
20.00 |
foreign_total |
USD | None | 24.994 |
24.994 |
Saving the invoice performs no exchange. If foreign_total is later posted to the EUR ledger, that posting selects a USD/EUR rate, converts the amount, rounds the ledger value, and records the original USD amount with its conversion evidence.
When money is refused
Section titled “When money is refused”- The currency is lowercase, padded with spaces, or not a recognized ISO-4217 code.
- The amount is not a canonical decimal string.
- Arithmetic or comparison mixes currencies.
- A script returns money in a currency different from its destination column.
- Division uses zero, or an operation exceeds the exact-decimal range.
These failures never trigger approximate arithmetic, silent currency conversion, or automatic truncation.