Use multi-currency Ledgers to easily build products that track currencies, stocks and inventories.
A Ledger can contain Ledger Accounts of different currencies, like USD and GBP bank accounts. Ledger Accounts can also be multi-currency, like one representing a stock portfolio, with a balance for each symbol.
You can post Ledger Entries with multiple currencies. It must follow the Accounting Equation per currency, so you'll need at least four Ledger Lines.
FRAGMENT includes a list of common currencies. You can also add your own custom currencies
Multi-currency ledgers often reflect transitory states: a company accepts payment in one currency intending to convert it to another currency. Between accepting and converting the money, the exchange rate could change. Tracking the potential gain or loss from this change is called exposure.
To track exposure, use a Change Ledger Account that has multiple balances, one for each currency. Here's an example that tracks exposure between USD and EUR:
In this example,
To create a multi-currency Ledger, set defaultCurrencyMode
to multi
and unset defaultCurrency
:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{...},
{...}
]
},
"ledgerEntries": {...}
}
For Ledger Accounts in a single currency, such as bank accounts, set currencyMode
and currency
on the account directly:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "bank-account",
"currencyMode": "single",
"currency": {
"code": "USD"
}
},
{...}
]
},
"ledgerEntries": {...}
}
Like other Ledger Account properties, currencyMode
and currency
are inherited by child Ledger Accounts unless they are overridden.
You can define multi-currency Ledger Entries types in your Schema in the same way as single-currency Ledger Entries.
Multi-currency Ledger Accounts accept Ledger Lines in any currency, so Ledger Entries that post to them must specify the currency of each Ledger Line:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"type": "asset",
"key": "bank-account"
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account_usd",
"description": "Fund {{funding_amount}} USD",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-account"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "USD"
}
},
{...other line}
]
}
]
}
}
You can parameterize currency
to make your Ledger Entries more reusable:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"type": "asset",
"key": "bank-account"
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account",
"description": "Fund {{funding_amount}} {{currency}}",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-account"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "{{currency}}"
}
},
{...other line}
]
}
]
}
}
You can also post multi-currency Ledger Entries to Ledger Account templates which parameterize currency
. This is useful for creating linked Ledger Accounts if you have multiple bank accounts in different currencies in a multi-currency Ledger.
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "bank-accounts",
"template": true,
"currencyMode": "single",
"currency": {
"code": "{{currency}}"
}
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account",
"description": "Fund {{funding_amount}} {{currency}}",
"lines": [
{
"key": "funds_arrive_in_bank",
"account": {
"path": "bank-accounts:{{currency}}"
},
"amount": "{{funding_amount}}",
"currency": {
"code": "{{currency}}"
}
},
{...other line}
]
}
]
}
}
Ledger Entry Conditions against multi-currency Ledger Accounts need to specify the currency the condition applies to:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{...},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "p2p_transfer",
"description": "Move {{funding_amount}} {{currency}}",
"lines": [
{...},
{...}
],
"conditions": [
{
"account": {
"path": "liabilities/users:{{from_user_id}}"
},
"currency": {
"code": "{{currency}}"
},
"postcondition": {
"ownBalance": {
"gte": "0"
}
}
}
]
}
]
}
}
Balances on multi-currency Ledger Accounts are lists of currency and amount, as opposed to just a single amount. To read all balances in all currencies, query the plural versions of the singular balance field.
You can read the latest balance in a specific currency or list the latest balance in all currencies.
Multi-currency Ledger Accounts have three balance lists:
ownBalances
, the sum of all Ledger Lines in the Ledger Account per currency, excluding Ledger Lines in child Ledger AccountschildBalances
, the sum of all Ledger Lines in child Ledger Accounts per currencybalances
, the sum of all Ledger Lines, including child Ledger Accounts per currencyTo read a specific currency's balance, pass in the currency
argument:
query GetUSDBalance(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalance(currency: { code: USD })
}
}
{
"ledgerAccount": {
"path": "assets/banks/user-cash",
"ledger": {
"ik": "multi-currency-ledger"
}
}
}
To read all the balances for a multi-currency Ledger Account, use ownBalances
:
query GetAllBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalances {
nodes {
currency {
code
}
amount
}
}
}
}
To read aggregated balances for multi-currency Ledger Accounts, pass in the currency
argument, or query childBalances
and balances
:
query GetBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
childBalance(currency: { code: USD })
childBalances {
nodes {
currency {
code
}
amount
}
}
balance(currency: { code: USD })
balances {
nodes {
currency {
code
}
amount
}
}
}
}
If any Ledger Account has a descendant that is a multi-currency Ledger Account or if it has descendants of different currencies, it has childBalances
and balances
.
To read consistent balances for multi-currency Ledger Accounts, pass in the consistencyMode
argument:
query GetOwnBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalance(
consistencyMode: strong
currency: { code: USD }
)
ownBalances(consistencyMode: strong) {
nodes {
currency {
code
}
amount
}
}
}
}
To read historical balances for multi-currency Ledger Accounts, pass in the at
argument:
query GetOldBalances(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalance(at: "1969", currency: { code: USD })
ownBalances(at: "1969") {
nodes {
currency {
code
}
amount
}
}
}
}
Multi-currency Ledger Accounts support reading balance changes:
ownBalanceChanges
, how much ownBalances
changedchildBalanceChanges
, how much childBalances
changedbalanceChanges
, how much balances
changedquery GetBalanceChanges(
$ledgerAccount: LedgerAccountMatchInput!
) {
ledgerAccount(ledgerAccount: $ledgerAccount) {
ownBalanceChange(
period: "1969"
currency: { code: USD }
)
ownBalanceChanges(period: "1969") {
nodes {
currency {
code
}
amount
}
}
childBalanceChange(
period: "1968-12"
currency: { code: USD }
)
childBalanceChanges(period: "1968-12") {
nodes {
currency {
code
}
amount
}
}
balanceChange(
period: "1968-12-25"
currency: { code: USD }
)
balanceChanges(period: "1968-12-25") {
nodes {
currency {
code
}
amount
}
}
}
}
Balance change queries require you to specify a period
. This can be a year, quarter, month, day or hour.
You can define your own currencies to track any type of value, like rewards points, stocks or physical items.
To create a custom currency, call the createCustomCurrency
mutation:
mutation CreateCustomCurrency (
$customCurrency: CreateCustomCurrencyInput!,
) {
createCustomCurrency(
customCurrency: $customCurrency
) {
... on CreateCustomCurrencyResult {
customCurrency {
code
customCurrencyId
precision
name
customCode
}
}
... on Error {
code
message
}
}
}
{
"customCurrency": {
"customCurrencyId": "blue-gems",
"precision": 0,
"name": "Blue Gems",
"customCode": "BLUE"
}
}
To use a custom currency, set the customCurrencyId
on the currency
field of a Ledger Account and Ledger Line:
{
"key": "...",
"chartOfAccounts": {
"defaultCurrencyMode": "multi",
"accounts": [
{
"key": "gems-issued",
"currencyMode": "single",
"currency": {
"code": "CUSTOM",
"customCurrencyId": "blue-gems"
}
},
{...}
]
},
"ledgerEntries": {
"types": [
{
"type": "issue-blue-gems",
"description": "Issue blue gems",
"lines": [
{
"key": "increase-pool",
"account": {
"path": "gems-issued"
},
"amount": "{{amount}}",
"currency": {
"code": "CUSTOM",
"customCurrencyId": "blue-gems"
}
},
{...other line}
]
}
]
}
}