Verified by Garnet Grid

ERP Data Migration: Field Mapping & Validation Playbook

Step-by-step guide for ERP data migration. Covers data profiling, field mapping, transformation rules, validation testing, cutover planning, and common pitfalls.

80% of ERP project delays are caused by data migration. Not because the technical migration is hard, but because nobody profiled the source data, nobody documented the mapping rules, and nobody validated the results until go-live week. This playbook prevents that by giving you an explicit phase-gate process with validation criteria at every step.

The golden rule of ERP migration: data cleansing happens in the source system, not during migration. If you try to fix data quality issues in your ETL scripts, they’ll grow in complexity until they become unmaintainable. Clean the source first, then migrate clean data.


Migration Phases

Phase 1: Data Profiling → Understand what you have
Phase 2: Field Mapping → Define source → target transformations
Phase 3: Data Cleansing → Fix quality issues before migration
Phase 4: Migration Development → Build ETL scripts/tools
Phase 5: Mock Migration → Run full migration in test environment
Phase 6: Validation → Verify accuracy and completeness
Phase 7: Cutover → Execute production migration

Phase Timeline

PhaseDurationKey DeliverableGate Criteria
Profiling2-3 weeksData quality reportBusiness sign-off on scope
Mapping3-4 weeksSigned mapping documentAll fields mapped or excluded
Cleansing3-6 weeksClean source dataQuality KPIs met
Development4-6 weeksETL scripts + unit testsAll mappings implemented
Mock Migration2-3 weeks (×3 mocks)Reconciliation reportRow counts + financials match
Validation1-2 weeksBusiness sign-offSME spot-checks pass
Cutover1 weekendProduction system livePost-live validation passes

Phase 1: Data Profiling

Before mapping anything, understand your source data.

-- Profile a source table
SELECT
    'customers' AS table_name,
    COUNT(*) AS total_rows,
    COUNT(DISTINCT customer_id) AS unique_ids,
    SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) AS null_emails,
    SUM(CASE WHEN phone IS NULL THEN 1 ELSE 0 END) AS null_phones,
    MIN(created_date) AS earliest_record,
    MAX(created_date) AS latest_record,
    SUM(CASE WHEN status = 'inactive' THEN 1 ELSE 0 END) AS inactive_count
FROM source_customers;

Profiling Checklist

CheckWhat to Look ForRed Flag
Row countBaseline for validationMillions more than expected
NULL rates per columnMandatory fields that are empty> 50% NULL on required fields
Duplicate keysIDs that aren’t actually uniqueAny duplicates on PK
Date rangesHistorical data you may not needRecords from 1900 or future dates
Orphan recordsChild records without parent> 1% orphan rate
Encoding issuesUnicode, special charactersMojibake (garbled text)
Status codesWhat values exist vs what target acceptsUnmapped status values
Data volume per entityHow many records per categoryOne category with 90% of data

Entity Prioritization

EntityDependenciesMigration OrderRisk Level
Chart of accountsNone (foundation)1stLow
CustomersNone2ndMedium
Vendors/SuppliersNone3rdMedium
Products/ItemsCategories, UoM4thMedium
Open purchase ordersVendors, Products5thHigh
Open sales ordersCustomers, Products6thHigh
Open invoices (AR/AP)Customers, Vendors7thCritical
General ledger balancesChart of accounts8thCritical
Transaction historyAll of the aboveLast (if at all)High

Phase 2: Field Mapping

Mapping Document Template

Source TableSource FieldTarget TableTarget FieldTransformValidationNotes
customerscust_idCustomerCustomerAccountDirect mapNOT NULL, uniquePK
customerscust_nameCustomerNameTRIM + PROPER CASEMax 100 chars
customersemailCustomerPrimaryEmailLOWER + TRIMValidate format
customersphoneCustomerPhoneFormat to E.164Strip non-digits first
customersstatusCustomerStatusCASE: A→Active, I→InactiveValid values onlyDefault: Active
customerscredit_limitCustomerCreditLimitConvert to target currencyNULL → 0.00, ≥ 0
ordersorder_dateSalesOrderOrderDateConvert timezone to UTCNot in future
orderstotalSalesOrderTotalAmount2 decimal placesValidate > 0

Transformation Rules

# Common transformation functions
def transform_customer_name(name: str) -> str:
    """Standardize customer names."""
    if not name:
        return "UNNAMED CUSTOMER"
    return name.strip().title()[:100]  # Trim, proper case, max length

def transform_phone(phone: str) -> str:
    """Convert to E.164 format."""
    digits = re.sub(r'\D', '', phone)
    if len(digits) == 10:
        return f"+1{digits}"  # US default
    elif len(digits) == 11 and digits[0] == '1':
        return f"+{digits}"
    return f"+{digits}"

def map_status(source_status: str) -> str:
    """Map source status codes to target."""
    mapping = {
        'A': 'Active', 'ACTIVE': 'Active',
        'I': 'Inactive', 'INACTIVE': 'Inactive',
        'S': 'Suspended', 'D': 'Deleted',
    }
    return mapping.get(source_status.upper(), 'Active')

