← DArnTech services
darnbooks · QuickBooks Desktop → ERPNext · migration proof · 2026-08-21
The Penny-Exact Proof
A complete company — 1,191 transactions over two fiscal years — was placed into real QuickBooks Desktop, extracted through QuickBooks' own API, and loaded into ERPNext. At every step, an independent checker compared the books. They match to the penny, everywhere.
TRIAL BALANCE337,670.12Dr = Cr, all four systems
1,191 transactions
1,828 line items
386 payments w/ applications
44 ledger accounts
0 differences at the gate
Scope, stated plainly: the company is synthetic — generated data built to exercise real migration hazards (partial payments, credit memos, owner draws, sales tax, near-duplicate vendor names). No client data was used, and this page claims exactly what it shows: one company, moved once, verified three ways. Payroll is out of scope by design — it routes to a payroll service, and the books receive the journal entry.
How the proof is built
One rule makes the whole thing checkable: every system must describe the books in the same neutral format — a normalized JSON schema where money is a decimal string, never a float. The generator writes it, the QuickBooks extractor writes it, the ERPNext reader writes it. The parity checker only ever compares two of those files; it doesn't know or care where they came from.
The three checks are independent: check 1 proves QuickBooks holds the intended books, check 2 proves the extracted transactions alone are sufficient to rebuild the trial balance, and check 3 proves ERPNext ends up holding the same books it was given. Any single-cent error anywhere fails a gate — that failure mode is itself tested.
The three checks, with their actual output
CHECK 1 · PASS
QuickBooks holds the intended books
The generator's canonical file says what the books should be. After importing into a fresh QuickBooks company and extracting back out over qbXML, the two snapshots are compared: trial balance per account, AR and AP agings per customer and vendor, and every entity count.
$ python3 tools/parity_check.py --named-deltas artifacts/seed42/expected-deltas-qbd.json \
artifacts/extract-20260821T183504Z.json artifacts/seed42/synthetic-company.json
~ named delta (accepted): counts.accounts: A=44 vs B=37 # QBD pre-creates 7 accounts
~ named delta (accepted): counts.terms: A=7 vs B=3 # QBD pre-creates standard terms
RESULT: PASS - snapshots match on all compared fields (2 named delta(s) accepted).
The two "named deltas" are QuickBooks' own furniture — accounts and payment terms it creates in every new company. They are declared in a file, matched exactly, and the check fails if they don't appear. Nothing is waved through silently.
CHECK 2 · PENNY-EXACT
The extraction is complete enough to rebuild the books
This is the strongest evidence that nothing was lost in extraction: taking only the extracted transactions and re-deriving every posting from first principles (debits, credits, tax lines, payment applications, fiscal-year close), the rebuilt trial balance equals QuickBooks' own report on every account.
$ python3 tools/recompute_tb.py artifacts/extract-20260821T183504Z.json
QBD TB total : 337670.12 Dr / 337670.12 Cr
recomputed TB total: 337670.12 Dr / 337670.12 Cr
accounts compared 36: matched 36, mismatched 0 (0 unexplained by declared gaps)
RESULT: PENNY-EXACT — recomputed TB matches QuickBooks on every account.
CHECK 3 · PASS, ZERO DELTAS
ERPNext ends up with the same books
The extract is loaded into a fresh ERPNext company over REST — chart of accounts from the extraction (never a template), every document tagged back to its QuickBooks transaction ID. A separate reader pulls the books out of ERPNext as the same normalized JSON, and the checker compares it against the extract. The reader also fails loudly if any untagged ledger entry exists — nothing may post outside the migration.
$ python3 tools/parity_check.py erpnext-snapshot-extract.json artifacts/extract-20260821T183504Z.json
Compared: trialBalance, arAging, apAging, counts
RESULT: PASS - snapshots match on all compared fields.
Same books, two systems
The trial balance as each system reports it, captured live from the running installs.
QUICKBOOKS DESKTOP ENTERPRISE 24 · Trial Balance, all dates
ERPNEXT v15 · Trial Balance, fiscal 2026 view of the migrated company
The two systems present the report differently — ERPNext shows opening/period/closing per fiscal year, QuickBooks a point-in-time balance — but the closing figures are the same numbers, cent for cent. The row-by-row, full-window comparison is what the checker does mechanically in Check 3.
Why you can trust the checker
- It can fail, and that's tested. The checker's self-test perturbs a single cent in one account and must catch it — a checker that can't fail proves nothing.
- Money is never floating-point. Amounts travel as decimal strings end to end; the checker rejects any JSON number with a fractional part as a contract violation.
- Known differences are declared, not ignored. Accepted deltas live in a committed file, must match character-for-character, and must actually occur — a stale allowlist fails the run.
- The raw material is archived. Every qbXML request and response — the actual XML QuickBooks produced — is committed to the repository. The extraction can be audited or replayed without QuickBooks installed.
- The input is reproducible. The synthetic company generates byte-identically from seed 42; anyone with the repository can regenerate it and re-run every gate.
Chain of evidence
Every check above writes a file: the canonical books, the raw QuickBooks XML, each comparison report. They live in a private repository. Read the artifacts with me on a call, and I'll re-run any gate you pick.
What the proof surfaced along the way
The value of doing this against real QuickBooks rather than on paper: the live install pushed back, and each finding is now encoded in the tools.
- QuickBooks' bulk import format (IIF) silently cannot carry bill payments at all — payments travel over the API with explicit invoice/bill applications, which is also what preserves aging reports.
- Fresh QuickBooks companies pre-create accounts and terms that collide with imported charts; the collision surface was mapped by extraction and is now declared, not discovered.
- QuickBooks ages credit memos by due date derived from customer terms — three tools had to agree on that truth before agings matched.
- Items imported via IIF come out one-sided; two-sided income/expense items require a UI conversion QuickBooks refuses over the API.
Sage 50, held to the same penny
Sage stopped selling Sage 50 licenses you keep on January 1, 2023, so its owners now rent it on Sage's schedule. We wrote the same 1,191-transaction company into Sage 50's documented export format: nine CSV files, laid out the way Sage's own documentation specifies. A separate reader rebuilt the whole books from those files alone. They match to the penny.
To show the gate can fail, we changed one cent on one row of the sales file — an 890.00 line became 889.99. The checker found seven differences, including a paid-up customer who suddenly showed a one-cent credit.
- The run is deterministic. Generate the company twice and you get byte-identical files; read them twice and you get byte-identical books.
- Into ERPNext and back out. All 1,191 transactions went into a fresh ERPNext company and came back out matching to the cent, with one difference named before the run.
Not proven yet: we haven't exported from a live Sage 50 install and run that file through. Until we do, that step is mapped, not proven.
Have books to move?
This proof is the method we bring to your company file. Scope depends on your books, so it starts with a conversation, not a quote form.
Book a 30-min call
All services →