Migrating from QuickBooks to Odoo with a clean cutover
A clean-cutover plan for moving accounting from QuickBooks to Odoo 18 Community, covering accounts, contacts, open items, opening balances and a go-live gate.
On this page
This document describes how Avunu plans a QuickBooks to Odoo 18 Community migration as a clean cutover: you bring across structure, contacts, open items and opening balances, and you leave transaction history behind. Use it when you are scoping a migration or writing the runbook for one.
What this document is based on
The core of the plan comes from the implementation guide of an Odoo 18 system Avunu built for an automobile and heavy-truck repair shop (see Repair Shop Workflow on Odoo). That guide records the decision ("clean cutover") and a six-step accounting runbook. It is a plan, not a record of a finished production migration, and the shop system has not been cut over in production yet.
Where this document goes beyond that guide, it says so. Those parts are general practice and should be checked against your own books.
The approach in one paragraph
You bring across five things: the chart of accounts, partners (customers and vendors), active products, open receivables and payables, and one opening trial balance entry. Everything that happened before the cutover date stays in QuickBooks. You prove the result by comparing an Odoo trial balance to the QuickBooks trial balance on the cutover date, and you do not go live until the two agree.
Decide what stays behind
A clean cutover means you do not import closed invoices, bills, payments or old journal entries. The source plan is explicit that history stays in QuickBooks.
That trade is deliberate. Importing years of transactions multiplies the mapping work and the reconciliation surface, and you rarely look at old transactions outside of audits or questions about a specific customer.
Plan for these consequences before you start:
You still need to look things up in QuickBooks after go-live. Keep it available read-only for as long as your accountant, auditors or tax filings need it. How long that is depends on your situation and is not something the source covers.
Prior-period reports (previous year profit and loss, for example) come from QuickBooks, not Odoo. Export the ones you will need before you lose access.
Open items are the exception to "history stays behind". Unpaid invoices and bills come across as individual documents so that aging is correct (see below).
Prepare the Odoo company first
Set the company up fully before you touch any imported data:
Enter the legal name, address and EIN.
Define the fiscal year and the
date_rangeperiods.Create payment terms and bank journals.
Add state and county sales taxes. The source plan starts with manual tax rates and leaves a tax service for later.
Then apply the chart of accounts, and do that before any account mapping.
Map the chart of accounts
Avunu uses the OCA l10n_us_gaap chart (from the OCA l10n-usa repository). The shop system also installs a companion l10n_us_gaap_mis_report module; the trial balance check later uses an MIS (MIS Builder) report.
The order matters. Apply the chart to the company first, then build the mapping from each QuickBooks account to an Odoo account. If you map first and change the chart later, you redo the mapping.
Expect to adjust the chart for the business rather than forcing the business into the chart. In the repair shop plan, income was split into Parts Sales, Labor Sales, Diagnostic Fees and Sublet, so the sales reports match how the shop thinks about revenue. If you use automated FIFO inventory valuation, confirm the valuation accounts at this step as well.
Tip
Keep the account mapping in a spreadsheet with three columns: QuickBooks account, Odoo account, and a note for anything that merges or splits. You will use it again when you check the opening trial balance.
Plan the import order
The source runbook gives this order, and it works because each step depends on the one before:
QuickBooks chart of accounts, as the account mapping.
Customers.
Vendors.
Active products only.
Open receivables and payables as individual documents, into a dedicated "Opening" journal.
One opening trial balance journal entry for the remaining balances, dated the day before cutover.
Lock dates.
Partners
Customers and vendors both become Odoo partners. Importing them before open items is what lets each open invoice or bill attach to its own partner. Clean duplicates in QuickBooks before the import where you can; it is cheaper than merging later.
Products
Import active products only. Inactive items are history, and history stays behind.
For a repair shop the guide goes further on structure: create each service (diagnostic fee, shop labor, each extra hourly rate, shop supplies, sublet) as its own separate product rather than as variants, and use pricelists for per-customer rate differences. That advice comes from the shop design, not from migration in general, so apply it only if it fits your catalog.
Open receivables and payables
Import each unpaid customer invoice and vendor bill as its own document, not as a lump sum. This preserves aging, so your first aged receivables report in Odoo looks like the one you had in QuickBooks. Post them into a dedicated "Opening" journal so they are easy to find, filter and explain later.
The opening trial balance entry
The remaining balances (everything that is not carried by the imported open items) go in as one opening trial balance journal entry dated the day before the cutover date.
Note
The source says "remaining balances" but does not spell out which accounts that excludes. Our reading, which you should confirm with your accountant, is that receivable and payable control accounts are already carried by the imported documents, so the opening entry must not include them again. Otherwise you double count them.
Inventory is a related trap. The source does not describe how opening stock should arrive. In general, when automated valuation is on, the asset balance should come from stock quantities and cost rather than from a manual journal line, or you will double count. Decide this before the rehearsal and test it there.
Lock dates
Once the opening entries are in and checked, set lock dates so nothing can be posted into the period before cutover by accident.
Use a connector or a script, but rehearse either way
The source plan calls for buying an Odoo 18 QuickBooks connector. It lists these candidates as evaluated: afi_quickbooks_connector, quickbooks_online_odoo_connector and sync_quickbook_connector for QuickBooks Online, and quickbook_desktop_connector for desktop. It does not record which one was chosen or how any of them performed, and we have not verified them here. Evaluate against your own QuickBooks edition and Odoo version.
Whatever tool you use, the rule from the source is firm: dry-run the full import on a staging database first.
Rehearse on a copy
Do the entire cutover, in order, on a staging database that is not production. Treat it as a timed dress rehearsal, not a spot check.
In an odoo-nix development shell, odoo db provision creates a database and installs everything in modules.txt:
odoo db provision <STAGING_DB>That gives you a clean database with the install set, which is a good starting point for a rehearsal. For a production-like rehearsal, restore a copy of your configured target database instead (general practice, not from the source). See Odoo Community and OCA Overview for how the module set is chosen and Odoo Development Environment for the commands.
Things worth learning on the rehearsal and writing down:
How long each import step takes, so you can plan the real cutover window.
Every mapping or data-quality problem, and what you did about it.
Whether the trial balance comparison passes, and what it took to make it pass.
Run the rehearsal more than once if the first pass turns up significant problems. Use a fresh staging database each time so leftover data does not mask a problem.
Reconcile before go-live
Two checks come from the source:
Reconcile the bank using
account_reconcile_oca.Validate an MIS trial balance in Odoo against the QuickBooks trial balance at the cutover date.
The second one is the gate. The source calls that comparison the go/no-go. If the totals differ, you do not go live until you can explain every difference.
A practical way to compare, which is general practice rather than from the source: line up the QuickBooks trial balance and the Odoo trial balance side by side using your account mapping spreadsheet. Check totals first, then account by account. Receivables and payables should also match your aged reports.
Two settings to check before production
Both come from the source runbook:
Check the
account_analytic_requiredpolicy so it does not block the postings your workflows create (sale orders and repair postings in the shop system).Uninstall
mis_builder_demobefore production.
Cutover checklist
Items marked (source) come from the implementation guide. The rest are general practice.
Company set up: legal details, fiscal year, periods, payment terms, bank journals, taxes (source).
l10n_us_gaapchart applied, income accounts split as designed, valuation accounts confirmed (source).Account mapping finished and reviewed by your accountant.
Full import dry-run on staging completed and timed (source).
Trial balance in staging matched to QuickBooks (source).
Agree a cutover date, ideally at a period boundary, and stop entering new transactions in QuickBooks after the final export (general practice).
Take final QuickBooks reports: trial balance, aged receivables, aged payables, and any prior-period reports you will need later (general practice).
Import in order: accounts, customers, vendors, active products, open AR and AP into the Opening journal, then the opening trial balance entry dated the day before cutover (source).
Reconcile the bank with
account_reconcile_oca(source).Compare the MIS trial balance to the QuickBooks trial balance at the cutover date. Go or no-go (source).
Check the analytic policy and uninstall
mis_builder_demo(source).Set lock dates (source).
Keep QuickBooks read-only for reference (general practice).
Warning
Do not run the real import against production until the rehearsal has passed end to end. An import that half-completes in a live accounting database is much harder to clean up than a staging database you can throw away.
Related documents
Sources
This article is in the public domain (CC0 1.0), code samples included. Use it however helps you.