GraphQL Currencies
This page documents GraphQL operations for managing currencies and exchange rates in Crane Ledger.
Currency Queries
List All Currencies
Get all supported currencies in the system.
query GetCurrencies {
currencies {
code
name
symbol
decimalPlaces
}
}
Returns:
- All active currencies
- Currency codes (ISO 4217)
- Display names and symbols
- Decimal precision for each currency
Get Currency by Code
Get a specific currency (though this is less common as currencies are system-wide).
query GetCurrency($code: String!) {
currencies(where: { code: { eq: $code } }) {
code
name
symbol
decimalPlaces
}
}
Currency Information
Currency Properties
Each currency has the following properties:
const CURRENCY_PROPERTIES = {
USD: { name: 'US Dollar', symbol: '$', decimalPlaces: 2 },
EUR: { name: 'Euro', symbol: '€', decimalPlaces: 2 },
GBP: { name: 'British Pound', symbol: '£', decimalPlaces: 2 },
JPY: { name: 'Japanese Yen', symbol: '¥', decimalPlaces: 0 },
CAD: { name: 'Canadian Dollar', symbol: 'C$', decimalPlaces: 2 },
AUD: { name: 'Australian Dollar', symbol: 'A$', decimalPlaces: 2 },
CHF: { name: 'Swiss Franc', symbol: 'Fr', decimalPlaces: 2 },
CNY: { name: 'Chinese Yuan', symbol: '¥', decimalPlaces: 2 }
};
Decimal Precision
Different currencies have different decimal requirements:
const getCurrencyPrecision = (currencyCode) => {
const precisionMap = {
JPY: 0, // Japanese Yen (no cents)
KRW: 0, // Korean Won (no cents)
VND: 0, // Vietnamese Dong (no cents)
BIF: 0, // Burundian Franc (no cents)
CLP: 0, // Chilean Peso (no cents)
// Most currencies use 2 decimal places
default: 2
};
return precisionMap[currencyCode] || precisionMap.default;
};
Organization Base Currency
Get Organization Currency
Every organization has a base currency for all financial operations.
query GetOrganizationCurrency {
organization {
baseCurrency {
code
name
symbol
decimalPlaces
}
}
}
Important:
- All monetary amounts are stored in the organization's base currency
- Multi-currency transactions are converted to base currency for storage
- Reports show amounts in base currency
Currency Conversion
Transactions in foreign currencies are converted to base currency:
const convertToBaseCurrency = (amount, fromCurrency, toCurrency, exchangeRate) => {
if (fromCurrency === toCurrency) {
return amount;
}
// Apply exchange rate
const convertedAmount = amount * exchangeRate;
// Round to base currency precision
const precision = getCurrencyPrecision(toCurrency);
const multiplier = Math.pow(10, precision);
return Math.round(convertedAmount * multiplier) / multiplier;
};
Multi-Currency Transactions
Transaction Currency Handling
Transactions can be recorded in any currency:
query GetMultiCurrencyTransactions {
organization {
recentTransactions(limit: 20) {
id
description
date
amount
currency {
code
name
symbol
}
entries {
accountId
amount
baseAmount
currency {
code
}
}
}
}
}
Fields:
amount: Original amount in transaction currencycurrency: Transaction currencybaseAmount: Converted amount in organization's base currency
Currency Exchange Rates
Exchange rates are used for currency conversion:
// Example exchange rate structure
const exchangeRates = {
'USD/EUR': 0.85, // 1 USD = 0.85 EUR
'EUR/USD': 1.18, // 1 EUR = 1.18 USD
'GBP/USD': 1.27, // 1 GBP = 1.27 USD
'USD/GBP': 0.79, // 1 USD = 0.79 GBP
};
// Get exchange rate between two currencies
const getExchangeRate = (fromCurrency, toCurrency) => {
if (fromCurrency === toCurrency) return 1;
const directRate = exchangeRates[`${fromCurrency}/${toCurrency}`];
if (directRate) return directRate;
// Try inverse rate
const inverseRate = exchangeRates[`${toCurrency}/${fromCurrency}`];
if (inverseRate) return 1 / inverseRate;
// Try cross rates through USD
const toUSD = exchangeRates[`${fromCurrency}/USD`] || (1 / exchangeRates[`USD/${fromCurrency}`]);
const fromUSD = exchangeRates[`USD/${toCurrency}`] || (1 / exchangeRates[`${toCurrency}/USD`]);
return toUSD * fromUSD;
};
Currency Formatting
Localized Formatting
Format amounts according to currency conventions:
const formatCurrency = (amount, currencyCode) => {
const currency = getCurrencyInfo(currencyCode);
const precision = getCurrencyPrecision(currencyCode);
// Format with proper decimal places
const formattedAmount = amount.toFixed(precision);
// Add currency symbol in correct position
const symbolPosition = getSymbolPosition(currencyCode);
if (symbolPosition === 'before') {
return `${currency.symbol}${formattedAmount}`;
} else {
return `${formattedAmount} ${currency.symbol}`;
}
};
const getSymbolPosition = (currencyCode) => {
// Most currencies put symbol before amount
const afterSymbolCurrencies = ['EUR', 'GBP', 'CHF'];
return afterSymbolCurrencies.includes(currencyCode) ? 'after' : 'before';
};
Examples
formatCurrency(1234.56, 'USD'); // "$1234.56"
formatCurrency(1234.56, 'EUR'); // "1234.56 €"
formatCurrency(1234.56, 'JPY'); // "¥1235" (rounded to 0 decimals)
formatCurrency(1234.56, 'BHD'); // ".د.ب1234.560" (3 decimals for Bahrain Dinar)
Currency Validation
Supported Currencies
Only predefined currencies are supported:
const SUPPORTED_CURRENCIES = [
'USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD', 'CHF', 'CNY',
'SEK', 'NZD', 'MXN', 'SGD', 'HKD', 'NOK', 'KRW', 'TRY',
'RUB', 'INR', 'BRL', 'ZAR', 'EGP', 'THB', 'IDR', 'MYR',
'PHP', 'VND', 'CZK', 'PLN', 'HUF', 'ISK', 'RON', 'HRK',
'DKK', 'BGN', 'EEK', 'LVL', 'LTL', 'MTL', 'CYP', 'SKK'
];
const isSupportedCurrency = (currencyCode) => {
return SUPPORTED_CURRENCIES.includes(currencyCode.toUpperCase());
};
Currency Code Validation
Validate currency codes using ISO 4217 standard:
const validateCurrencyCode = (code) => {
if (!code || typeof code !== 'string') {
throw new Error('Currency code must be a non-empty string');
}
if (code.length !== 3) {
throw new Error('Currency code must be exactly 3 characters');
}
if (!/^[A-Z]{3}$/.test(code)) {
throw new Error('Currency code must contain only uppercase letters');
}
if (!isSupportedCurrency(code)) {
throw new Error(`Currency code ${code} is not supported`);
}
return true;
};
Exchange Rate Management
Current Exchange Rates
Get current exchange rates (when implemented):
query GetExchangeRates {
exchangeRates {
fromCurrency
toCurrency
rate
effectiveDate
source
}
}
Rate Update Process
Exchange rates are typically updated from external sources:
const updateExchangeRates = async () => {
// Fetch rates from external API
const rates = await fetchExchangeRatesFromAPI();
// Validate rates
validateExchangeRates(rates);
// Store in database
await storeExchangeRates(rates);
// Update cached rates
await updateRateCache(rates);
return rates;
};
const validateExchangeRates = (rates) => {
for (const rate of rates) {
if (rate.rate <= 0) {
throw new Error(`Invalid exchange rate for ${rate.fromCurrency}/${rate.toCurrency}: ${rate.rate}`);
}
if (rate.rate > 1000) {
throw new Error(`Exchange rate seems unreasonably high: ${rate.rate}`);
}
}
};
Multi-Currency Reporting
Currency Conversion in Reports
Reports show amounts in base currency, but can include original currencies:
query GetMultiCurrencyReport {
organization {
transactions(limit: 100) {
id
description
amount
currency {
code
}
baseAmount
entries {
accountId
amount
baseAmount
currency {
code
}
}
}
}
}
Foreign Currency Balances
Track balances in foreign currencies:
const getForeignCurrencyBalances = async () => {
const transactions = await getAllTransactions();
const balances = {};
for (const transaction of transactions) {
const currency = transaction.currency.code;
if (currency !== getBaseCurrency()) {
balances[currency] = (balances[currency] || 0) + transaction.amount;
}
}
return balances;
};
Currency Risk Management
Exposure Analysis
Analyze foreign currency exposure:
const analyzeCurrencyExposure = async () => {
const foreignBalances = await getForeignCurrencyBalances();
const exposure = {
currencies: Object.keys(foreignBalances),
totalExposure: 0,
largestExposure: null,
exposureByCurrency: {}
};
for (const [currency, balance] of Object.entries(foreignBalances)) {
const baseAmount = convertToBaseCurrency(balance, currency, getBaseCurrency());
exposure.exposureByCurrency[currency] = baseAmount;
exposure.totalExposure += Math.abs(baseAmount);
if (!exposure.largestExposure ||
Math.abs(baseAmount) > Math.abs(exposure.largestExposure.amount)) {
exposure.largestExposure = { currency, amount: baseAmount };
}
}
return exposure;
};
Hedging Recommendations
Generate hedging recommendations based on exposure:
const generateHedgingRecommendations = (exposure) => {
const recommendations = [];
// Recommend hedging if exposure > 10% of total assets
const totalAssets = getTotalAssets();
const exposureThreshold = totalAssets * 0.1;
if (exposure.totalExposure > exposureThreshold) {
recommendations.push({
type: 'hedge',
message: `Consider hedging ${exposure.largestExposure.currency} exposure of ${formatCurrency(exposure.largestExposure.amount)}`,
action: 'forward_contract'
});
}
// Recommend diversification if single currency exposure > 50% of total exposure
const largestPercentage = Math.abs(exposure.largestExposure.amount) / exposure.totalExposure;
if (largestPercentage > 0.5) {
recommendations.push({
type: 'diversify',
message: `High concentration in ${exposure.largestExposure.currency}. Consider diversifying currency exposure.`,
action: 'currency_basket'
});
}
return recommendations;
};
Currency in Financial Reports
Balance Sheet Currency
Balance sheets show values in base currency:
query GetBalanceSheet {
organization {
balanceSheet {
generatedAt
totalAssets
totalLiabilities
totalEquity
# All amounts in base currency
}
}
}
Income Statement Currency
Income statements show revenues and expenses in base currency:
query GetIncomeStatement {
organization {
incomeStatement {
totalRevenue
totalExpenses
netIncome
# All amounts in base currency
}
}
}
Multi-Currency Footnotes
Reports can include currency information:
const addCurrencyFootnotes = (report) => {
const foreignCurrencies = getForeignCurrenciesUsed();
if (foreignCurrencies.length > 0) {
report.footnotes = report.footnotes || [];
report.footnotes.push({
type: 'currency',
text: `Report amounts are in ${getBaseCurrency()}. Foreign currency transactions were converted using period-end exchange rates.`,
currencies: foreignCurrencies
});
}
return report;
};
Error Handling
Currency Errors
VALIDATION_ERROR: Invalid currency codeNOT_FOUND: Currency not supportedINTERNAL_ERROR: Exchange rate unavailable
Conversion Errors
VALIDATION_ERROR: Invalid exchange rateINTERNAL_ERROR: Currency conversion failed
Best Practices
Currency Selection
Choose base currency carefully:
const recommendBaseCurrency = (businessInfo) => {
const { country, industry, customerBase } = businessInfo;
// For US businesses, USD is obvious choice
if (country === 'US') return 'USD';
// For EU businesses, EUR is common
if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(country)) return 'EUR';
// For international businesses, consider USD or EUR
if (customerBase === 'global') return 'USD';
// Default to USD for most businesses
return 'USD';
};
Exchange Rate Updates
Keep exchange rates current:
const EXCHANGE_RATE_UPDATE_SCHEDULE = {
major: '0 */4 * * *', // Every 4 hours for major currencies
minor: '0 9 * * 1-5', // Weekdays at 9 AM for minor currencies
weekend: '0 12 * * 0,6' // Weekends at noon
};
const scheduleRateUpdates = async () => {
// Update major currency pairs frequently
await scheduleJob('update_major_rates', EXCHANGE_RATE_UPDATE_SCHEDULE.major);
// Update minor currencies daily
await scheduleJob('update_minor_rates', EXCHANGE_RATE_UPDATE_SCHEDULE.minor);
// Update on weekends
await scheduleJob('update_weekend_rates', EXCHANGE_RATE_UPDATE_SCHEDULE.weekend);
};
Currency Audit Trail
Maintain audit trail for currency operations:
const logCurrencyOperation = async (operation, details) => {
const auditEntry = {
operation,
timestamp: new Date().toISOString(),
user: getCurrentUser(),
details,
exchangeRates: await getCurrentExchangeRates()
};
await writeToAuditLog(auditEntry);
};
// Example usage
await logCurrencyOperation('transaction_conversion', {
transactionId: 'TXN_123',
fromCurrency: 'EUR',
toCurrency: 'USD',
originalAmount: 1000,
convertedAmount: 1180,
exchangeRate: 1.18
});
Rounding Policies
Implement consistent rounding for currency operations:
const ROUNDING_POLICIES = {
bank: 'round_half_up', // Standard banking round
cash: 'round_half_up', // For cash transactions
tax: 'round_half_up', // For tax calculations
report: 'round_half_up' // For financial reports
};
const roundCurrency = (amount, currencyCode, context = 'bank') => {
const policy = ROUNDING_POLICIES[context];
const precision = getCurrencyPrecision(currencyCode);
switch (policy) {
case 'round_half_up':
return Math.round(amount * Math.pow(10, precision)) / Math.pow(10, precision);
case 'round_down':
return Math.floor(amount * Math.pow(10, precision)) / Math.pow(10, precision);
case 'round_up':
return Math.ceil(amount * Math.pow(10, precision)) / Math.pow(10, precision);
default:
return Math.round(amount * Math.pow(10, precision)) / Math.pow(10, precision);
}
};
Need help?
Create a free account to access our support portal. Once signed in, use the Support tab in your dashboard to submit a support ticket — our team typically responds within 24 hours.
- ✨ For LLMs/AI assistants: Read our structured API reference