Handling Unmapped Data

ScenarioStrategyExample
Source field has no targetPark in _legacy tablemisc_field_3 → legacy reference table
Target field has no sourceSet default valueCurrencyCode → ‘USD’
Values don’t match target enumMap or rejectSource ‘X’ status has no target equivalent
Source has multiple values, target has oneMerge with priority rules3 phone fields → pick most recent

Phase 3: Data Cleansing

Common Cleansing Tasks

IssueFrequencyFix StrategyWhere to Fix
Duplicate customer records5-15%Deduplicate merge rulesSource system
Invalid email formats10-20%Regex validation + manual reviewSource system
Missing mandatory fieldsVariesDefault values or manual entrySource system
Inconsistent addresses20-40%Address standardization serviceETL or source
Orphaned transactions2-5%Re-link or excludeSource system

Phase 5: Mock Migration

Run at least 3 full mock migrations before cutover.

MockPurposeTimingSuccess Criteria
Mock 1Test ETL scripts, identify mapping errors8 weeks before go-liveScripts execute without errors
Mock 2Fix issues from Mock 1, validate with business4 weeks before go-liveRow counts within 0.5%
Mock 3Dress rehearsal — full cutover simulation2 weeks before go-liveRow counts exact, financials to the penny

Mock Migration Runbook

Pre-Mock:
  □ Refresh test environment to clean state
  □ Take snapshot of source data (point-in-time)
  □ Notify stakeholders of mock schedule

Execute:
  □ Run ETL scripts in sequence (master data → transactions)
  □ Record execution time for each entity
  □ Log all errors and warnings to tracking spreadsheet

Validate:
  □ Row count reconciliation (source vs target)
  □ Financial reconciliation (AR, AP, GL balances)
  □ Business spot-checks (50 random records per entity)
  □ Integration tests (can users create orders against migrated data?)

Post-Mock:
  □ Document all issues found
  □ Estimate remediation effort
  □ Update migration runbook with lessons learned

Phase 6: Validation

Reconciliation Report

-- Row count reconciliation
SELECT
    'Customers' AS entity,
    (SELECT COUNT(*) FROM source_customers WHERE status != 'D') AS source_count,
    (SELECT COUNT(*) FROM target.customers) AS target_count,
    (SELECT COUNT(*) FROM source_customers WHERE status != 'D') -
    (SELECT COUNT(*) FROM target.customers) AS difference;

-- Financial reconciliation
SELECT
    'Open AR Balance' AS metric,
    (SELECT SUM(balance) FROM source_ar WHERE status = 'open') AS source_total,
    (SELECT SUM(amount) FROM target.ar_transactions WHERE open = 1) AS target_total;

Validation Gates

GateCriteriaAction if Failed
Row countSource − target < 0.1%Investigate, re-migrate
Financial totalsDifference < $0.01Trace every cent
Referential integrity0 orphan recordsFix parent records first
Business spot-check50 random records validated by SMEFix mapping rules
Integration testUsers can create transactions on migrated dataFix configuration

Phase 7: Cutover Planning

Cutover Timeline (Example Weekend)

TimeActivityOwnerRollback Point
Friday 6 PMFreeze source system (read-only)IT OperationsN/A
Friday 7 PMFinal data extract from sourceMigration Team✅ (restore read-write)
Friday 9 PMRun production migration scriptsMigration Team✅ (discard and retry)
Saturday 6 AMAutomated validation suite runsMigration Team✅ (restore source)
Saturday 9 AMBusiness validation (SME spot-checks)Business Team✅ (restore source)
Saturday 3 PMGo/No-Go decisionSteering Committee✅ (last rollback point)
SundayTraining day, resolve issuesAllNo rollback after this
Monday 8 AMProduction go-liveAllIncident response mode

Common Pitfalls

PitfallImpactPrevention
Not profiling source dataDiscover bad data at go-liveProfile in Phase 1, no exceptions
Migrating ALL history10x migration timeDefine cutoff (2-3 years typical)
No mock migrationsUntested scripts in productionMinimum 3 mocks
Skipping financial reconciliationBalance discrepancies post go-liveReconcile to the penny
One person owns migrationSingle point of failureCross-train, document everything
Data cleansing during migrationETL complexity explodesClean in source system first
No rollback planStuck with bad migrationDocument rollback at every step

Checklist

  • Source data profiled (row counts, NULL rates, duplicates, orphans)
  • Entity migration order defined (master data first, transactions last)
  • Field mapping document completed and reviewed by business
  • Transformation rules coded and unit tested
  • Unmapped data strategy documented (legacy tables, defaults)
  • Data cleansing completed in source system before migration
  • 3+ mock migrations executed with documented results
  • Row count reconciliation passes (<0.1% variance)
  • Financial reconciliation passes (<$0.01 variance)
  • Business SME validated 50+ random records per entity
  • Integration tests pass (create transactions on migrated data)
  • Cutover runbook documented with rollback points
  • Post-go-live validation checklist ready
  • Support plan for first 2 weeks post-migration

:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For ERP migration consulting, visit garnetgrid.com. :::

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →