UNPKG

@accounter/server

Version:
108 lines (104 loc) 3.18 kB
import { GraphQLError } from 'graphql'; import { Currency } from '../../../shared/enums.js'; import { getCurrencySymbol } from '../../../shared/helpers/index.js'; import type { LedgerProto } from '../../../shared/types/index.js'; export function validateExchangeRate( businessId: string, ledgerRecords: LedgerProto[], amount: number, defaultLocalCurrency: Currency, ): string | true { try { const exchangeAmount = calculateExchangeRate(businessId, ledgerRecords, defaultLocalCurrency); if ( (!exchangeAmount && !!amount) || (exchangeAmount && Math.abs(exchangeAmount - amount) > 0.005) ) { return 'Exchange rate error'; } return true; } catch (e) { return (e as Error).message; } } function updateAmounts( amounts: Partial<Record<Currency, number>>, currency: Currency, defaultLocalCurrency: Currency, amount?: number, localAmount?: number, ) { if (localAmount) { if (!(defaultLocalCurrency in amounts)) { amounts[defaultLocalCurrency] = 0; } amounts[defaultLocalCurrency]! += localAmount; } if (amount) { if (!(currency in amounts)) { amounts[currency] = 0; } amounts[currency]! += amount; } } export function calculateExchangeRate( businessId: string, ledgerRecords: LedgerProto[], defaultLocalCurrency: Currency, ) { const amounts: Partial<Record<Currency, number>> = {}; for (const record of ledgerRecords) { if (record.creditAccountID1 === businessId) { updateAmounts( amounts, record.currency, defaultLocalCurrency, record.creditAmount1, record.localCurrencyCreditAmount1, ); } if (record.creditAccountID2 === businessId) { updateAmounts( amounts, record.currency, defaultLocalCurrency, record.creditAmount2, record.localCurrencyCreditAmount2, ); } if (record.debitAccountID1 === businessId) { updateAmounts( amounts, record.currency, defaultLocalCurrency, -(record.debitAmount1 ?? 0), -record.localCurrencyDebitAmount1, ); } if (record.debitAccountID2 === businessId) { updateAmounts( amounts, record.currency, defaultLocalCurrency, -(record.debitAmount2 ?? 0), -(record.localCurrencyDebitAmount2 ?? 0), ); } } const currencies = Object.keys(amounts) as Currency[]; if (!(defaultLocalCurrency in amounts)) { throw new GraphQLError('Local currency amount is required'); } const foreignCurrencies = currencies.filter(currency => currency !== defaultLocalCurrency); if (foreignCurrencies.length === 0 && amounts[defaultLocalCurrency] !== 0) { throw new GraphQLError('Exchange should be 0 as there are no foreign currencies involved'); } for (const foreignCurrency of foreignCurrencies) { if (Math.abs(amounts[foreignCurrency] ?? 0) > 0.005) { throw new GraphQLError( `Exchange rate error - ${foreignCurrency} amount not balanced By ${amounts[foreignCurrency]!.toFixed(2)}${getCurrencySymbol(foreignCurrency)}`, ); } } return amounts[defaultLocalCurrency]; }