Functional Weave
Code in TypeScript

finance.fx-revaluation@1.0.0

impl/typescript.ts

1,833 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { addMoney, subtractMoney } from "./money_add.ts";  ← from money.add ^1.0.0 · built alongside by fune
import { money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { convertMoney } from "./money_convert.ts";  ← from money.convert ^1.0.0 · built alongside by fune
import { type ExchangeRate } from "./money_convert_types.ts";
import { type ForeignBalance, type FxRevaluation, type RevaluedBalance } from "./finance_fx_revaluation_types.ts";

/**
 * Revalue foreign-currency balances at the closing rates and report the
 * unrealised gain (positive) or loss (negative) on each and in total.
 */
export function fxRevaluation(
  balances: readonly ForeignBalance[],
  rates: readonly ExchangeRate[],
  functionalCurrency: string,
): FxRevaluation {
  let netGainLoss = money(0, functionalCurrency);
  const lines: RevaluedBalance[] = [];
  for (const item of balances) {
    const foreign = item.balance.currency;
    if (foreign === functionalCurrency) {
      throw new RangeError(`account ${item.account} is already in ${functionalCurrency}`);
    }
    if (item.bookedValue.currency !== functionalCurrency) {
      throw new RangeError(`booked value of account ${item.account} must be in ${functionalCurrency}`);
    }
    const matching = rates.filter((r) => r.base === foreign && r.quote === functionalCurrency);
    if (matching.length === 0) {
      throw new RangeError(`no period-end rate from ${foreign} to ${functionalCurrency}`);
    }
    if (matching.length > 1) {
      throw new RangeError(`more than one period-end rate from ${foreign} to ${functionalCurrency}`);
    }
    const revaluedValue = convertMoney(item.balance, matching[0], "half-up");
    const gainLoss = subtractMoney(revaluedValue, item.bookedValue);
    lines.push({ account: item.account, balance: item.balance, bookedValue: item.bookedValue, revaluedValue, gainLoss });
    netGainLoss = addMoney(netGainLoss, gainLoss);
  }
  return { lines, netGainLoss };
}