Skip to main content

When to use these helpers

Use these helpers when a value is an amount in a currency. A MoneyValue keeps its currency, adds and compares exactly, and refuses to mix two currencies without a conversion. The arithmetic and comparison operators also understand MoneyValues; plain numbers behave exactly as before.

Calling style

Call a helper as roundMoney($.unit_price * $.quantity). Not every helper is available everywhere: validation, form, and document-template helpers have distinct context contracts. Editor suggestions are not proof that a helper is supported at runtime.

Complete function reference

allocate

Splits an amount by weights so the parts add up exactly to the total rounded to the currency’s decimals (2 decimals for a plain number). Leftover units go to the parts with the largest remainders, earlier parts first on a tie. Weights that are all zero give zero parts; weights that are not a list give an empty list. The parts carry no base.Signature: allocate(value, weights)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | allocate(...).

base

With a MoneyValue, returns its captured base-currency equivalent as a number, or null when none was captured. With no argument, returns the organization’s base currency code inside a calculated field and null elsewhere. Any other value gives null.Signature: base(value?)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | base(...).

convert

Converts a MoneyValue to another currency at the organization’s rate (options.type and options.on as for rateOn) and rounds the result to the target currency’s decimals with the organization’s rounding mode. Returns the value unchanged when it is already in that currency, and null when the first argument is not a MoneyValue or to is not text. Stops with MONEY_RATE_MISSING when no rate qualifies. The result records the rate and rate type but has no base.Signature: convert(value, to, options?)Example:
Availability: Reads the organization’s exchange rates only in entity calculated fields evaluated on the server when a record is created or saved. Elsewhere rateOn and fx return null and convert stops with MONEY_RATE_MISSING. Function and transform forms are available.Note: It can also be written as a transform when the first value is the input: value | convert(...).

currencyOf

Returns the ISO currency code of a MoneyValue, or null for any other value.Signature: currencyOf(value)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | currencyOf(...).

fx

Short alias of rateOn with the same arguments, defaults, and result.Signature: fx(currency, on?, type?, to?)Example:
Availability: Reads the organization’s exchange rates only in entity calculated fields evaluated on the server when a record is created or saved. Elsewhere rateOn and fx return null and convert stops with MONEY_RATE_MISSING. Function and transform forms are available.Note: It can also be written as a transform when the first value is the input: value | fx(...).

isMoney

Checks whether a value is a MoneyValue. A Money field is a MoneyValue only where the organization’s Money-aware operators put one in the calculated-field context; otherwise it is a plain number.Signature: isMoney(value)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | isMoney(...).

money

Creates a MoneyValue from an amount (a number or decimal text) and a three-letter uppercase ISO currency code. Returns null when the amount is not numeric or the code is not three uppercase letters. The optional denomination key is kept on the value.Signature: money(amount, currency, denomination?)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | money(...).

rateOn

Returns the exchange rate from a currency to a target currency (the organization’s base currency when to is omitted) as a number. on is a date text or Date and defaults to the moment of evaluation; type is a rate-type key and defaults to the organization’s default rate type. Returns 1 when the currency is the target and null when no rate qualifies. The lookup is asynchronous; the expression waits for it.Signature: rateOn(currency, on?, type?, to?)Example:
Availability: Reads the organization’s exchange rates only in entity calculated fields evaluated on the server when a record is created or saved. Elsewhere rateOn and fx return null and convert stops with MONEY_RATE_MISSING. Function and transform forms are available.Note: It can also be written as a transform when the first value is the input: value | rateOn(...).

roundMoney

Rounds a MoneyValue to its currency’s decimals, or a plain number to 2 decimals or to the decimals of the currency code you pass. A number as the second argument sets the decimals. The mode is half_up, half_even, half_down, up, or down; without it the organization’s rounding mode applies. A rounded MoneyValue keeps its currency and base; a value that is not numeric is returned unchanged.Signature: roundMoney(value, currencyOrDecimals?, mode?)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | roundMoney(...).

sumBase

Totals the captured base-currency equivalents of the MoneyValues in a list, across currencies, and returns a plain number. MoneyValues without a base and plain numbers are skipped; an empty list or a value that is not a list gives 0.Signature: sumBase(list)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | sumBase(...).

sumMoney

