Migrate from spreadsheets to Odoo: what the move involves
Moving from spreadsheets or an older accounting package to Odoo is a planned sequence of master data loads, opening entries, a test database and a parallel run. The effort is set mainly by two things: how clean your source data is, and how many processes are in scope at cutover. Spreadsheets that carry duplicate customers, inconsistent product codes or open items with no clear owner take longer to fix than the import itself. DeployOpen scopes this work in writing from your own files and system exports, so the plan reflects your actual records. This section does not give durations or prices. Those follow from the written scope.
Decide the edition and hosting model before any data is mapped, because the answer changes what you can load and where it runs. Odoo's Community edition is open source under LGPL-3.0 and runs on infrastructure you operate. The Enterprise edition adds features and support, and its source code is available only through a subscription or partner access. According to Odoo's pricing page, only the Custom plan can be hosted on Odoo.sh or on-premise, and only Custom includes multi-company, Odoo Studio, external API access and custom modules. If your books span several companies or a feed from another system depends on the API, confirm those points against the current plan terms before you design the migration. The upgrade service also requires an Enterprise subscription for on-premise use, which affects the upgrade plan that follows go-live.
Load master data first, in an order that respects dependencies. The chart of accounts, tax settings and fiscal configuration come before partners, products and journals, because later records point at them. Customers and vendors follow, then products, then open invoices and bills. Use External IDs for every related record so that a bill can reference its vendor and a payment can reference its invoice without depending on internal database IDs, which differ between environments. Before you prepare any files, confirm the import tools and file templates available in your Odoo version in the current documentation, since these can change between releases. Test each load on a copy and keep the mapping sheets as controlled documents.
Post opening balances as of a fixed cutover date, and reconcile them to the trial balance and to the aged receivables and payables in the legacy system. Keep open items as open items so that matching and aging continue from where they stood. Build a test database from a production copy and run it through at least one full month-end close before cutover. Finance should compare the Odoo outputs with the legacy reports line by line. Odoo documents database duplication with neutralization for test copies, but you should confirm exactly what neutralization disables in your version, such as outgoing mail or scheduled actions, before any test copy connects to live systems.
Run a parallel period in which the legacy system and Odoo both record the same transactions. Agree in advance the tolerance for differences, who investigates them, and who signs off the result. Plan for the risks that most often stall the move: duplicate partners created from spreadsheets with inconsistent names, tax codes that do not map cleanly to the Odoo fiscal localization, and historical attachments that users still need to find. When you request a quote, ask for a written scope that lists source systems, data volumes, processes in scope, reconciliation sign-off steps and the handover of parallel run results. Name the person on your side who approves the opening balances. DeployOpen prepares the controls and evidence for your finance team, but independent auditors issue any opinion on the figures.
What maps cleanly from spreadsheets and legacy accounting into Odoo
This table compares common source items from spreadsheets or a legacy accounting package with the Odoo Accounting and related app records they would load into. Each row marks whether the item maps directly, maps with reshaping, or needs a decision from your finance team. Confirm each row against the Odoo version you will run, since import templates and field names differ between releases.
| Source item | Odoo equivalent | Mapping status | What to confirm in your Odoo version |
|---|---|---|---|
| Chart of accounts | Accounting chart of accounts | Maps with reshaping | Account types and codes must align with your fiscal localization; decide how legacy codes and groupings are kept |
| Customers | Contacts (customer records) | Maps with reshaping | Split duplicate contacts and decide which fields become company, address and payment terms |
| Vendors | Contacts (vendor records) | Maps with reshaping | Confirm how a single partner is used as both customer and vendor and which vendor terms carry over |
| Products | Products in Sales, Purchase and Inventory | Needs a decision | Decide whether services, stockable goods and consumables are separate products and which apps you will install |
| Tax rates | Taxes and fiscal localization | Needs a decision | Map each legacy rate to an existing Odoo tax or create one; confirm reporting tags with your fiscal localization |
| Open invoices and bills | Customer invoices and vendor bills | Needs a decision | Choose between re-entering open items or loading them as opening entries; confirm how payments and partial balances are matched |
| Bank balances | Bank journals and opening balances | Maps with reshaping | Confirm the bank journal setup and the reconciliation start date before loading statement lines |
| Trial balance | Opening journal entries | Maps with reshaping | Balance the opening entry to zero and confirm the cut-off date with your accountant |
| Fixed assets | Assets module (depreciation schedules) | Needs a decision | Confirm whether the Assets feature in your edition covers your depreciation methods before deciding on import or manual setup |
| Recurring entries | Recurring entries or scheduled templates | Needs a decision | Confirm the recurrence options available in your version and rebuild each template rather than assuming a direct copy |
| Custom reports and macros | Standard reports, Studio or custom development | Needs a decision | Inventory each report and macro, then decide whether the standard report, a configuration or a custom module replaces it |
Which documented import paths Odoo offers for this move
Four documented routes cover most of a move from spreadsheets or an older accounting package: a file import for master data and balances, external IDs for linking records, a neutralized test copy for rehearsal, and connectors for specific source systems. They are meant to be used together, in an order set by your cutover plan. Confirm each route's behavior for the Odoo version you will run, because import options and connector support change between releases.
Built-in CSV and Excel import
Odoo's import function loads master data such as contacts, products and chart of accounts entries from CSV or Excel files, and it is the usual route for opening balances once the accounts exist. It does not, on its own, rebuild a full transaction history, attachments or documents that depend on other records, so those need a separate plan. Confirm in the documentation for your version which models support import, which fields are required, and how the import handles errors and existing records before you schedule the first load.
External IDs for related records
External IDs give each imported record a stable reference, so invoices can point to the correct customer and product in a later file without knowing internal database IDs. They make a staged load in the right order workable, because a child file can resolve parents loaded earlier. They do not fix poor source keys: duplicate or changing identifiers in the spreadsheet carry straight into the database. Check in the version documentation how external IDs are created, how they are referenced during import, and whether a re-import updates or duplicates existing records.
Database duplication with neutralization
Duplicating a database and neutralizing the copy gives the migration team a test environment with production-shaped data, where outgoing email, scheduled actions and payment connections are disabled. It is the right place to rehearse imports, check reconciliations and time the cutover steps. The copy is a snapshot, so changes made after it was taken stay out of it. Confirm the duplication and neutralization steps for your deployment type in the on-premise documentation, and test that outbound messages and custom integrations are actually disabled.
Third-party connectors from the Apps Store
Connectors published on the Odoo Apps Store are built for specific source systems and can map records and balances that a generic file import would need restructured by hand. Their publishers, not Odoo, maintain them, so supported source versions, licensing and migrated data scope vary by listing. A connector may not move every object your team uses. Before choosing one, check the listing and its documentation for supported source and Odoo versions, the data it migrates, whether it runs on your deployment type, and how it handles reruns.
Why the import order decides whether the data loads
Odoo checks references when it creates a record, so the order of imports decides whether rows load at all. A journal entry cannot point to an account that does not exist yet, and an open invoice cannot name a customer that is still waiting in another file. The safe sequence starts with master data: the chart of accounts, taxes, partners such as customers and vendors, products, and users. Users come early because records such as sales orders or tasks assign a responsible person. Open items, including unpaid invoices and bills, load only after the partners and products they reference are in place. Load and check each stage before the next begins, so a rejected row in one file does not go unnoticed behind a successful import in another.
External IDs let a later file reference a record from an earlier file without relying on internal database IDs, which differ between a test database and production. Each record in the first file carries a stable identifier, often the customer code or account number from the legacy system, and each later file uses that same value to point at the record. Set the scheme before the first load: one agreed pattern per object type, with a prefix that shows where the record came from, applied the same way in every file. This matters on re-runs. After a correction, the import is designed to match rows with a known external ID to the existing record, while rows without one create new records. Most duplicates trace back to a missing or changed identifier. Confirm the update behavior for each model in Odoo's onboarding documentation before relying on it.
Opening entries carry the legacy balances into Odoo, and they must balance. Choose a cut-off date and take the legacy trial balance as of that date. The opening journal entry should have total debits equal to total credits, and each balance sheet account should match its legacy figure. Open receivables and payables are loaded as open items against their partners, so their totals should tie to the aged reports at the same date. Bank balances should match the bank statements at the cut-off, and inventory valuation, if you carry stock, needs its own reconciliation. Stop new postings in the legacy system after the cut-off, or record later activity separately, so the load does not chase a moving target. Ask your accountant to sign off the tie-out before any user works in the new system.
Run the whole sequence on a test database first and treat that run as a rehearsal. After each stage, compare record counts and control totals with the source file, and keep the log of rejected rows. Fix errors in the source file and reload it, so each run is repeatable and external IDs stay consistent. Plan a full run from start to finish rather than a sample of records, and plan a parallel run in which both systems record the same activity for a set period before users switch. The exact import steps, file formats and order of models depend on the Odoo version and the apps you install, so confirm them against Odoo's own onboarding documentation before the first file is prepared.
The cutover sequence: inventory, test database, parallel run and switch
This sequence moves finance records out of spreadsheets or a legacy package and into Odoo in an order that can be checked at each stage. Each phase ends with a signed output and a reconciliation, so the switch happens only after the books agree.
Inventory and clean the source data
Your team lists every source file, export and system that holds finance records, including the chart of accounts, contacts, products, open invoices and bills, and the trial balance at the cutover date. We review each source with you for duplicates, unused accounts, inconsistent naming and records that should be archived rather than loaded. The output is a source inventory and a cleaned extract for each dataset, approved by the finance lead. The check is that record counts and balances in each cleaned extract match the source at the agreed cutoff, with every exclusion logged and justified.
Sign off field mapping with finance
We map each source field and account to its Odoo equivalent, including the chart of accounts, tax rates tied to the fiscal localization for your country, payment terms, currencies and analytic or journal structure. Finance signs off the mapping document before any data is loaded, because later changes force reloads. Decisions that need a policy answer, such as how to treat historical dormant accounts, are recorded with an owner. The output is a signed mapping workbook. The check is a dry run of a sample of records through the mapping, reviewed line by line by the finance lead.
Load master data in dependency order
Odoo records refer to one another, so the loads follow a fixed order: company and fiscal settings, taxes, accounts, journals, partners, products, and then transactional records. Related records are linked using External IDs so that imported invoices and payments attach to the right contacts and products without manual lookup. Each load is run with Odoo's import tools in the test environment first, and errors are fixed in the source extract, not in Odoo by hand. The output is a load log per dataset. The check is that the counts loaded match the approved extract and that a sample of linked records opens correctly with its parents.
Post opening entries and open items
Opening balances are posted as a single opening entry dated before the cutover, and open customer invoices and vendor bills are imported so that they can be matched and paid in Odoo. The opening entry must balance and must agree with the trial balance signed off in the first step. Open items are reconciled against the sub-ledger totals so that receivables and payables in Odoo match the source. The output is a signed opening trial balance and an open items schedule. The check is that the Odoo trial balance and aged receivable and payable reports equal the approved source figures to the cent.
Rehearse on a test database
We create a test database from the Odoo instance and run the full load sequence against it, so the team can rehearse the cutover before production is touched. The test copy is duplicated with neutralization for test use, which keeps outbound email and payment connectors from acting on real records, a step that matters if your Odoo edition or hosting type supports the duplication you need. Each rehearsal is timed and every defect is logged against the mapping or the source extract. The output is a rehearsal report and a runbook. The check is two consecutive rehearsals that produce the same totals and no open defects in the runbook.
Run both systems in parallel
For an agreed period, the business records transactions in both the legacy system and Odoo, so finance can compare results from real activity. Daily or weekly totals for sales, purchases, cash and the trial balance are reconciled between the two systems, and differences are traced to a cause and resolved before the period closes. The output is a parallel run reconciliation pack signed by finance. The check is that every difference has a documented cause, that the closing totals agree, and that users have completed their training on the Odoo workflows being used.
Switch over and decommission the old system
At cutover, the final incremental extract is loaded to production, the opening entry is posted, user access is switched, and the legacy file or package is set to read-only. The legacy system is archived with a retained copy of its data for the period your records policy requires, which your compliance or legal lead should confirm. Decommissioning happens only after the first closing period in Odoo is reconciled. The output is a cutover certificate and an archive record. The check is that production totals match the signed reconciliation, that the legacy system accepts no new postings, and that the archive can be opened and read by someone other than the original file owner.
Choosing the Odoo edition and hosting before the data load
The edition and hosting choice shapes the migration plan, so settle it before any data is loaded. Odoo offers a fully open source Community edition under the LGPL-3.0 license and a paid Enterprise edition with additional features and support. The Enterprise source code is not publicly available, so using it requires a subscription or partner access. According to Odoo's pricing page, only the Custom plan can be hosted on Odoo.sh or on-premise, and only Custom includes multi-company, Odoo Studio, external API access and custom modules. If your migration depends on an API connection to a legacy system or on a custom module, those requirements point to the Custom plan from the start. Confirm the current contents of each plan on Odoo's pricing page before you decide, because plan inclusions can change.
The upgrade path has to be decided before the build, because it determines who can move the database forward later. Odoo's upgrade service requires an Enterprise subscription for on-premise use and is not available for the Community edition. Standard support covers the first three years after a major release on Odoo Online, Odoo.sh and on-premise, including security updates and bug fixes. Paid extended support is limited to Odoo.sh for select versions and to on-premise, and security patches are only provided during standard support. Odoo 19.0 is supported under standard support until September 2028, and Odoo 20.0 until September 2029. Ask which version you will start on, when its standard support ends relative to your planned go-live and retirement dates, and which upgrade route applies to your edition.
Hosting also sets the recurring upgrade workload. On Odoo Online and Odoo.sh, major version upgrades are mandatory every two years, according to Odoo's upgrade documentation. Odoo SaaS versions are released every two to three months and cannot use extended support. For on-premise Enterprise installations, the database is activated by entering the subscription code, and Odoo documents packaged installers and source installation. Odoo also documents database duplication with neutralization, which is the method for creating test copies. Your parallel run and upgrade rehearsals depend on that capability, so confirm that your chosen hosting gives your team the access needed to create and maintain those copies.
Record the decisions in one document before the first import: edition, plan, hosting model, starting version, standard support end date, subscription holder, who performs upgrades, and where test databases will live. Ask any implementation partner to price against that written record, and ask the quote to separate Odoo subscription fees, hosting charges, implementation effort and recurring upgrade and support work. Subscription costs recur, while configuration and data migration are usually one-time. A clear record also gives your finance and security reviewers the information they need to approve the plan without re-opening the edition question later.
What drives the cost and duration of a migration to Odoo
This table lists the factors that most change the effort of moving from spreadsheets or a legacy accounting package into Odoo, the direction each one pushes the work, and what the client should prepare. Use it to see which items your own data and processes will raise or lower the scope before you request a written quote.
| Driver | Effect on cost and duration | What the client should provide |
|---|---|---|
| Data quality in spreadsheets | Duplicates, inconsistent names, missing tax IDs and mixed date formats increase cleansing and re-import cycles | Owner for each master data list and a cleaned sample file reviewed by finance |
| Number of legacy entities and tax regimes | More entities, tax codes and rates increase mapping work and fiscal localization checks | Chart of accounts, tax rate schedule and a list of legal entities with their filing obligations |
| Volume of open items | Large numbers of open invoices, bills and payments increase matching and reconciliation time | Aged receivables and payables listings as of the cutover date, with bank statements |
| Processes moving at once | Moving sales, purchasing, inventory and payroll together adds configuration, testing and cutover coordination | A ranked list of processes to go live first and the owner of each |
| Custom reports or macros to rebuild | Each spreadsheet macro or legacy report needs a specification, a build and acceptance testing | Copies of existing reports and macros, with the people who use them and the decisions they support |
| Bank and payment provider integrations | Live bank feeds or payment connectors need access setup, mapping and reconciliation testing | Bank list, account numbers, connector credentials policy and provider contacts |
| Number of companies and currencies | Each company and currency adds opening balances, consolidation rules and separate testing | Company structure, base and foreign currencies, and exchange rate sources |
| Edition and hosting chosen | Community versus Enterprise and Odoo Online versus self-hosting change which features, upgrade paths and support windows apply | Decision on edition and hosting, and confirmation of required features with Odoo's current pricing page |
| Parallel-run cycles | Each parallel run repeats imports and reconciliations, so more cycles add time but reduce cutover risk | Agreed number of parallel periods and the reports used to compare results |
| Finance staff availability for sign-off | Delayed reviews of test databases and opening entries extend the schedule and can push cutover | Named approvers, their review dates and a delegate for each sign-off step |
What usually goes wrong in spreadsheet and legacy accounting moves
Most failed Odoo accounting migrations fail in the same few places: balances that never tied out, records loaded twice or in the wrong order, and access granted before anyone designed the roles. Each risk below has one prevention step and a named client-side owner, so the decisions sit with people who can make them before the first test import.
Opening balances that do not tie
Opening entries loaded into Odoo must reproduce the legacy trial balance at the cutover date, account by account, including open receivables and payables. Without a signed-off comparison, differences surface months later as unexplained variances. Prevention: produce the legacy trial balance and the Odoo opening entries from the same cutover date, and reconcile them line by line before any test database is approved. Owner: the financial controller.
Duplicate partners and products
Spreadsheets often hold the same customer as "Acme Ltd", "ACME Limited" and "Acme", and the same product under several codes. Loading each variant creates duplicate partners, which splits open items and breaks aging reports. Prevention: deduplicate contacts and products in the source files against agreed matching keys, such as tax ID and bank account, before the import template is filled. Owner: the accounts receivable and payable leads.
Master data loaded out of order
Odoo records refer to one another, so journals, taxes, accounts, partners and products must exist before the transactions that use them. An invoice file loaded before its customers or tax rates fails or creates placeholder records that nobody reviews. Prevention: write down the load sequence, run each step on the test database, and stop the sequence if a step leaves rejected rows. Owner: the migration lead on the client side.
External IDs changed on re-runs
Odoo uses External IDs to link imported records to each other and to the source system. If a re-run reuses an ID for a different record, or the ID scheme changes between runs, related records attach to the wrong parents or duplicate silently. Prevention: fix one External ID scheme per object type before the first test load, keep the source key in every file, and reject re-runs that alter existing IDs. Owner: the data migration engineer.
Tax setup that misses legacy rates
Legacy tax codes often encode rates, reverse-charge rules or rounding that the Odoo fiscal localization does not reproduce by default. Invoices then carry rates that differ from the filed history, and returns no longer reconcile. Prevention: map every legacy tax code to an Odoo tax with its rate, scope and rounding, and test one period of returns against the old filings. Owner: the tax manager.
Access granted before roles are designed
Users who receive full access during testing often keep it at go-live, and roles then get built around the accounts people happen to hold rather than the duties they need. Prevention: design the role matrix, separating entry, approval and posting duties, before any user is created in the test database, and map each legacy user to one designed role. Owner: the finance systems owner, with internal audit review.
Parallel run skipped or unowned
Skipping the parallel run removes the only period in which Odoo outputs are compared with the legacy ledger under live volumes. Running it without a reconciliation owner means differences are logged but never cleared. Prevention: agree the number of parallel periods, the reconciliation checks and a named owner who signs off each difference before the legacy system is switched off. Owner: the controller, with the finance operations lead as day-to-day owner.
Questions buyers ask before hiring help with an Odoo migration
What does an Odoo migration from spreadsheets or legacy accounting software cost?
No price is published for this work, and DeployOpen does not publish a price list. The cost follows from a written scope, and the main components are the cutover plan, data mapping and cleansing, the import work for each master data type, opening entries, a test database, the parallel run and cutover support. Costs rise with the number of legacy sources, the volume and quality of records, the number of companies and currencies, any custom fields or modules, and any integrations that must be rebuilt. Ask any partner to itemize these components in the quote, and to state which items are one-time and which recur. The licensing and hosting you pay Odoo or your hosting provider are separate from the migration fee and recur for as long as you run Odoo.
How long does a migration to Odoo take?
Duration depends on scope and on how ready your data is. A small company with clean customer, vendor and product lists and a single chart of accounts has a much shorter path than a business with several legacy systems, duplicate records and years of open items. The biggest schedule risk is usually data readiness, not the import itself. Ask for a timeline in the written plan, which should show the master data imports in order, the opening entry date, the test database cycles, the parallel run period and the agreed cutover window. Treat any duration given before the data has been reviewed as an early estimate, not a commitment.
What does our team need to provide during a migration?
Four things decide whether the project moves on schedule. Finance must sign off the chart of accounts mapping, the opening balances and the trial balance that the new database must reproduce. You need a named data owner who answers questions about records, duplicates and exceptions quickly. The partner needs access to the source files, exports or database extracts, along with any documentation on how they were used. Finally, you need a test environment where imports can be run, reset and checked before anything reaches production. Agree these responsibilities in writing so delays are visible early.
Can our own team migrate to Odoo without a partner?
Yes, for small datasets that are clean and have a simple structure. Odoo provides documented import tools, including CSV and Excel import for master data, and a team with accounting knowledge can load contacts, products and opening balances in the documented order. The work gets harder once you have several legacy sources, custom fields, multiple companies, open invoices that must keep their history, or integrations and custom modules that depend on external IDs. Those cases need someone who can design the mapping, verify each import and reconcile the results against the trial balance. Decide based on your data and your team's capacity to test, not only on the number of users.
What is one-time work and what recurs after an Odoo migration?
Migration work is one-time. Mapping, cleansing, importing, reconciling, testing and cutover support are paid once for the project, although later changes to the setup are separate work. Recurring costs are the Odoo subscription, hosting, backup storage and ongoing support or maintenance. On Odoo Online, plans are priced per user per month with a yearly discount, and hosting is included. Custom plan features such as multi-company, Odoo Studio, external API access and custom modules are only available on the Custom plan, which affects recurring cost. Confirm current plan details on Odoo's pricing page before budgeting.
How do we ask a partner for a quote from a written scope?
Send the partner a written scope that lists your legacy sources and their export formats, the approximate record counts for each master data type, the companies, currencies and tax setups involved, the open items you need to carry over, and any integrations or custom modules expected. Add the go-live goal, your data owner and the testing you expect. Ask for a quote that separates discovery, mapping, imports, opening entries, test cycles, parallel run and cutover, and that names the assumptions behind each line. Ask what a change in scope would cost and how the parallel run and rollback plan are handled. Quotes built this way are easier to compare.
What must be verified before we switch users to Odoo?
Before switching users, the test database should reproduce the opening trial balance, the open customer invoices and vendor bills should match the legacy totals, and master data counts should reconcile with the source exports. A parallel run compares transactions in the legacy system and in Odoo over an agreed period. Confirm that Finance has signed off each reconciliation and that the cutover steps and a rollback path are written down. Ask your partner to show how each check is recorded so auditors and your own team can trace the migration later.
How DeployOpen scopes, delivers and supports an Odoo migration
DeployOpen scopes a migration from spreadsheets or legacy accounting software to Odoo as a fixed-scope engagement, with deliverables agreed in writing before work starts. The build follows Odoo's onboarding order: master data imports, External IDs for related records, opening entries, a test database and a parallel run, all inside infrastructure you control. Ongoing support is available as a retainer.
How we scope
- The written scope lists which source files, ledgers and master data sets move into Odoo, and which stay behind.
- Scope covers the chart of accounts, customers, vendors, products and the cutover date for opening entries.
- Engagements are fixed-scope, so deliverables and timelines are agreed in writing before any import work starts.
How we build
- Master data is imported in dependency order, so parent records exist before the records that reference them.
- Related records receive stable External IDs, so links between imported records can be checked and re-run.
- Opening entries are reconciled to the legacy trial balance in a test database before cutover.
How we hand over
- Each import is documented with its source file, mapping rules, record counts and any rejected rows.
- A parallel run compares Odoo results with the legacy system for an agreed period before users switch over.
- Confirm in the written scope which mapping files, import logs and cutover checklists you receive at handover.
How we support
- Odoo standard support covers the first three years after a major release, including security updates and bug fixes.
- Plan the version path before cutover, since Odoo Online and Odoo.sh require major upgrades every two years.
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.

