Skip to content
← Free FDE training guides

Free learning / 50-minute financial ledger proof lab

Double-entry posting: balanced does not mean correct

Execute balanced wrong-account, wrong-party and duplicate postings in SQLite. Compare partial writes with atomic rollback, then retain an original journal through reversal and correction.

No account needed. Use your own copy for writing and keep the original as a reference.

Engineering lab library / Run locally

Choose your next agent lab.

Start with the durable workflow reference, then choose the boundary you want to test. Open its walkthrough for setup, commands and expected observations.

Synthetic records. No provider key or model inference. Runs locally with the standard library.

  1. Start here / Python 3.11+ · Standard library

    Double-entry meaning, atomic posting and correction history

    Expose balanced business errors, preserve incomplete-collection state after rollback and trace an original journal through correction.

    Python 3.11+ · Standard library · In-memory SQLite · Fictional USD minor units · No network

Original LockedIn Labs workshop · 50 minutes. Python 3.11+ standard library. Follow rows, sums and a SQL transaction first. The program creates a fresh in-memory SQLite ledger for each fictional case and closes it afterward. No network, bank, payment adapter, account credentials or persistent database is used. All amounts are integer USD minor units; 12500 means USD 125.00.

You will distinguish bookkeeping vocabulary, balance, business meaning, transaction completeness, duplicate prevention and explicit correction history. The financial reconciliation guide compares case and credit extracts. This workshop inspects the posting beneath those extracts; it does not implement a payment system or determine real policy.

1. Learn the accounting words before choosing fields

A journal groups the entry lines that record an event. A ledger organizes postings by account. Debit and credit name opposite accounting sides; they do not universally mean loss and gain. In double-entry bookkeeping, posted debit totals and credit totals must agree. IRS Publication 583 provides an introductory primary reference for journals, ledgers and equal sides. The exercise supplies its own fictional chart and posting instruction, rather than deriving a real accounting policy from that general rule.

Our chart is deliberately small:

AccountFictional meaningHow this fixture records it
cashBook cash assetDebit increases its net debit; credit decreases it
receivableAmount owed on a booked invoiceDebit increases it; a collection credits the matching party
revenueAlready-booked service revenueCredit creates a negative net-debit value in this report

A trial balance compares account debit and credit totals. Our trial_net_debit_minor is their signed difference: positive amounts are net debits and negative amounts are net credits. A zero difference is an arithmetic check, not evidence that the account, party or source event was right.

A subsidiary ledger preserves detail beneath a control account, such as receivables by customer. The fixture represents this as separate A and B buckets. It is not a full customer ledger, chart of accounts or reporting standard. A credit balance in B's bucket is recorded explicitly; the program does not silently relabel it as a valid settlement of A's invoice.

2. State the collection target independently

Every case starts with a supplied already-booked invoice for party A:

Text example
Opening journal INVOICE-A:
  Debit receivable A 12500
  Credit revenue     12500

Independent supplied instruction PAY-1:
  Collect that existing invoice once for A.

Expected final net-debit buckets:
  cash:          12500
  receivable_A:      0
  receivable_B:      0
  revenue:      -12500

The opening invoice is a fixture assumption. This lesson does not decide when a real service may recognize revenue, whether a payment has settled, or which business unit owns a source. Cash here is a ledger account, not a read of a bank balance. A collection should reduce this existing receivable rather than book that revenue a second time under the supplied instruction.

Read instruction and expected_net_debit_minor separately from actual rows. The expectation is not derived from a successful insert or a zero trial sum. The six visual frames explain eight independently reset SQL cases; the browser does not execute the ledger or move money.

UNDERSTAND THE SYSTEM

Balanced entries can still describe the wrong business event

Six authored frames explain eight actual in-memory SQLite cases. Default amounts are fictional USD minor units: 12500 means USD 125.00. The diagram does not execute SQL or move money; journal balance and the independent collection target are separate observations.

FOLLOW THE WORK

Watch one example, step by step.

6 authored stepsYou control the pace
01 / EXPLORE THE PARTSControls loading · written explanations below
Direction of information or workReturn / feedback

WHAT THIS PART DOES

Collection instruction

The supplied fixture asks for one collection of party A's already-booked invoice. It fixes the expected cash, receivable and revenue effects independently of whether an insert or balance check succeeds.

CONNECTED TO
  • Bind intent: Journal identity
