@accounter/server
Version:
Accounter GraphQL server
108 lines (104 loc) • 3.18 kB
text/typescript
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];
}