Skip to Content
Skip to content
Data migration

Data Migration Playbook (legacy system → Odoo)

Odoo data migration playbook: what to migrate, import order, external IDs, opening stock and balances, open AR/AP and validation.

1. What to migrate

Data Migrate? How
Chart of accounts Map to the localisation CoA, add missing accounts Import / manual
Customers, vendors, contacts Yes (active only, deduplicated) CSV/XLSX import
Products, categories, UoM, variants, barcodes Yes Import (variants: import attribute values; v19+ "import product variants")
Vendor price lists, customer pricelists Yes Import
BoMs, work centers, routings Yes (manufacturers) Import BoM + lines
Opening stock (qty, location, lot, cost) Yes Inventory adjustment import, with unit cost
Opening trial balance Yes One opening journal entry (or Accounting > Opening balances)
Open AR/AP (unpaid invoices/bills) Yes, one line per open document Import as invoices/bills against an opening clearing account
Open SOs / POs Yes (not fully delivered/received) Import or re-key
Employees, contracts, leave balances Yes Import + allocation
Historical closed transactions Usually no: keep the old system read-only or import summaries —
Fixed assets Yes, with accumulated depreciation Import assets with "already depreciated" amount

2. Steps

  1. Extract from the old system (Tally: masters + ledgers + stock summary; QuickBooks: lists + aged reports).
  2. Clean in a spreadsheet: remove duplicates, fix GSTIN/VAT format, unify units, give every product an Internal Reference.
  3. Map old codes → Odoo fields. Download Odoo's import template (Favorites > Import records > template), or export one record to see the columns.
  4. Use External IDs (id column, e.g., legacy_cust_0012). Re-imports then update instead of duplicating, and relations (Many2one) can reference them (parent_id/id).
  5. Trial import into a duplicate/test database → check counts and totals.
  6. Reconcile: stock value in Odoo = old stock report; AR total = old aged receivables; trial balance matches.
  7. Repeat the trial migration at least twice; time it, because the real cut-over has a time window.
  8. Final migration on go-live day.

3. Import order (dependencies)

1 Currencies / companies / users
2 Chart of accounts adjustments, taxes, fiscal positions, journals, payment terms
3 Product categories (+ accounts & costing) → UoM → attributes
4 Contacts (companies first, then child contacts with parent_id/id)
5 Products / variants / vendor pricelists / customer pricelists
6 Warehouses & locations → lots/serials
7 BoMs, work centers
8 Opening stock (inventory adjustment with cost)
9 Opening balances JE; open invoices & bills; open SO/PO
10 Employees, allocations

4. Opening stock and accounting: avoid double-counting

Odoo 18 (perpetual / automated valuation): the opening inventory adjustment posts Dr Stock Valuation / Cr Inventory adjustment (stock variation). Your opening trial balance must then not debit the stock account again. Either:

  • import the TB without the stock line and let the adjustment create it, then clear the counterpart to equity/opening-balance account; or
  • import the full TB, and set the adjustment counterpart account = opening balance / suspense account so it nets to zero.

Odoo 19 / 20: valuation layers are gone. Stock moves don't post entries; the inventory closing entry (Accounting > Review > Inventory Valuation) brings the stock account in line. Import the TB with the stock balance and run the closing for the opening date. Check that the Stock Variation balance is explained.

5. Open receivables and payables

  • Import each unpaid invoice as a customer invoice dated with the original date, using a product/account "Opening AR" that books to an opening clearing account (not revenue).
  • The total of the clearing account must equal the AR line in the opening trial balance. After posting both, the clearing account is zero.
  • Do the same for vendor bills (AP).

6. Validation checklist (sign-off by the customer's accountant)

  • [ ] Counts: customers, vendors, products, BoMs match the source
  • [ ] Stock quantity per product/location/lot = physical count
  • [ ] Stock value = old valuation report (± rounding)
  • [ ] Trial balance in Odoo at opening date = old system's closing TB
  • [ ] Aged receivable and aged payable totals match
  • [ ] 10 random products checked (price, cost, taxes, routes, accounts)
  • [ ] 10 random customers checked (pricelist, payment term, fiscal position, GSTIN)

Need an Odoo expert on your project?

Implementation, migration, customisation or team training: tell me what you need and I will reply within 24 hours.