Read the full worked example
  1. 01
    1 · Equal sides do not choose the account

    The wrong-account receipt debits cash and credits revenue instead of the existing receivable. Every journal balances and the trial net is zero. Yet A still has a 12500 receivable and revenue is credited twice across the opening invoice and receipt. The independent collection target fails.

    ACTUAL WRONG-ACCOUNT ROWS: Trial net 0 · A receivable 12500 · target mismatch

  2. 02
    2 · An aggregate can hide the wrong party

    Wrong-party debits cash and credits B's receivable. The aggregate receivable reaches zero, but A retains a debit 12500 and B a credit 12500. Keep those buckets separate. Neither the overall trial balance nor matching account totals can explain away the party mismatch.

    ACTUAL RECEIVABLE DETAIL: Aggregate receivable 0 · A +12500 · B −12500

  3. 03
    3 · A duplicate can balance twice

    Duplicate-naive inserts two balanced PAY-1 receipts, leaving cash 25000 and A at credit 12500. The separately reset guarded case stores one exact operation receipt: identical delivery replays its journal, while changing the party under the same key conflicts. Balance, operation identity and intended effect are separate checks.

    SEPARATELY RESET CASES: Naive cash 25000 · guarded cash 12500

  4. 04
    4 · Rollback protects integrity, not completion

    Partial-naive commits the journal and first debit separately, then the authored exception leaves trial net 12500. Partial-atomic rolls the same incomplete attempt back. Its only remaining journal is the opening invoice, so trial net is zero while A is still owed and no collection is posted.

    ACTUAL TRANSACTION COMPARISON: Atomic: receipt absent · A still owes 12500

  5. 05
    5 · Correct the book while keeping its history

    Reversal first posts the wrong-account receipt. A new journal references that original and inserts its inverse lines; the intermediate balances return to the opening state. A separate correction then posts the intended collection. All four journals remain, and final buckets match the supplied target. No real cash is refunded or collected by these rows.

    ACTUAL RETAINED HISTORY: Original + reversal + correction · final target matches

  6. 06
    6 · Receive the business contract, not a green sum

    The receiver locates the original instruction, exact journal/entry rows, operation receipt and any reversal reference. The fixture target is independent of the balance calculation. The receiving team still needs authoritative source, account/party mapping, access, policy and installed-adapter evidence before any real financial operation.

    RECEIVING BOUNDARY: Local equality · explicit unresolved external evidence

Read the connections in plain English
  1. 01
    Collection instruction → Journal identity

    Retain the source and operation identities rather than treating matching amounts as sufficient.

  2. 02
    Journal identity → Debit / credit lines

    The normal writer validates balance before committing its complete transaction.

  3. 03
    Debit / credit lines → General-ledger totals

    Equal debits and credits establish only the local arithmetic relationship.

  4. 04
    Debit / credit lines → Receivable detail

    Do not discard the receivable party while rolling up account totals.

  5. 05
    General-ledger totals → Receiving decision

    A zero trial difference cannot approve the posting by itself.

  6. 06
    Receivable detail → Receiving decision

    Inspect the independent target and actual party/account effects.

  7. 07
    Journal identity → Receiving decision

    A reversal references a retained original; correcting the book is not a bank operation.

Check your understanding: Does a zero trial balance prove that the collection is correct?

No. Wrong-account, wrong-party and duplicate-naive all have zero trial difference while failing the supplied collection target. Partial-atomic also has zero difference with no collection posted. Compare the independent instruction and exact account/party effects, and keep real source or settlement evidence separate.

3. Run eight actual posting cases

Download and read the runner. Keep an untouched reference. Predict the trial difference and whether the independent collection target matches before reading JSON output:

Code example
python3 --version
python3 fde-ledger-posting.py > ledger-12500.json
python3 fde-ledger-posting.py --amount-minor 12501 > ledger-12501.json

The changed amount is one minor unit larger. Accepted amounts are integers from 1 through 1000000; the fixture makes no currency conversion or rounding decision. Use the money-contract workshop for units, currency identity, precision and explicit rounding questions.

CaseActual trial net at default amountWhat remains wrong or unresolved
correct0Local buckets match the supplied target; external source is untested
wrong-account0A still owes 12500; revenue has been credited twice
wrong-party0A still owes 12500; B has a credit 12500 despite aggregate receivables zero
duplicate-naive0Cash is 25000 and A has a credit 12500 after two balanced receipts
duplicate-guarded0One receipt; identical replay returns it and changed-party payload conflicts
partial-naive12500First debit remains committed without its credit
partial-atomic0Only the opening invoice remains; the collection is still unposted
reversal0Original, inverse and corrected history remain; no real payment operation occurred

A successful process exit only means the report was produced. Inspect post_results, journals, entries, actual_net_debit_minor and business_differences. An intentionally bad case can exit successfully while reporting a false collection match. That is counterexample evidence, not a pass for a learner or a real financial operation.

4. Find the balanced error in the actual rows

Use this written ledger alongside your original prediction:

Text example
wrong-account:
  receipt: debit cash 12500 / credit revenue 12500
  every_journal_balanced: true
  trial_net_debit_minor: 0
  receivable_A: 12500        # not cleared
  revenue: -25000           # opening invoice plus wrong credit

wrong-party:
  receipt: debit cash 12500 / credit receivable B 12500
  receivable_A:  12500
  receivable_B: -12500
  aggregate receivables: 0  # conceals the party mismatch

partial-atomic:
  post result: rolled_back / injected_after_first_line
  remaining journals: opening invoice only
  trial_net_debit_minor: 0
  cash: 0 / receivable_A: 12500
  matches_supplied_collection_target: false

Wrong-party demonstrates why a general-ledger account total can agree while its underlying business allocation is wrong. Do not drop party identity when building a reconciliation extract. Wrong-account demonstrates why equal amounts and equal sides cannot choose the correct classification. These are distinct gaps for the responsible source and accounting owners.

