ERP migration
Field-level mapping, validation, and rollback between NetSuite and Odoo ERP. We move data and schema; workflows are rebuilt natively in Odoo ERP.
NetSuite
Source
Odoo ERP
Destination
Compatibility
9 of 12
objects map 1:1 between NetSuite and Odoo ERP.
Complexity
BStandard
Timeline
4-8 weeks
Overview
Moving from NetSuite to Odoo ERP is a structural migration, not a record copy. NetSuite's single-database subsidiary model lets one environment represent multiple legal entities with intercompany eliminations; Odoo uses a multi-company architecture where each legal entity is a separate database pod with shared contacts and products. We resolve that mapping during discovery, flagging whether each NetSuite subsidiary maps to a distinct Odoo company or whether consolidation happens at the reporting layer. Historical journal entries require an explicit carry-forward policy: either replay every historical posting date with original exchange rates, or create fresh opening-balance entries and close the historical periods in NetSuite. NetSuite custom fields (custbody_*, custrecord_*, custentity_*) have no public schema catalog; we run a metadata discovery pass against SuiteQL before building the migration map. Workflows, SuiteFlow automations, and SuiteScript customizations do not migrate as code; we deliver a written inventory for the customer's Odoo partner to rebuild as Studio actions or Python modules. Odoo's Community Edition is open-source and free to run; Enterprise runs approximately $24 per user per month with hosting included, making the five-year cost differential versus NetSuite's tiered subscription significant for mid-market buyers.
Every standard and custom field arrives verified.
AI proposes the map; you confirm before any record moves.
Parent–child, lookups, and ownership stay linked.
Calls, emails, meetings — with original timestamps.
Documents, uploads, and inline notes move with the record.
Why teams make this switch
Leaving
What's pushing teams away
Choosing
What's pulling them in
Object mapping
Each row shows how a NetSuite object lands in Odoo ERP, including any object-level transformations, lookup resolution, or schema-design dependencies.
Typical mapping — final map is confirmed during the sample migration step.
NetSuite
Chart of Accounts (Accounts)
Odoo ERP
Accounting > Chart of Accounts
1:1NetSuite account type codes (0=Bank, 1=AR, 2=Inventory, 5=Fixed Assets, 10=AP, 21=Income, 23=COGS, 24=Expense) map to Odoo Account types. We preserve acctNumber as the account code, account name as the account label, and NetSuite's isInactive flag as a archived state in Odoo. Eliminations accounts in NetSuite's subsidiary model become Odoo inter-company accounts under multi-company configuration.
NetSuite
Customer
Odoo ERP
Contact (with Customer = True)
1:1NetSuite Customers map to Odoo Contacts with the Customer flag enabled. We preserve address fields (billingAddress, shippingAddress), tax registration fields, credit limit, payment terms, and currency settings. NetSuite's custentity_* custom fields on Customer records migrate as Odoo custom fields on res.partner via Studio or Python field definition.
NetSuite
Vendor
Odoo ERP
Contact (with Vendor = True)
1:1NetSuite Vendors map to Odoo Contacts with the Vendor flag enabled, enabling the purchase module to function. We preserve tax ID, bank account details, payment terms, and the vendor-specific address subrecords. Custom fields on Vendor (custentity_*) migrate alongside standard fields as Odoo res.partner custom fields.
NetSuite
Item (Inventory Assembly, Non-Inventory, Service)
Odoo ERP
Product Template + Product Variants
1:1NetSuite inventory items, assemblies, non-inventory items, and service items map to Odoo Product Templates. We preserve the item's SKU (stored in itemId or itemNumber), unit of measure, cost, sales price, product type mapping (inventory item = storable product, assembly = consignment or kit depending on Odoo configuration), BOM relationships on assemblies, and landed cost fields. Lot and serial number settings map to Odoo's lot/serial tracking on stock.move.
NetSuite
Sales Order
Odoo ERP
Sale Order
1:1NetSuite Sales Orders map to Odoo Sale Orders. We preserve order date, customer reference (poNumber), line items with quantities and prices, tax amounts, shipping address, incoterms mapping where present, and order status (Pending Fulfillment maps to Sale Order state 'sale', Shipped to 'done'). Open versus closed status is determined by shipping status in NetSuite at the time of extraction.
NetSuite
Invoice (AR)
Odoo ERP
Account Move (Invoice type)
1:1NetSuite AR invoices map to Odoo Account Moves of type 'out_invoice'. We preserve invoice number, invoice date, due date, line-level amounts with taxes, exchange rates on foreign-currency invoices, and payment terms. NetSuite's custbody_* custom fields on transaction headers migrate as Odoo account.move custom fields. Historical paid invoices are migrated as posted moves; open AR invoices are migrated with state='draft' pending payment reconciliation.
NetSuite
Bill (AP)
Odoo ERP
Account Move (Vendor Bill type)
1:1NetSuite Vendor Bills map to Odoo Account Moves of type 'in_invoice'. We preserve vendor reference, bill date, due date, line-level amounts, taxes, and currency exchange rates. Open AP bills migrate with state='draft' pending payment reconciliation against Odoo's bank statement reconciliation feature. Historical paid bills migrate as posted moves.
NetSuite
Journal Entries (General Ledger)
Odoo ERP
Journal Entries
1:1NetSuite journal entries require an explicit carry-forward policy agreed upon before migration. Option A (historical replay): we preserve original posting dates, line descriptions, amounts, and exchange rates by replaying each journal entry as an Odoo move with the original date and account assignments. Option B (opening balances): we extract period-end GL balances from NetSuite and create fresh Odoo opening-balance entries as of the cutover date, carrying forward net balances rather than historical detail. We present both options to the customer's finance team and capture the choice in the migration contract before sequencing data.
NetSuite
Open AR Aging
Odoo ERP
Open AR Aging Report / Rescheduled Move Lines
lossyNetSuite open receivables are extracted by aging bucket (0-30, 31-60, 61-90, 90+) with invoice numbers, amounts, due dates, and customer references. We create a reconciliation view in Odoo showing open AR as aged move lines linked to the migrated contacts. Payments received after cutover are reconciled manually in Odoo's bank statement reconciliation view.
NetSuite
Open AP Aging
Odoo ERP
Open AP Aging Report / Rescheduled Move Lines
lossyNetSuite open payables are extracted similarly to AR aging, with vendor, bill number, amount, due date, and aging bucket. We create a reconciliation view in Odoo showing open AP as aged move lines linked to migrated vendor contacts. NetSuite payments made before cutover are reconciled against the migrated bills.
NetSuite
Subsidiary
Odoo ERP
Multi-Company Configuration / Separate Company Pods
many:1NetSuite's subsidiary model allows one database to represent multiple legal entities with intercompany eliminations at the subsidiary level. Odoo handles multi-entity differently: each legal entity is a separate company database pod under a single Odoo instance with multi-company rules for shared contacts and products. Each NetSuite subsidiary must map to a distinct Odoo company with its own Chart of Accounts, fiscal year, and posting journal. Intercompany transactions in NetSuite become cross-company journal entries in Odoo under the multi-company configuration. We build the subsidiary-to-company map during discovery and validate journal configuration before any data lands.
NetSuite
Custom Record Types (custrecord_*)
Odoo ERP
Custom Models via Studio or Python
1:1NetSuite Custom Records are user-defined record types accessed via custrecord_* script IDs. Every NetSuite environment has a unique set of custom record types not discoverable via public documentation. We run a metadata discovery pass against the account's SuiteQL endpoint to enumerate all active custom objects and fields before building the migration map. Custom fields on transactions (custcol_*) and entities (custentity_*) require special handling in the NetSuite REST/SOAP payload. Destination Odoo custom models are created via Studio or Python model definition matching the source schema, including field types, required flags, and relational fields. Custom Record migration runs last because these records often reference Customers, Vendors, and Items that must be present first.
| NetSuite | Odoo ERP | Compatibility | |
|---|---|---|---|
| Chart of Accounts (Accounts) | Accounting > Chart of Accounts1:1 | Fully supported | |
| Customer | Contact (with Customer = True)1:1 | Fully supported | |
| Vendor | Contact (with Vendor = True)1:1 | Fully supported | |
| Item (Inventory Assembly, Non-Inventory, Service) | Product Template + Product Variants1:1 | Fully supported | |
| Sales Order | Sale Order1:1 | Fully supported | |
| Invoice (AR) | Account Move (Invoice type)1:1 | Fully supported | |
| Bill (AP) | Account Move (Vendor Bill type)1:1 | Fully supported | |
| Journal Entries (General Ledger) | Journal Entries1:1 | Fully supported | |
| Open AR Aging | Open AR Aging Report / Rescheduled Move Lineslossy | Fully supported | |
| Open AP Aging | Open AP Aging Report / Rescheduled Move Lineslossy | Fully supported | |
| Subsidiary | Multi-Company Configuration / Separate Company Podsmany:1 | Fully supported | |
| Custom Record Types (custrecord_*) | Custom Models via Studio or Python1:1 | Fully supported |
Gotchas + challenges
Platform-specific issues from each side, plus the pair-specific challenges that don't show up on either platform's page on its own.
NetSuite gotchas
API concurrency limits gate extraction throughput
Subsidiary-to-company mapping is structural, not cosmetic
Service tier transaction-line limits block migration mid-flight
Custom records and custom fields have no standard schema
Historical journal entries require strict sequencing
Odoo ERP gotchas
No rollback for CSV imports
External ID conflicts on re-import
Many2many field encoding in CSV imports
Large export timeouts require batching
Version schema drift between Odoo releases
Pair-specific challenges
Migration approach
Discovery and custom field registry enumeration
We audit the source NetSuite account across service tier (Standard, Premium, Enterprise, Ultimate), subsidiary count and hierarchy, transaction volume by type, custom record type count, custom field registry via SuiteQL, and open AR/AP aging balances. We extract a representative sample of each transaction type to validate field coverage before building the full ETL map. The discovery output is a written migration scope, subsidiary-to-company map, journal carry-forward policy recommendation, and an Odoo edition recommendation (Community free self-hosted vs Enterprise hosted).
Staging environment setup and custom field schema creation
We provision a staging Odoo environment matching the production configuration. We create the Chart of Accounts structure, multi-company configuration for each NetSuite subsidiary, and all custom field definitions (via Odoo Studio or Python model definition) matching the discovered NetSuite custom field registry. The Odoo staging environment runs in parallel with the production NetSuite account during UAT so that the customer can validate data accuracy before production cutover.
Data quality audit and cleansing
We extract representative samples from NetSuite across each object type and surface data quality issues in writing: duplicate customer and vendor records, inconsistent item pricing, misaligned tax codes, missing required fields on transactions, and any records with invalid foreign currency exchange rates. We do not silently clean data. Every cleansing decision is documented and presented to the customer's admin for approval before ETL scripts are finalized.
ETL script development and sandbox rehearsal
We build ETL scripts with automated reconciliation: for each object, we generate control totals (record count, sum of line amounts, sum of balances) and validate them against NetSuite exports before any Odoo import. Scripts handle the subsidiary-to-company mapping, currency exchange rate resolution using NetSuite's exchange rate table, and parent-record lookup resolution (e.g., customer must exist in Odoo before a sale order referencing it is imported). We run a first full rehearsal migration into staging, reconcile all control totals, and fix issues in code before proceeding.
User acceptance testing in staging
The customer's finance and operations leads validate migrated records in the staging Odoo environment. They spot-check 25-50 records per object against the NetSuite source, verify that open AR and AP aging matches the source report, confirm that journal carry-forward matches the agreed policy, and sign off the schema and mapping before production migration begins. Any corrections discovered during UAT are made in the ETL scripts and re-reconciled in staging.
Production migration and cutover
We freeze NetSuite writes during the cutover window, run a delta migration for any records modified during the migration process, then load into production Odoo in dependency order: Chart of Accounts, Contacts (Customers then Vendors), Products, Open AR/AP aging entries, Sale Orders, Vendor Bills, Journal Entries, then Custom Records last. Each phase emits a row-count reconciliation report. We enable Odoo as the system of record and maintain a four-to-eight-week read-only window on NetSuite in case discrepancies surface during hypercare. We deliver the SuiteFlow and custom SuiteScript inventory document to the customer's Odoo partner for Studio and Python workflow rebuild.
Platform deep dives
NetSuite
Source
Strengths
Weaknesses
Odoo ERP
Destination
Strengths
Weaknesses
Complexity grading
Standard ERP migration. 1 of 8 objects need a mapping; the rest are 1:1.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across NetSuite and Odoo ERP.
Object compatibility
1 of 8 objects need a mapping; the rest are 1:1.
Field mapping clarity
Field mapping is derived from defaults — final spec confirmed during the sample migration.
Timeline complexity
8-object category — typical timelines run 2–7 days end-to-end.
API constraints
NetSuite: 15 concurrent requests (default); up to 55 on Tier 5 with SuiteCloud Plus; 1,000 records per request; 60-second and 24-hour frequency windows per account.
Data volume sensitivity
NetSuite doesn't expose a bulk API — REST + parallelization used for high-volume runs.
Estimator
Rule-based pricing — no per-record fees, no manual quotes. Migrations over 2M records are scoped individually.
Step 1
Pick a category, then your source and destination platforms.
Category
FAQ
Answers to the questions buyers ask most during NetSuite to Odoo ERP migration scoping. Not seeing yours? Book a call.
Walk through your NetSuite to Odoo ERP migration with a real engineer — 30 minutes, free, written quote within 24 hours.
Book a free 30 minute consultationAdjacent paths
Other ways to leave NetSuite
Other ways to arrive at Odoo ERP
Ready when you are
Tell us record counts and timeline. We'll come back with a written quote inside 1 business day — no commitment, no sales pitch.