Tech Fundamentals

Migrate to Odoo from Excel/CSV: Import Order, IDs, Traps

Migrate to Odoo from Excel/CSV without duplicates: External IDs, import order, relation columns, date traps and why imports can't be undone.

Long Nguyen Avatar

Long Nguyen

Fullstack Developer · AI Engineer · Researcher

• • 6 min read •

How to migrate to Odoo from Excel or CSV: the short answer

Odoo's import tool accepts .xlsx and .csv files on any business object, including contacts, products, journal entries and orders. The upload is the easy part. A migration succeeds or fails on three decisions you make in the spreadsheet, before you open Odoo:

  1. One sheet per Odoo model. Customers, products and order lines are separate imports with separate files.
  2. A stable External ID on every row. It is what lets you re-import a corrected file without creating duplicates.
  3. Relations by reference, parents first. A product can only point to a category that already exists in Odoo.

Imports are permanent. Odoo's documentation states they cannot be undone, so everything below is about getting the file right on a Test run, not after the data is in.

Version note: Odoo 20 shipped in September 2026 according to Odoo's release notes page. This guide was checked against the 19.0 documentation. The External ID and re-import mechanics are documented the same way as far back as 14.0, but confirm menu labels in your own version with a Test run before trusting them.

Which spreadsheets become which Odoo records?

Map each workbook tab to one Odoo model, then import in dependency order. The rule from Odoo's docs is that the target of a relation must exist before the records that point to it. The table applies that rule to a typical Excel-run business.

Import order Your spreadsheet Becomes in Odoo Why it goes here
1 Customer and supplier companies Contacts (companies) Nothing else depends on them being late; people and orders point to them.
2 Contact people Contacts (individuals) Each row references its company by External ID.
3 Product categories, tags Product categories Products reference a category.
4 Product catalog Products Order lines reference products.
5 Open quotes and orders Sales or purchase orders with lines References customers and products, so it comes last.

A sound rule of thumb is to migrate current state, not full history: active customers, the live catalog, open orders. Years of closed orders rarely justify the cleanup cost, and a read-only export of the old workbook covers the occasional lookup. That is a judgment call, not an Odoo requirement, so adjust it if your auditors or your accountant need history inside the system.

How to prepare Excel data for an Odoo import

Start from a file Odoo already understands. The import screen offers a template for common objects with the column mapping preconfigured. If none exists for your object, Odoo's docs suggest exporting a sample record with the fields you plan to import, so the column names are exactly right. Do not delete the External ID column from a template.

Then strip the habits that make a workbook readable to humans but unreadable to an importer:

Excel habit What to do instead
Merged cells, two-row headers One header row, one record per row, no merged cells.
Totals, notes and blank spacer rows Delete them. Every row is imported as a record.
Dates typed as text, such as 01-03-2016 Use real date cells in .xlsx, or ISO 8601 (2016-03-01) in CSV. Odoo guesses date formats and cannot tell day from month in 01-03-2016.
Prices with currency text, such as ABC 32.000,00 Plain numbers. Odoo accepts symbols like $ or a trailing euro sign, but an unknown currency symbol can crash the import.
Category names with spelling variants Normalize first. Relation columns are matched against existing records by name or ID, so every variant must resolve to one record.
The same customer listed twice Deduplicate in the sheet. Rows without an External ID always create new records.

Choosing the file type: prefer .xlsx when people type the data in Excel. Odoo does not show the CSV Formatting options for Excel files, so there is no delimiter or date-format guesswork, and real date cells stay unambiguous. Use CSV when a script or another system generates the file.

How do External IDs prevent duplicates in an Odoo import?

The External ID is a unique identifier you assign to each imported row. Setting one is not mandatory, but it unlocks the two things a real migration needs: importing the same file several times without duplicates, and linking records to each other. When a file contains an External ID or Database ID column, rows that already exist are updated instead of created.

Three rules decide whether this works:

  • Unique across every object. The ID must not repeat anywhere, so a customer and a product can never both be 1. Prefix it with the source, such as cust_ or prod_.
  • Stable over time. If an ID is changed or removed between imports, Odoo adds a duplicate instead of updating the original.
  • Two records with the same ID conflict. Check for repeats before you upload.

