Functional Weave
Code in TypeScript

finance.ledger.trial-balance@1.0.0

impl/typescript.ts

2,104 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 { validateJournal } from "./finance_ledger_journal_validate.ts";  ← from finance.ledger.journal-validate ^1.0.0 · built alongside by fune
import { type JournalLine } from "./finance_ledger_journal_validate_types.ts";
import { addMoney } from "./money_add.ts";  ← from money.add ^1.0.0 · built alongside by fune
import { type Money, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { type TrialBalance, type TrialBalanceRow } from "./finance_ledger_trial_balance_types.ts";

const FATAL = ["invalid-account", "currency-mismatch", "negative-amount"];

/**
 * A trial balance: each account's balance on its own side, and whether the
 * debit column equals the credit column.
 *
 * Lines that cannot be totalled are thrown with journal-validate's message; an
 * unbalanced ledger is an answer, not an error.
 */
export function trialBalance(lines: readonly JournalLine[], currency: string): TrialBalance {
  const check = validateJournal(lines, currency);
  for (const problem of check.problems) {
    if (FATAL.includes(problem.code)) throw new RangeError(problem.message);
  }

  const zero = money(0, currency);
  const totals = new Map<string, { debits: Money; credits: Money }>();
  for (const line of lines) {
    if (line.debit.minor === 0 && line.credit.minor === 0) continue;
    const current = totals.get(line.account) ?? { debits: zero, credits: zero };
    totals.set(line.account, {
      debits: addMoney(current.debits, line.debit),
      credits: addMoney(current.credits, line.credit),
    });
  }

  const accounts = [...totals.keys()].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
  const rows: TrialBalanceRow[] = [];
  let totalDebit = zero;
  let totalCredit = zero;
  for (const account of accounts) {
    const { debits, credits } = totals.get(account)!;
    const net = debits.minor - credits.minor;
    const debitBalance = money(net > 0 ? net : 0, currency);
    const creditBalance = money(net < 0 ? -net : 0, currency);
    rows.push({ account, debits, credits, debitBalance, creditBalance });
    totalDebit = addMoney(totalDebit, debitBalance);
    totalCredit = addMoney(totalCredit, creditBalance);
  }
  return { rows, totalDebit, totalCredit, balanced: totalDebit.minor === totalCredit.minor };
}