Totals a list exactly. MoneyValues must share one currency (MONEY_CURRENCY_MISMATCH otherwise) and give a MoneyValue; plain numbers in the list are added to that amount. A list of plain numbers gives a number, a list with nothing numeric gives 0, and a value that is not a list gives null. The total keeps a base only when every MoneyValue has one and the list has no plain numbers.Signature: sumMoney(list)Example:
Availability: Shared builder expression runtime and document templates; function and transform forms are available. The organization’s currency decimals, rounding mode, base currency, and Money field values are in scope only in entity calculated fields evaluated on the server when a record is created or saved.Note: It can also be written as a transform when the first value is the input: value | sumMoney(...).

Runtime behavior and edge cases

Money values and Money fields

A MoneyValue is an amount with a currency. Create one with money(amount, currency). In an entity calculated field evaluated on the server, a Money field reads as a MoneyValue when the organization has turned on Money-aware operators; otherwise it stays a plain number, and the helpers treat it as one. A MoneyValue exposes these properties: .rateDate and .rateType give the date and type of that rate. When a calculated field’s result is a MoneyValue, the field stores its amount.

Operators with MoneyValues

Unary minus negates a MoneyValue. Dividing by zero gives Infinity, as it does for plain numbers. Comparing a MoneyValue with a value that is not numeric, such as null, is false for == and the ordering operators and true for !=. When neither side is a MoneyValue, every operator behaves exactly as before.

One currency at a time

Adding, subtracting, dividing, or comparing two MoneyValues in different currencies stops the expression with MONEY_CURRENCY_MISMATCH (“Cannot add USD and EUR: convert one of them first.”). sumMoney applies the same rule. Convert one side first:
When a calculated field’s expression stops with an error, the field keeps its previously stored value.

Exact decimal arithmetic

Money arithmetic works on exact decimals rather than floating-point numbers, so amounts do not drift:
Results are not rounded automatically: money(10, "USD") / 3 keeps up to 18 decimal places, and a Money-to-Money ratio keeps up to 10. Only convert and allocate round for you. Round with round(m) or roundMoney before you show, compare, or store an amount that must match the currency’s precision.

Rounding

round(m) and roundMoney(m) round a MoneyValue to its currency’s decimals; round(number, decimals) on a plain number is unchanged. In a calculated field evaluated on the server, the decimals come from the organization’s currency list and the mode from the organization’s rounding setting. Elsewhere KayanOS uses built-in decimals (3 for KWD, JOD, BHD, OMR, TND, LYD, and IQD; 0 for JPY, KRW, CLP, VND, and ISK; 2 otherwise) and half_up. When a Money field uses a fixed number of decimals that differs from its currency, pass that number: roundMoney($.unit_price * $.quantity, 4). Pass a mode only when a rule requires one, for example roundMoney(m, 2, "half_even").

Exchange rates

rateOn, fx, and convert read the organization’s exchange rates only in an entity calculated field evaluated on the server. The lookup uses:
  • Rate type: the type you pass, otherwise the organization’s default rate type.
  • Date: the on date you pass, otherwise the moment the expression runs. The helpers do not read the record’s document date by themselves; pass it, as in convert(m, "USD", { on: $.invoice_date }).
  • Missing-rate policy: the rate type’s policy decides how old a rate may be. Under a same-day policy only a rate for that day qualifies; under a latest-earlier policy the most recent earlier rate within the allowed number of days qualifies. A monthly rate type’s rate covers its whole month, and the type’s fallback rate types are tried in order.
When no rate qualifies, rateOn and fx return null and convert stops with MONEY_RATE_MISSING. Each record evaluation makes at most 200 distinct rate lookups; further lookups find no rate. Outside a calculated field evaluated on the server, such as in rules, document templates, and background recalculation, there are no rates: rateOn returns null and convert stops with MONEY_RATE_MISSING unless the value is already in the target currency.

When .base is null

The base-currency equivalent is captured when a record is saved with an exchange rate. .base and base(m) are null when no rate was captured for that value, and for MoneyValues created inside the expression: money(), convert(), allocate(), and Money plus or minus a plain number. Multiplying or dividing by a number scales the base; adding two MoneyValues keeps a base only when both have one. sumBase skips values without a base, so a total can be understated; test base(m) == null before you rely on it.

Before you publish

  • Start with a known value and confirm the expected result.
  • Handle null, empty text, and empty lists deliberately.
  • Do not place secrets, access tokens, or sensitive personal data in an expression.
  • Read expression contexts and availability before copying an expression between features.
Money helpers in KayanOS