Practical advice that goes beyond the docs: build the ID from a business key you already have, such as the old customer code or the SKU, not from the row number. A drag-down sequence is fine for a one-shot load. But if you will fix errors and re-import, sorting or inserting a row in Excel renumbers everything, and the next import quietly creates duplicates of records that were already loaded.

External ID,Name,Is a Company,Related Company/External ID
cust_0041,Hanoi Steel Fittings JSC,True,
cust_0042,Mai Tran,False,cust_0041

The second row links the person to the company through the company's External ID. The company row must be imported first, which is why the order in the previous section matters.

How to import relations: companies, categories and order lines

Odoo gives you three ways to reference a related record in a column. Use only one per field.

Column style Example Use it when
By name or code Country: Belgium Data was typed by hand and names are unique.
By External ID Country/External ID: base.be Data comes from another system, or names are duplicated.
By Database ID Country/Database ID: 21 Rarely. Mostly a developer tool, since the ID is Odoo's internal number.

Name matching has a trap. If two categories share a child name, such as Misc. Products/Sellable and Other Products/Sellable, Odoo halts validation but may still let you import, and every row then links to the first match. Odoo recommends against importing in that state. Rename one category, or switch the column to External ID.

Many-to-many fields (tags)

Put the values in one cell separated by commas with no spaces, such as Manufacturer,Retailer. In a CSV file the cell must be quoted, because the value itself contains a comma:

External ID,Name,Tags
cust_0041,Hanoi Steel Fittings JSC,"Manufacturer,Retailer"

One-to-many fields (order lines)

Reserve one row per order line. The first line sits on the same row as the order header. Each extra line goes on its own row with every order-level column left empty.

External ID,Customer,Order Lines/Product,Order Lines/Quantity
so_0001,Hanoi Steel Fittings JSC,Hex bolt M8,100
,,Washer M8,200
so_0002,Mai Tran,Hex bolt M8,40

Treat these column names as illustrative. Field labels differ by app and version, so copy the exact headers from Odoo's template or from an export of one existing order.

Step-by-step: import an Excel or CSV file into Odoo

  1. Back up first. Take a database backup, or run the first pass on a copy of the database. There is no undo.
  2. Open the import screen. In the list view of the target object, click the Action (cog) icon and choose Import records. Older versions put this under Favorites.
  3. Load the file. Download the Import Template for that object, or click Upload Data File.
  4. Check Formatting (CSV only). Confirm the separator, text delimiter, date format and thousands separator. These options do not appear for Excel files.
  5. Verify the column mapping. Each File Column should map to the right Odoo Field. Map unrecognized columns by hand.
  6. Click Test, fix, and Test again. Repeat until the test is clean.
  7. Click Import.

Two details save time. First, Odoo guesses each column's field type from the first ten lines of the file, so a column of numbers is only offered integer fields. If the field you need is missing, tick the option to show fields of relation fields (advanced). Second, with developer mode on, the Advanced menu adds Track history during import, which sets up subscriptions and notifications and makes the import slower, and Allow matching with subfields.

Why does my Odoo import fail? Common errors and fixes

Symptom Cause Fix
Whole row shows in one column Delimiter is not a comma. Odoo does not detect tab-separated files. Adjust Formatting for other delimiters, or re-save the file as comma-separated.
Delimiter keeps changing after saving from Excel The spreadsheet app applies the computer's regional settings for separator and delimiter. In locales that use a decimal comma, that typically means semicolons. Re-save from LibreOffice or OpenOffice with Edit filter settings, or set the encoding in Excel's Save As dialog.
Dates land in the wrong month Odoo guessed day and month the wrong way round. Set the Date Format explicitly to ISO 8601, or use real date cells in .xlsx.
Import crashes on a price column Unknown currency symbol, or a negative written as $ (32.000,00). Negatives in parentheses need the symbol inside them: (32000.00 €). Safest is plain numbers.
Needed field is not offered for a column Type guessed from the first ten lines. Show fields of relation fields (advanced) and pick the field manually.
Products all land in one wrong category Duplicate child names matched to the first hit. Rename duplicates or use the Category/External ID column.
Duplicates after the second import External IDs changed or removed between runs. Keep IDs identical across files. Re-export with the import-compatible option to recover them.
Fields you left blank became empty instead of default A column with empty cells writes the empty value. A column that is absent gets the default. Delete columns you do not intend to fill.