In the atomic partial case, rollback preserves the opening state. It does not complete the collection or authorize another attempt. The naive partial case uses SQLite autocommit for individual inserts, then deliberately raises after the first entry. The atomic case instead runs BEGIN IMMEDIATE, inserts the journal/lines and rolls the same exception back. This is a controlled local exception and real SQL rollback, not a process kill, concurrent-worker test or unknown remote commit outcome.

5. Preserve operation identity and correction history

The transactional writer checks at least two entry lines and a zero sum before committing. Its receipt binds an operation key to the exact normalized posting payload and journal ID. In duplicate-guarded, the outcomes are:

Text example
first PAY-1:        applied  -> journal 2
identical PAY-1:    replayed -> journal 2
PAY-1 changed to B: conflict -> journal 2 unchanged

The payload retains source reference, journal kind, ordered lines, account, party, side, amount and any reversal reference. The operation receipt and posting commit together. A key alone is not enough to accept a different intent. The naive comparison deliberately skips this receipt boundary while still checking balance, so two balanced postings can represent duplicate work. No source reference or operation label authenticates a caller.

For reversal, inspect all three snapshots: the initial wrong posting, the state after its inverse, and the final correction. Journal 2 remains unchanged. Journal 3 points to journal 2 with reverses_journal_id and swaps each original side. That intermediate state returns to the opening invoice balances. Journal 4 separately records the intended collection with a new operation key and the same source reference. All four journals, including the opening invoice, remain.

This writer preserves history through inserts; the database is not a tamper-proof or immutable ledger. The fixture permits at most one reversing journal for an original and implements no partial-reversal workflow or production approval. Correcting these book entries does not refund, collect or undo real cash at another service. An authorized workflow owner must define those separate actions.

Read the small schema and post. Account names, sides, positive integer amounts and receivable parties have database checks. Journal balance and exact receipt binding are enforced by this writer inside its transaction. Direct privileged SQL can bypass application rules. This one-connection in-memory exercise tests no production permissions, concurrent posting, durable recovery, currencies, period close, accounting standard, tax rule or real payment rail. Use your organization's reviewed ledger and posting contract rather than this fixture.

6. Receive the posting as a financial-services pod

Rotate three roles across a changed run:

  • Builder: retain original command, runtime, source instruction, exact journal and entry IDs, post status and account/party residuals. Locate the transaction.
  • Receiver: change the amount by one minor unit without author takeover. Reproduce one balanced wrong result and the atomic still-unposted result. Trace a reversal without deleting the original evidence.
  • Source/accounting and application owners: agree the source identity, account and party mapping, operation scope, correction authority and real receiving-adapter evidence. Retain any unresolved business action explicitly.

Each participant keeps an individual record:

Text example
Learner / run / exact command / Python and SQLite versions:
Prediction before execution:
Source instruction / currency / amount unit:
Actual journal and entry IDs / operation receipt:
Debit and credit totals / actual party-level buckets:
Independent expected buckets / specific residual:
My contribution / assistance received:
Changed amount / prediction / observed result:
Receiving action actually attempted / assistance:
Remaining action / accountable owner / next bounded source or adapter check:

For a real receiving exercise, use an approved synthetic adapter and agreed posting contract. Ask an accounting owner to explain one mismatched classification or customer residual. Then identify the exact external source evidence still needed before calling a payment complete. Neither local trial equality nor the fictional target match supplies that evidence.

7. Defend the boundary in an interview

Explain: Why can two balanced journal entries still be duplicate work? Use the naive PAY-1 receipts and cash 25000 beside the original 12500 target.

Debug: Why does aggregate receivables zero fail to settle A? Locate both party buckets and the actual credit line, rather than proposing a new tolerance.

Design: Describe the independent posting instruction, atomic validation, scoped operation identity, original/correction lineage, source authority and receiving test. Distinguish book correction from an external compensation.

Primary references checked 28 October 2026: IRS · Bookkeeping systems, Python SQLite transactions, and SQLite transactions. The chart, synthetic invoice, posting cases, visual frames and receiving exercise are original instruction, not an accounting policy or tax recommendation.

Before you move on

Keep the evidence of your work.

  • Independent collection targets beside actual account and party buckets.
  • Exact journals, entry lines, operation receipts and retained correction history.
  • An individual receiving record with source/accounting and application responsibilities.

Keep these in your own working copy. Reading this page does not save work or record completion.

Suggested next free session

Financial reconciliation: when matching amounts are not enough

Reconcile the posting with business-case meaning and complete source evidence. This is a suggested sequence; choose the practice that addresses your current gap.

Open the next free guide

Free account

Give your practice a home.

Try a role exercise with account drafts and a downloadable worksheet. Keep the files you made in this guide in your own working copy.

Create a free learning account

Guided training · by application

Build toward a program.

Request structured lessons and a training seat. Scope, schedule, feedback arrangements and tuition are confirmed in writing before you commit.

Explore a training application
Have Academy access?

Use your existing training seat to open worked explanations and practice in the related lesson. Creating a free account does not grant a seat.

Open the related Academy lesson →
Back to top ↑