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 };
}