Agent Wranglers

PRC-22Recommended

Export balanced journals to the accounting system each period

At each period end, export the period's journal entries, summed per account and currency, to the general ledger. Each export is balanced, numbered and reconciled against the ledger totals before the period closes.

How to do it

Accountants post summaries, not every charge. Map each ledger account to an account number in the chart of accounts the accountant uses. Export once per period with a unique export ID, so a rerun replaces the file instead of posting twice. Close the period after export, and book later corrections in the next open period. Reconcile payouts first, as Reconcile every payout says, so cash in the export matches the bank. The entries come from Book every money movement as balanced double-entry postings.

Examples

TypeScript example

export async function exportPeriod(db, period: string) {
  const lines = await db.query(
    `SELECT a.gl_number, p.currency, sum(p.amount_minor) AS amount_minor
     FROM postings p JOIN journal_entries e ON e.id = p.entry_id JOIN gl_accounts a ON a.account = p.account
     WHERE to_char(e.occurred_at, 'YYYY-MM') = $1 GROUP BY a.gl_number, p.currency`, [period])
  const unbalanced = Object.values(Object.groupBy(lines.rows, l => l.currency))
    .some(rows => rows.reduce((sum, l) => sum + BigInt(l.amount_minor), 0n) !== 0n)
  if (unbalanced) throw new Error(`Period ${period} does not balance`)
  return { exportId: `gl-${period}`, lines: lines.rows }
}

Python example

from collections import defaultdict

def export_period(db, period: str) -> dict:
    lines = db.execute(
        """SELECT a.gl_number, p.currency, sum(p.amount_minor)
           FROM postings p JOIN journal_entries e ON e.id = p.entry_id JOIN gl_accounts a ON a.account = p.account
           WHERE to_char(e.occurred_at, 'YYYY-MM') = %s GROUP BY a.gl_number, p.currency""", (period,)).fetchall()
    totals = defaultdict(int)
    for _, currency, amount in lines:
        totals[currency] += amount
    if any(totals.values()):
        raise ValueError(f"Period {period} does not balance")
    return {"export_id": f"gl-{period}", "lines": lines}

How to check

A test exports a closed month. The export's debits equal its credits in every currency, and each account total equals the ledger total for that month. A second export of the same month has the same ID and the same lines. A posting dated in a closed month is refused.

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/pricing/ledger-and-accounting/general-ledger-export">Agent Wranglers pricing, plans and entitlements playbook</a>. In code you write, add a comment next to it with the same link.