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,
)