How to use this ERP data migration checklist
A legacy ERP or accounting system is being retired, and the team has to decide what moves into Odoo or ERPNext, what stays behind as an archive, and how every opening balance will be proven before users rely on the new system. This checklist is built for that decision. It groups the work into functional, security, integration, operational and compliance requirements, and each item carries a verification method: a reconciliation report, a sample record review, a restore test, a role-permission walkthrough or a signed sign-off. Treat it as a scoping tool. It shows what to ask, who must answer and how the answer will be checked, but it does not guarantee a clean cutover, because the result depends on data quality in the source system, the chart of accounts design and how much history the business actually needs.
Use the items in order. Start by agreeing which master data, open items and balances are in scope, then confirm the verification method for each one before any file is prepared. Record who owns each control, whether that is finance for balances, IT for access and encryption of the migration environment, or operations for stock and open orders. Where a requirement comes from a regulation, such as record retention or access controls under the FTC Safeguards Rule at 16 CFR 314.4 for covered financial institutions, name the control owner and the evidence that will be kept, and confirm the interpretation with your own compliance or legal advisers. DeployOpen plans, implements, tests and documents these controls in your environment, and prepares evidence for compliance assessments, but it does not issue audit reports, attestations or certifications. Independent auditors and certification bodies make those determinations.
Which data to migrate and which to archive
This table compares each data type in a legacy ERP or accounting system against the decision to migrate it, archive it, or rebuild it, and how to verify the result. Read each row as a sign-off point: the verification column names the evidence to request before cutover. The main takeaway is that balances and open items must tie to the legacy system, while most closed history can stay in a read-only archive.
| Data type | Migrate or archive | Why it matters | How to verify |
|---|---|---|---|
| Customers | Migrate active and open-balance records | Receivables, credit terms and tax status depend on clean customer identity and duplicates break reporting | Count and duplicate check against the legacy customer list, plus a sample of ten customers with terms compared field by field |
| Vendors | Migrate active records and those with open payables | Payment runs and 1099 or equivalent tax reporting depend on correct vendor tax IDs and bank details | Tie open payables by vendor to the legacy aged payables report; confirm bank detail changes were approved |
| Products and items | Migrate active items, units of measure and costing method | Stock valuation and pricing rules fail when units or cost methods differ between systems | Compare item master extract to the legacy item list; test one item's on-hand value after load |
| Chart of accounts | Migrate as mapped target structure, not a copy | Every balance and report line depends on the account mapping, and legacy accounts often carry obsolete codes | Signed mapping table approved by the controller; legacy-to-target account totals compared before and after load |
| Tax codes and rates | Rebuild in target, validated against filing history | Tax rules are the most common source of misstated returns after migration | Re-run a sample of prior-period invoices through new tax setup and compare tax totals to filed returns |
| Currencies and exchange rates | Migrate currency setup and only the rates needed for open items | Foreign currency open items revalue incorrectly if the rate basis differs | Revalued open items in target compared to the legacy revaluation report at the same date |
| Open items (AR, AP, inventory on hand) | Migrate in full as balances with source document references | These drive collections, payments and audit testing, and they must tie to the closing position | Sample tie-out of open invoices by document number, amount and due date to the legacy aged reports |
| Opening balances | Load as a single balanced opening entry per entity and period | Errors here flow into every later period and are hard to isolate after go-live | Trial balance in target matches legacy trial balance at the cutover date for each entity, with differences explained |
| Transaction history | Archive closed periods as read-only; migrate only what reporting needs | Full history increases load risk and cost, and it rarely changes operations once periods are closed | Confirm the archive is searchable, retention periods are set, and archived totals agree to legacy period totals |
In what order to load master data, balances and history
The sequence loads the structure the ledger depends on first, then the records that point to it, then balances and open items, and only then any archived history. Each stage ends with a trial load and a check before the next one starts, so errors surface where they are cheap to fix.
Fix the chart of accounts and dimensions
Map every legacy account to a target account in Odoo or ERPNext, then define the company, fiscal years, currencies, cost centers or analytic dimensions, and tax accounts that the rest of the data will reference. Finance owns the mapping and signs off each legacy-to-target line, while the implementation partner builds the structure in a non-production environment. Done means every legacy account has one target account, every balance-bearing account is accounted for, and a trial import of the structure completes without rejected rows.
Load master data in dependency order
Load the records that other records link to, in the order they depend on each other: units of measure, tax templates, payment terms, customers, suppliers, items, bank accounts, and employees or users where they carry finance roles. Odoo and ERPNext both let you import child records and linked documents, but a row that references a missing parent is rejected, so parents must exist first. Done means each master data file imports cleanly in a trial load and a sample of records opens with the expected links.
Size batches to the import tool
Split each file into batches before the first trial load. The ERPNext Data Import documentation advises importing only a few thousand records at a time, so large customer or item files are split into several runs with a log kept for each. Test the batch size on a copy of the real data, time each run, and record the error file produced for every batch. Done means the batch plan for every file is written down and the rerun procedure for failed rows has been tested.
Load opening balances and open items
Enter opening balances as a single balanced opening entry per company and fiscal year start, then load open invoices, open purchase invoices, unpaid payments and partially allocated items so that subledgers agree with the general ledger. Load these only after customers, suppliers and accounts exist, because each open item points to them. Done means total debits equal total credits, receivables and payables reconcile to their subledger reports, and the open item count matches the legacy aging report.
Decide which history to archive
Separate the history that must be queryable in the new system from the history that only needs to be retained. Transactions needed for current-year reporting, open tax periods and audit trail continuity usually stay in the ERP, while older records can move to a read-only archive. The retention rules for your sector and jurisdiction decide the split, so confirm them with your compliance and tax advisers rather than assuming a period. Done means each legacy data set has a recorded decision and an owner.
Load migrated history where the decision requires it
For the transaction history you chose to keep, load it after balances are proven, in the same batched way, and link it to the opening balance so reports do not double count. Keep the legacy system readable until the archive is verified. Done means historical totals by period match the legacy reports for the sampled periods and the archive can be searched by the people who need it.
Run full trial loads and reconcile before sign-off
Repeat the full sequence on a fresh copy at least once more, so the final run is a rehearsal of the cutover and not a first attempt. Reconcile trial results against the legacy trial balance, subledgers, bank statements and open item listings, then record each difference and its cause. Finance and the implementation partner sign the reconciliation together, and the signed record becomes evidence for auditors. Done means the last trial run reconciles with no unexplained difference.
Import tooling and batch sizes in Odoo and ERPNext
Each ERP has its own import path, and the path you choose sets the batch sizes, the order of loads and how much custom code the team must maintain. The cards below compare the built-in import tools, the external ID approach in Odoo, scripted loads and the Server Script restrictions that apply to ERPNext versions 15 and later.
ERPNext Data Import tool
The Data Import tool loads and updates records from CSV or Excel files or public Google Sheets, using downloadable templates that match each doctype. It supports child table rows, so a sales order or journal entry can arrive with its lines in one file. Because the documentation advises importing only a few thousand records at a time, plan master data, open items and balances as separate, sized batches. Confirm the template columns against your ERPNext version before the trial load, since templates change between releases.
Odoo spreadsheet import with external IDs
Odoo imports records from spreadsheets, and relations such as a product's category or a contact's country are resolved through external IDs. Load parent records first, assign each one a stable external ID from the legacy system, then reference that ID in child files. This keeps reruns idempotent, so a corrected file updates existing rows instead of creating duplicates. Confirm in your Odoo version how the import handles many2many fields and whether it can update existing records.
Scripted loads by the implementation team
For large histories, transformation rules or logic that the standard templates cannot express, the implementation team writes scripts that call the ERP's API or use its ORM. Scripts give repeatable, reviewable runs with logging and reconciliation hooks built in. They also become code that must survive upgrades and be documented for auditors. Agree in the statement of work who owns the scripts after cutover, where they are stored, and how each run is approved and recorded.
Server Scripts disabled by default
In ERPNext, Server Scripts run Python on document events or as API endpoints, and from version 15 they are disabled by default. They must be enabled with the server_script_enabled setting, and Frappe Cloud public shared benches do not allow them at all. If your load depends on a Server Script, you need a self-hosted or dedicated bench where the setting can be changed under change control. Confirm this setting and your hosting tier before the design is final, not during the cutover weekend.
Trial loads and batch logs
Each trial load should run the full batch sequence against a copy of production, with a log that records the source file, row counts, rejected rows and the reason for each rejection. Repeat trial loads until the rejection rate is low enough that the remaining errors can be fixed by hand, and record that decision with the data owner. Ask the partner how logs are retained, since they are part of the evidence for the reconciliation sign-off.
How trial loads and reconciliation sign-off work
The dress rehearsal tests the mechanics before any real migration data is trusted. The team runs the full sequence of extracts, transforms and imports against a copy of the target environment, so that import errors, mapping gaps and missing master data surface while they are cheap to fix. Nothing from this run is considered migrated. In Odoo, the on-premise documentation describes database duplication with neutralization for test copies, which keeps a scratch database from sending emails, posting to live bank feeds or calling external services. In ERPNext, the environment can be built with Docker Compose using the production images from frappe_docker, or with the bench command line tool, so the rehearsal runs on the same versions planned for go-live.
- Import order: Confirm that master data loads before transactions, and that each import file depends only on data already present.
- Error log: Record every rejected row, its cause and the fix, so the same error does not return in the trial load.
- Runtime: Measure how long each batch takes so the cutover plan rests on observed figures, not estimates.
- Neutralized copy: Confirm that outbound email, payment connectors and integrations are disabled in the test copy.
- Version match: Check that the rehearsal environment runs the exact release and add-ons planned for production.
Before leaving this stage, ask the implementation partner to show the rehearsal log and the list of fixes made to mappings and source files.
The full trial load loads the complete, cleaned data set into a fresh environment, then reconciles it against the legacy system. This is where the migration proves that the new ledger says the same thing as the old one. Reconciliation compares control totals, not just whether the import succeeded. Each check should be run against the same extraction date in the legacy system, so differences reflect mapping or transformation issues rather than activity that happened between extracts. Open items are compared individually, because a balanced total can hide a receivable that was matched to the wrong invoice.
- Record counts: Compare the number of customers, vendors, products, open invoices and journal lines loaded against the legacy extract, with any exclusions listed and approved.
- Balances by account and entity: Compare the opening balance of every general ledger account for each legal entity and currency.
- Open item aging: Compare receivables and payables by age bucket and by counterparty, so overdue items keep their original due dates.
- Tax totals: Compare tax collected and tax payable by tax code and period, since a tax mapping error often shows up only here.
- Exceptions register: Log each unresolved difference with an owner, a cause and a decision to fix, accept or defer.
A reconciliation pack should show the legacy figure, the new figure and the difference for every check, with the trial load run identifier attached.
Sign-off converts the reconciliation evidence into a documented decision to cut over. It should name who approves each area, because finance, tax, operations and IT each own different checks, and a single approver for everything is rarely the right design. The sign-off pack should be complete before the production cutover window opens, and it should state which open exceptions were accepted and by whom. Records that must be kept in a retrievable form after the migration, such as archived history and supporting documents, should be listed in the pack so that the retention decision is visible to auditors. For regulated entities, the approvers and the criteria should match the entity's own control framework.
- Finance owner: Approves general ledger balances, open items and the handling of accepted exceptions.
- Tax owner: Approves tax totals and the mapping of tax codes to the new configuration.
- Business owners: Confirm that customer, vendor and product master data is usable for daily work.
- IT and security owner: Confirms access roles, user accounts and that the change was executed through the agreed change process.
- Evidence retention: Store the reconciliation packs, logs and approvals in a location that outlives the project.
Ask the partner who holds the signed pack, how it is versioned, and whether it can be produced for an internal audit or external assessor without rebuilding it from the project team's memory.
Security controls for data in transit and during the load
Migration extracts often contain customer names, bank details, tax IDs and employee records, so they fall under the same safeguards as production data. The cards below cover the controls a migration team should be able to show during a load, and each is tied to the FTC Safeguards Rule elements in 16 CFR 314.4 for covered financial institutions. Confirm with counsel or your compliance lead whether your organization is covered before you map these controls to your program.
Encrypt extracts in transit
Every transfer of legacy extracts from the source system to the staging area and into Odoo or ERPNext should cross an encrypted channel, especially where the path leaves your internal network. Ask the migration partner to document the protocol, the host keys or certificates used, and who can read the transfer logs. The FTC Safeguards Rule element on encrypting customer information in transit over external networks is the reference point here. Confirm whether your load crosses any external network, including vendor-managed staging.
Encrypt staging data at rest
Staging databases, CSV and Excel files, and backups of the load environment hold the same data as the source ledger, so they need encryption at rest and a defined retention period. Check where extracts are stored between the trial loads and the cutover, and who holds the keys. The Safeguards Rule requires encryption of customer information at rest, so a file share that is only access controlled does not meet that element on its own.
Limit access to extracts and staging
Apply least privilege to the extract folders, the staging database and the Data Import tool, and remove access once each load wave ends. Named accounts should replace shared logins, and the migration team should not hold production admin rights unless the cutover plan requires it. Review the access list before each trial load, because contractors and short-term analysts often keep access after their task closes.
Multi-factor authentication for data access
Anyone who can open an extract, log in to the staging environment or run an import against production should authenticate with multi-factor authentication. The Safeguards Rule requires this for any individual accessing any information system, which includes vendor consultants and service accounts used by a partner's tooling. Ask for a list of every account that touches production data, and confirm how each one is protected. Where an account cannot support a second factor, document the exception and the compensating control.
Log all load activity
Record who ran each import, which file and template were used, the record counts loaded and rejected, and any manual edits made to records during the load. These logs support the Safeguards Rule element on logging the activity of authorized users, and they also give the reconciliation team an audit trail for opening balances. Confirm where the logs are stored, how long they are kept, and whether they can be exported without the migration tool's administrator.
Change control for load scripts
Custom transformation scripts, mapping files and Server Scripts used during the load are changes to the system, so they should go through the same change management steps as production code. Record the version of each script, who approved it, and which trial load tested it. Frappe disables Server Scripts by default from version 15, so confirm whether the migration depends on enabling them and who signs off that setting. The Safeguards Rule requires change management procedures for this work.
Remove test copies with personal data
Trial loads often leave copies of production data in test sites, sandboxes and developer laptops, and those copies frequently carry personal information. Set a removal date for each copy, delete extracts from shared drives once the cutover is signed off, and use neutralized or masked data where the test does not need real identities. Odoo documents database duplication with neutralization for test copies, which is a useful pattern to confirm with your partner.
Operational requirements for the cutover and first close
This table lists the operational requirements that should be written into scope before a legacy ledger moves to Odoo or ERPNext, with a verification method for each. Use it to test whether a partner's cutover plan can be evidenced, not just described. The main takeaway is that every requirement should end in a signed or rehearsed artifact that finance and IT can both inspect.
| Requirement | What it asks for | How to verify it |
|---|---|---|
| Named cutover owner | One accountable person with authority over go or no-go decisions, data loads and finance sign-off | Written RACI in the scope document naming the person, their deputy and the decision rights |
| Rehearsal calendar in scope | Dress rehearsals on copies of production data, each with timed steps and recorded issues | Rehearsal plan attached to the contract, with a log of each run and the defects closed before the next run |
| Rollback plan | A tested path back to the legacy system or a pre-cutover database snapshot if the go-live fails | A rehearsed rollback during a trial run, timed and signed off by the cutover owner |
| Freeze window agreed with finance | Agreed cut-off for postings in the legacy ledger, with a defined period end and a rule for late entries | Signed freeze notice from the controller, plus a test confirming no postings after the cut-off timestamp |
| Parallel reporting for the first period | Legacy and new ledgers run side by side for the first close, with balances compared at agreed reports | Reconciliation pack showing trial balance, open items and subledger totals matched or explained, signed by finance |
| Archived legacy access | Read-only access to the legacy system and an archive of history not migrated, retained under the records policy | Test log showing named users can open archived records, plus confirmation of the retention rule applied to each record type |
| Support path after go-live | Named escalation contacts, a defined issue severity process and a hypercare arrangement written into the contract | Support runbook and a dry run of one reported defect through the escalation path before cutover |
| Access and role checks at go-live | Production user roles, approval rules and segregation of duties tested before users log in | Role matrix reviewed by finance and IT, with test evidence that users cannot approve their own entries |
What buyers ask before hiring migration help
What makes an ERP data migration take longer or cost more?
The biggest drivers are the state of the legacy data, the number of legal entities and charts of accounts being merged, how many years of history must be loaded, and how much custom logic sits in the old system. Duplicate customers, inconsistent item codes and unreconciled bank balances each add cleanup and review cycles before any load runs. Each entity adds its own mapping, validation and reconciliation pass. Ask any partner to break the estimate into these drivers in writing, and to state which assumptions about data quality they have made. Fixed-scope agreements should list the entity count, the history depth and the number of trial loads included.
Can our internal finance and IT team run the migration without a partner?
A capable internal team can run a migration if it has someone who has loaded data into the target system before, a clear owner for each master data object, and time to repeat trial loads. Most teams struggle with the reconciliation stage, not the import itself. Internal staff must supply the legacy extracts, a signed-off chart of accounts mapping, open item listings with aging, bank statement cut-off balances, and decisions on what to archive. They also need a test environment that mirrors production. Ask a partner to list which of these items they expect from your staff and which they will produce, so the split of work is explicit before the project starts.
How much time do finance and IT need to commit during migration?
Finance is needed most for mapping accounts, confirming opening balances, reviewing reconciliation differences and signing off each trial load. IT is needed for extracts from the legacy system, environment setup, access to the target database, scheduled batch runs and cutover steps. The commitment peaks during trial loads and the final cutover window, so ask for a calendar of named reviewers and approval points. Reviewers should be identified before the first load, because delays in sign-off usually extend the schedule more than technical failures do. A written plan should state who approves each reconciliation and what happens when a difference cannot be explained.
Should we migrate all historical transactions or archive older data?
Migrate balances, open items and the data needed to run the business from day one, and archive closed history that is only needed for reference or retention. Loading full history into an ERP increases trial load volume, slows reconciliation and adds error cases, and it rarely improves day-to-day operations. Archived data should remain retrievable for auditors and tax reviews, so decide the retention period with your compliance and legal owners first. For example, a company might load one prior fiscal year of detailed transactions and keep older ledgers in a read-only archive (illustrative). Put the retention rule in writing before scoping the load.
How do we choose between Odoo and ERPNext migration tooling?
Choose based on the target platform's import capabilities, the data volumes you need to load, and the team who will maintain the import process after go-live. ERPNext documents a Data Import tool that works from CSV or Excel files and supports child table rows, and its documentation advises importing only a few thousand records at a time, which makes batch planning essential. Odoo's import features and its module set differ, so confirm the specific import paths, file formats and batch limits for your chosen version with the vendor or your partner. Whichever platform you choose, ask how custom import scripts will be kept upgradable and who will own them.
What should a fixed-scope data migration quote include?
A fixed-scope quote should name the source systems, the entities and currencies in scope, each data object to migrate (master data, open items, balances, selected history), and the data objects excluded and why. It should list the number of trial loads, the reconciliation reports produced, the sign-off points, and the cutover steps covered. It should also state which items the client must supply, the assumptions about data quality, the change process if the scope shifts, and what post-cutover support is included. Request a written statement of exclusions, such as custom report rebuilds or data cleansing performed by your staff. DeployOpen agrees scope, deliverables and timelines in writing before work starts, so ask any provider for the same level of detail.
What data quality problems should we fix before the first trial load?
Fix problems that break mapping or reconciliation: duplicate customers and vendors, missing tax identifiers, inconsistent item units, orphaned invoices and bank balances that do not match statements. Cleansing in the legacy system is usually cheaper than cleansing during import, because the source owner understands the business rules. Set a rule for how unresolved records are handled, such as moving them to a suspense account or excluding them with a documented approval. Ask the partner to run a data profiling pass on the extracts and report the defect counts by object, so cleanup effort is visible before the migration plan is fixed.
What a migration scope should contain before work starts
A written migration scope names every source system and extract before any data moves. For each legacy ERP or accounting system, list the database or export format, the period covered, the entities and currencies included, and who at the client owns the extract. Attach a mapping document for each object (customers, vendors, items, chart of accounts, open invoices, bank balances) that shows the source field, the target field in Odoo or ERPNext, the transformation rule, and the default value used when the source is blank. The scope also fixes the load order: chart of accounts and fiscal years first, then master data, then opening balances and open items, then any transactions the client has agreed to bring across. Confirm with your implementation partner whether the load order is written as a runbook with a named owner for each step.
The scope must state reconciliation criteria in measurable terms before the first trial load. Define which totals must match between the legacy system and the target at each cutover point, such as customer and supplier balances by account, inventory valuation by warehouse, and bank balances by account at the cutover date. Name the sign-off roles: the finance controller who approves balances, the data owner for each master data object, the application owner who approves configuration, and the person who accepts the cutover on behalf of the business. Also list what is out of scope. Historical transactions older than an agreed cutoff, attachments, and archived reports are often moved to a read-only archive rather than imported, and that decision belongs in the written scope with the retention owner named, so that nobody discovers the gap during the cutover weekend.
Most DeployOpen engagements are fixed-scope, so the scope document is the basis for the written agreement, and the quote follows from it. Ask the partner to show how each deliverable in the scope maps to a trial load, a reconciliation report and a sign-off record, because those items drive the effort. Ask what happens when the source data is poorer than expected: who decides whether a record is cleansed, excluded or loaded with a flag, and how a change to scope after work starts is priced and approved. Confirm the batch approach with the partner before pricing, since the ERPNext documentation advises importing only a few thousand records at a time through the Data Import tool.
How DeployOpen scopes, delivers and supports an ERP migration
DeployOpen treats the ERP data migration checklist as a written, fixed-scope workstream inside your own infrastructure, covering master data, open items, balances and cutover. Each stage ends with documented evidence that finance and IT can review and sign off before the next one starts.
How we scope
- Inventory the legacy ERP or accounting system, its tables, extracts and reports, and the owner of each data set
- Agree in writing which master data, open items, balances and history move, and which are archived
- Set the target edition, hosting location and import tooling, with batch sizes and trial load count stated in the scope document
How we build
- Map legacy fields and chart of accounts to Odoo or ERPNext targets, with transformation rules recorded per field
- Run repeated trial loads into a non-production copy and log every rejected record with its reason and fix
- Produce reconciliation reports for balances, open receivables and payables that finance can check line by line
How we hand over
- Deliver a cutover runbook with a step-by-step weekend sequence, owners, rollback points and go or no-go criteria
- Provide signed-off reconciliation packs, load logs and mapping documentation as the migration evidence file
- Walk your finance and IT teams through the archive location and how to retrieve historical records afterwards
How we support
- Support post-cutover correction of data exceptions during the period agreed in the written scope
- Offer ongoing support, upgrades and testing as a retainer once the new system is live
- Confirm with your auditors which migration controls and records they expect to see before cutover is scheduled