Can you undo an Odoo import, and how big can the file be?

You cannot undo an import. Odoo's only built-in help is that you can filter records by creation date or last-modified date to see what an import touched. In practice the real rollback is restoring a backup, which is why the first pass belongs on a database copy.

The documentation sets no row limit. It does warn that large files can cause a time-out or a record that will not process, and its answer is to split the import into smaller batches. Splitting is also a good habit for debugging: a 500-row file with one bad row is easier to fix than a 20,000-row file with the same bad row. If you import many product images, developer mode exposes a maximum batch size and a delay between batches.

How to update existing Odoo records from a spreadsheet

The same tool updates records in bulk. Select the records in list view, choose Export, and tick the option labelled I want to update data (import-compatible export). That limits the field list to importable fields and includes the External ID automatically. Edit the file, then import it through the normal flow.

Keep only the columns you intend to change, and never touch the External ID column. If the ID is altered or removed, Odoo creates a duplicate record instead of updating the existing one.

This round trip is also how you handle a long cutover: keep the spreadsheet as the source of truth until go-live, and re-import corrected files as often as needed. It only works if the IDs stayed stable from the first import.

When Excel/CSV import is not enough

The import tool is built for table-shaped data you can fix by hand. A scripted migration is usually the better tool when any of these apply:

  • The source data needs transformation or validation rules that span several sheets, such as splitting one messy column into attributes.
  • Relation names are ambiguous and you cannot rename the source records.
  • The old system keeps running during a parallel period, so the load must be repeatable and diffable.
  • Volumes are large enough that batching by hand becomes the project.

If your source is a store catalog or a multi-sheet workbook with those problems, Netalith's CSV and Excel data migration service covers the scripted route, including keeping IDs stable for repeat loads.

Excel-to-Odoo migration checklist

  1. One tab per Odoo model, one header row, no merged cells or total rows.
  2. Every row has a prefixed External ID built from a business key.
  3. Relation columns use name only where names are unique, External ID everywhere else.
  4. Dates are real date cells or ISO 8601; numbers are plain.
  5. Files are ordered parents first: companies, people, categories, products, orders.
  6. The first full pass ran on a database copy, with Test clean on every file.
  7. Large files are split into batches, and the backup exists before the live import.

If you would rather have the migration designed, scripted and verified for you, see Netalith's Odoo development and migration service.

FAQ

Frequently asked questions

Can I import an Excel file directly into Odoo?

Yes. Odoo's import tool accepts both .xlsx and .csv files on any business object. Open the list view, click the Action icon, choose Import records, upload the file, check the column mapping, click Test, then Import. CSV files also show Formatting options for separator and date format; Excel files do not.

Do I need an External ID to import data into Odoo?

No, it is not mandatory. But without one you cannot safely re-import a corrected file, because each row creates a new record, and you cannot link records from other files to it. For any real migration, give every row a unique, stable External ID with a prefix such as cust_ or prod_.

Can I undo an Odoo import?

No. Odoo's documentation states imports are permanent. You can filter by creation or last-modified date to see which records an import touched, but the practical rollback is restoring a backup. Run the first full pass on a copy of the database.

In what order should I import data into Odoo?

Import the target of a relation before the records that point to it. A typical order is companies, contact people, product categories, products, then orders with their lines. Each later file references the earlier records by name or External ID.

Why do my Odoo import dates come out wrong?

Odoo guesses the date format from common patterns, and a value like 01-03-2016 is ambiguous between day-first and month-first. In CSV files set the Date Format explicitly using ISO 8601 (YYYY-MM-DD). In Excel files, store dates in real date cells instead of text.

How do I import order lines or multiple tags in one file?

For many-to-many fields such as tags, put the values in one cell separated by commas without spaces, and quote the cell in CSV. For one-to-many fields such as order lines, put the first line on the order's row and each additional line on its own row with the order-level columns left empty.

How large can an Odoo import file be?

Odoo's documentation sets no fixed limit. It warns that very large files can cause a time-out or a record that will not process, and recommends splitting the import into smaller batches.

Stay updated with Netalith

Get coding resources, product updates, and special offers directly in your inbox.