Functional Weave
Code in Rust

finance.ledger.trial-balance@1.0.0

impl/python.py

2,140 bytes · the Python 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.

from typing import Dict, List, Sequence, Tuple

from .finance_ledger_journal_validate import validate_journal  ← from finance.ledger.journal-validate ^1.0.0 · built alongside by fune
from .finance_ledger_journal_validate_types import JournalLine
from .finance_ledger_trial_balance_types import TrialBalance, TrialBalanceRow
from .money_add import add_money  ← from money.add ^1.0.0 · built alongside by fune
from .money_amount import Money, money  ← from money.amount ^1.0.0 · built alongside by fune

_FATAL = ("invalid-account", "currency-mismatch", "negative-amount")


def trial_balance(lines: Sequence[JournalLine], currency: str) -> TrialBalance:
    """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 raised with journal-validate's message;
    an unbalanced ledger is an answer, not an error.
    """
    check = validate_journal(lines, currency)
    for problem in check.problems:
        if problem.code in _FATAL:
            raise ValueError(problem.message)

    zero = money(0, currency)
    totals: Dict[str, Tuple[Money, Money]] = {}
    for line in lines:
        if line.debit.minor == 0 and line.credit.minor == 0:
            continue
        debits, credits = totals.get(line.account, (zero, zero))
        totals[line.account] = (add_money(debits, line.debit), add_money(credits, line.credit))

    rows: List[TrialBalanceRow] = []
    total_debit = zero
    total_credit = zero
    for account in sorted(totals):
        debits, credits = totals[account]
        net = debits.minor - credits.minor
        debit_balance = money(net if net > 0 else 0, currency)
        credit_balance = money(-net if net < 0 else 0, currency)
        rows.append(
            TrialBalanceRow(
                account=account,
                debits=debits,
                credits=credits,
                debit_balance=debit_balance,
                credit_balance=credit_balance,
            )
        )
        total_debit = add_money(total_debit, debit_balance)
        total_credit = add_money(total_credit, credit_balance)
    return TrialBalance(
        rows=rows,
        total_debit=total_debit,
        total_credit=total_credit,
        balanced=total_debit.minor == total_credit.minor,
    )