---
# === IDENTITY ===
id: business/erp-integration/data-archival-integration-cutover/2026
canonical_question: "How do you handle data archival and integration cutover during ERP migration?"
aliases:
  - "ERP migration cutover strategy — big-bang vs phased vs parallel run"
  - "What data should I migrate vs archive vs abandon during ERP migration?"
  - "How do I switch integrations from old ERP to new ERP during cutover?"
  - "ERP migration cutover weekend runbook and rollback planning"
entity_type: erp_integration
domain: business > erp-integration > data-archival-integration-cutover
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Cross-ERP (Architecture Pattern)"
    vendor: "N/A — architecture pattern"
    version: "N/A"
    edition: "All"
    deployment: "cloud"
    api_surface: "REST, OData, SOAP, Bulk, File-based"
  - name: "SAP S/4HANA Migration Cockpit"
    vendor: "SAP"
    version: "2408"
    edition: "Cloud & On-Premise"
    deployment: "hybrid"
    api_surface: "Migration Object Modeler (MOM), Staging Tables"
  - name: "Microsoft Dynamics 365 Data Management Framework"
    vendor: "Microsoft"
    version: "10.0"
    edition: "Finance & Operations"
    deployment: "cloud"
    api_surface: "Data Entities, DIXF, OData"

# === VERIFICATION ===
last_verified: 2026-03-07
confidence: 0.83
version: 1.0
first_published: 2026-03-07

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: evolving
  last_breaking_change: null
  next_review: 2026-09-03
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Cutover window is finite — 24-72h for most enterprises. Every migration task must fit within this window or be pre-staged."
  - "Historical data cannot be migrated incrementally after cutover — it must be archived or migrated before the cutover window opens."
  - "Integration switching is all-or-nothing per interface — you cannot run half an integration against old ERP and half against new ERP for the same data flow."
  - "Rollback becomes exponentially harder after users begin transacting in the new system — plan a point-of-no-return decision gate."
  - "Parallel run requires 2x staff effort and 2x integration capacity — budget accordingly or choose a different strategy."
  - "ERP vendor migration tools impose batch size and format constraints that differ from production API limits."

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs a specific ERP's API capabilities, not migration guidance"
    use_instead: "business/erp-integration/{system}-rest-api/2026"
  - condition: "User needs ongoing data synchronization between two live ERPs"
    use_instead: "business/erp-integration/change-data-capture-erp/2026"
  - condition: "User needs master data governance strategy, not migration mechanics"
    use_instead: "business/erp-integration/master-data-management-erp/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: cutover_strategy
    question: "Which cutover approach are you considering?"
    type: choice
    options:
      - "big-bang (single cutover weekend)"
      - "phased (module-by-module or location-by-location)"
      - "parallel run (both systems live simultaneously)"
      - "not decided yet — need guidance"
  - key: data_volume
    question: "How much historical data exists in the legacy ERP?"
    type: choice
    options:
      - "< 1 million records"
      - "1-50 million records"
      - "> 50 million records"
  - key: integration_count
    question: "How many integrations connect to the legacy ERP?"
    type: choice
    options:
      - "< 10 integrations"
      - "10-50 integrations"
      - "> 50 integrations"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/data-archival-integration-cutover/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-07)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/master-data-management-erp/2026"
      label: "Master data management for ERP integrations"
    - id: "business/erp-integration/change-data-capture-erp/2026"
      label: "Change data capture patterns for ERPs"
  solves:
    - id: "business/erp-integration/batch-vs-realtime-integration/2026"
      label: "Batch vs real-time integration pattern selection"
  alternative_to:
    - id: "business/erp-integration/idempotency-erp-integrations/2026"
      label: "Idempotency patterns (complementary, not alternative)"
  often_confused_with:
    - id: "business/erp-integration/error-handling-dead-letter-queues/2026"
      label: "Error handling and DLQs — operational, not migration"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Cutover Management: Planning and Orchestration"
    author: SAP Community
    url: https://community.sap.com/t5/enterprise-architecture-blog-posts/cutover-management-planning-and-orchestration/ba-p/13693946
    type: community_resource
    published: 2024-06-15
    reliability: high
  - id: src2
    title: "Transitional approaches in Cloud Transformations: Cutover planning and the role of middleware"
    author: Microsoft
    url: https://techcommunity.microsoft.com/blog/azuremigrationblog/transitional-approaches-in-cloud-transformations-cutover-planning-and-the-role-o/4107990
    type: technical_blog
    published: 2024-08-12
    reliability: high
  - id: src3
    title: "Best Practices for ERP Migration with Minimal Production Disruption"
    author: BlueNet Inc
    url: https://www.bluenetinc.com/erp-migration-best-practices
    type: technical_blog
    published: 2025-03-10
    reliability: moderate_high
  - id: src4
    title: "Dynamics 365 Data Migration: Strategy and Archiving Best Practices"
    author: Archon Data Store
    url: https://www.archondatastore.com/blog/dynamics-365-data-migration/
    type: technical_blog
    published: 2025-06-20
    reliability: moderate_high
  - id: src5
    title: "Data Migration Strategy: A Practical Guide for 2026 Projects"
    author: Lumitech
    url: https://lumitech.co/insights/data-migration-guide
    type: technical_blog
    published: 2025-11-01
    reliability: moderate_high
  - id: src6
    title: "Manage configuration and migration data for Dynamics 365 projects"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/guidance/implementation-guide/data-management-configuration-data-migration
    type: official_docs
    published: 2025-08-15
    reliability: authoritative
  - id: src7
    title: "SAP S/4HANA Cloud Migration Challenges & Best Practices in 2026"
    author: Birchman Group
    url: https://birchmangroup.com/7-top-challenges-and-best-practices-for-sap-cloud-erp-s-4hana-public-migration-in-2026
    type: technical_blog
    published: 2026-01-15
    reliability: high
---

# Data Archival & Integration Cutover During ERP Migration

## TL;DR

- **Bottom line**: Separate data into migrate (active master + open transactions), archive (historical for compliance), and abandon (obsolete/duplicate) categories before cutover. Switch integrations using a middleware abstraction layer during a planned cutover window, with 2-3 dress rehearsals executed beforehand. [src3, src5]
- **Key limit**: Cutover window is typically 24-72 hours. Every migration task, integration switch, and validation must fit within this window or be pre-staged in the weeks before. [src1]
- **Watch out for**: Migrating all historical data into the new ERP — this inflates cutover time, degrades new system performance, and typically wastes 60-70% of migration effort on data that will never be accessed. [src4]
- **Best for**: Any ERP-to-ERP migration (SAP to S/4HANA, legacy to D365, NetSuite greenfield) where integrations must switch over without extended downtime. [src3]
- **Authentication**: Each integration must be re-authenticated against the new ERP's API surface — pre-provision service accounts, API keys, and OAuth apps in the new system weeks before cutover. [src2]

## System Profile

This card covers the architecture pattern for data archival decisions and integration cutover mechanics during ERP migration. It is system-agnostic at the pattern level but includes specific tooling references for SAP, Microsoft Dynamics 365, Oracle ERP Cloud, and NetSuite. The pattern applies to all major migration scenarios: SAP ECC to S/4HANA, legacy ERP to cloud, multi-ERP consolidation, and greenfield implementations.

The card does NOT cover ongoing data synchronization between two live ERPs (see change-data-capture-erp), master data governance strategy (see master-data-management-erp), or specific ERP API capabilities (see system-specific cards).

| System | Role | Migration Tool | Cutover Capability |
|---|---|---|---|
| SAP S/4HANA | Target ERP | Migration Cockpit (MOM), DMIS, SLT | Staging tables, delta load, migration objects |
| Microsoft D365 F&O | Target ERP | Data Management Framework (DMF/DIXF) | Data packages, entity-based import/export |
| Oracle ERP Cloud | Target ERP | FBDI (File-Based Data Import), ADFdi | CSV/XML templates, scheduled imports |
| NetSuite | Target ERP | CSV Import Assistant, SuiteCloud | Record-type templates, SuiteTalk bulk |
| Middleware (iPaaS) | Integration router | MuleSoft, Boomi, Workato, Azure Integration Services | Traffic switching, parallel routing, canary |

## API Surfaces & Capabilities

Migration tools use different API surfaces than production integrations. Understanding which surface to use for each phase prevents bottlenecks: [src1, src6]

| Phase | API Surface | Protocol | Best For | Volume Limit | Real-time? |
|---|---|---|---|---|---|
| Pre-cutover archival | Database-level export (SQL dump, ABAP extract) | Direct DB / RFC | Historical data extraction | Unlimited | No |
| Master data load | Vendor migration tool (Migration Cockpit, DMF) | Staging tables / data packages | Customers, vendors, items, COA | 50K-500K per batch | No |
| Open transaction load | Vendor migration tool + custom scripts | REST/OData + bulk | Open POs, SOs, invoices | 10K-100K per batch | No |
| Delta data sync | CDC or incremental extract | REST, OData, Change tracking | Transactions created during freeze | 1K-10K records | Near-real-time |
| Integration switching | Production API (REST, OData, SOAP) | HTTPS/JSON/XML | Live integration endpoints | Production rate limits | Yes |
| Post-cutover validation | Reporting API + direct queries | SQL, OData, REST | Reconciliation | N/A | No |

## Rate Limits & Quotas

### Migration Tool Limits (Different from Production API Limits)

| ERP System | Migration Tool | Batch Size Limit | Concurrency | Throttle Behavior | Notes |
|---|---|---|---|---|---|
| SAP S/4HANA | Migration Cockpit | 50,000 records per migration object | Sequential (1 object at a time) | Queue-based | Staging table approach — no API rate limits |
| D365 F&O | DMF/DIXF | 500,000 records per data package | Up to 5 parallel imports | Batch framework throttle | Entity-dependent; some entities slower |
| Oracle ERP Cloud | FBDI | 250 MB per file, unlimited records | 3 concurrent imports per module | ESS job queue | Schedule imports during off-peak hours |
| NetSuite | CSV Import | 25,000 records per import | 1 import per record type | Governance-limited | SuiteCloud sandbox limits lower |

[src1, src6]

### Cutover Window Time Budgets

| Activity | Typical Duration | Parallelizable? | Risk if Delayed |
|---|---|---|---|
| System lockdown (freeze legacy) | 1-2 hours | No (gate) | Users transact in legacy during migration |
| Master data final delta | 2-4 hours | Partially | Stale customer/vendor records |
| Open transaction migration | 4-8 hours | Yes (by module) | Missing POs, SOs, invoices |
| Integration endpoint switching | 2-4 hours | Yes (by integration) | Data flows to wrong system |
| Validation & reconciliation | 4-8 hours | Yes (by domain) | Undetected data loss |
| Go/no-go decision | 1 hour | No (gate) | Delayed rollback decision |
| **Total typical cutover** | **24-48 hours** | — | — |

[src1, src3]

## Authentication

### Pre-Cutover Credential Provisioning

Every integration that currently authenticates against the legacy ERP must have equivalent credentials provisioned in the new ERP before cutover begins: [src2, src3]

| Credential Type | Legacy ERP | New ERP Action Required | Lead Time |
|---|---|---|---|
| Service account (username/password) | Direct login | Create integration user with equivalent roles | 2-4 weeks |
| OAuth 2.0 client credentials | Client ID/secret registered | Register new OAuth app, configure scopes | 2-4 weeks |
| API key / token | Static key | Generate new key, whitelist IPs | 1-2 weeks |
| Certificate-based (mTLS, JWT) | PKI cert enrolled | Generate new cert, install in new ERP trust store | 4-6 weeks |
| SSO/SAML federation | IdP configured | Add new ERP as SP in IdP, test assertion mapping | 3-4 weeks |

### Authentication Gotchas

- **Credential provisioning is the most commonly missed cutover task**: Teams focus on data migration and forget that 30+ integrations each need new credentials. Start provisioning 4-6 weeks before cutover. [src3]
- **OAuth scopes differ between ERP versions**: The legacy ERP may have used broad API scopes. The new ERP may require granular scopes. Map each integration's actual API usage to the new scope model. [src2]
- **Test credentials expire**: Credentials created in sandbox/test environments during dress rehearsals may expire before production cutover. Regenerate production credentials separately. [src6]

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **Cutover window is finite and non-negotiable**: Most enterprises negotiate a 24-72 hour window with the business. Every migration task must fit within this window. Tasks that can be pre-staged (master data, archived data) should be moved out of the cutover window entirely. [src1]
- **No partial integration switching**: Each integration interface must point entirely to the old ERP or entirely to the new ERP. Splitting an integration across both systems creates duplicate records, missing transactions, and unresolvable conflicts. [src2]
- **Rollback window closes once users transact**: After go-live, every transaction in the new ERP must be manually reversed or re-entered in the legacy system if you rollback. Plan a point-of-no-return decision gate within the first 4-24 hours of go-live. [src3]
- **Audit trail continuity is a legal requirement**: In regulated industries (SOX, GDPR, HIPAA), historical transactions must remain accessible with full audit trails. Archival must preserve document linkages, approval chains, and timestamps. [src4]
- **Parallel run doubles everything**: Two systems, two sets of integrations, two sets of data entry, two reconciliation processes. Only use parallel run for mission-critical financial processes. [src5]
- **Migration tool batch sizes differ from production API limits**: SAP Migration Cockpit uses staging tables (no API rate limits), but DMF uses data entities with batch framework throttling. Do not assume migration throughput equals production API throughput. [src6]

## Integration Pattern Decision Tree

```
START — ERP migration with integrations to cut over
├── How many integrations connect to the legacy ERP?
│   ├── < 10 integrations
│   │   └── Direct endpoint switching (manual reconfiguration during cutover)
│   ├── 10-50 integrations
│   │   └── Middleware abstraction layer (route through iPaaS, switch target)
│   └── > 50 integrations
│       └── DNS/load-balancer switching + middleware routing (mandatory)
├── What cutover strategy?
│   ├── Big-bang (single cutover weekend)
│   │   ├── All integrations switch simultaneously
│   │   ├── Requires: 2-3 dress rehearsals, detailed runbook, rollback plan
│   │   ├── Data volume < 50M records? → Feasible in 48h window
│   │   └── Data volume > 50M records? → Pre-stage historical data, cutover only delta
│   ├── Phased (module-by-module or location-by-location)
│   │   ├── Integrations switch per module/location
│   │   ├── Requires: cross-system bridge for modules in different ERPs
│   │   ├── Higher complexity but lower risk per phase
│   │   └── Budget 3-6 months total migration timeline
│   └── Parallel run (both systems live)
│       ├── All integrations run against BOTH systems
│       ├── Requires: 2x integration capacity, daily reconciliation
│       ├── Duration: 1-4 weeks
│       └── Only for mission-critical financial processes (GL, AR, AP)
├── What to do with historical data?
│   ├── Active master data (customers, vendors, items, COA)
│   │   └── MIGRATE — cleanse and load into new ERP
│   ├── Open transactions (open POs, SOs, unpaid invoices)
│   │   └── MIGRATE — load as opening balances or open documents
│   ├── Closed transactions < retention period (3-7 years)
│   │   └── ARCHIVE — store in data warehouse, data lake, or archival system
│   ├── Closed transactions > retention period
│   │   └── EVALUATE — archive if cheap, abandon if no legal requirement
│   └── Obsolete/duplicate records
│       └── ABANDON — cleanse out before migration
└── Rollback plan?
    ├── Before point-of-no-return → Restore legacy from backup, revert integrations
    ├── After users transact in new system → Manual reversal (costly, slow)
    └── No rollback plan? → STOP. Do not proceed without one.
```

## Quick Reference

### Cutover Checklist

| Phase | Task | Owner | Duration | Dependencies | Validation |
|---|---|---|---|---|---|
| T-6 weeks | Provision integration credentials in new ERP | Integration lead | Ongoing | New ERP environments ready | Test each credential individually |
| T-4 weeks | First dress rehearsal (full cutover simulation) | Migration lead | 48h | Test environment ready | Compare record counts + balances |
| T-3 weeks | Archive historical data to data warehouse | Data architect | 1-2 weeks | Archival infrastructure ready | Spot-check 100 random records |
| T-2 weeks | Second dress rehearsal | Migration lead | 48h | First rehearsal issues resolved | < 5 open defects |
| T-1 week | Final dress rehearsal + timing confirmation | Migration lead | 48h | All defects resolved | Cutover fits within window |
| T-0 (Friday PM) | System lockdown — freeze legacy ERP | ERP admin | 1h | Business confirmation | No new transactions after freeze |
| T+1h | Extract delta data (transactions since last load) | Data engineer | 2-4h | System frozen | Delta record count matches expected |
| T+3h | Load delta into new ERP | Data engineer | 4-8h | Delta extract complete | Load success rate > 99.9% |
| T+8h | Switch integration endpoints | Integration lead | 2-4h | Data load validated | Each integration sends test message |
| T+12h | End-to-end integration smoke test | QA lead | 2-4h | Integrations switched | All critical paths pass |
| T+16h | Reconciliation — compare legacy vs new | Finance / data team | 4-8h | Smoke test passed | Balances match within tolerance |
| T+24h | Go/no-go decision gate | Steering committee | 1h | Reconciliation complete | Pass: open to users. Fail: rollback |
| T+48h | Monitor and stabilize | Support team | Ongoing | Go-live confirmed | Issue count trending down |

[src1, src3, src5]

### Data Archival Decision Matrix

| Data Category | Example Records | Migrate? | Archive? | Abandon? | Rationale |
|---|---|---|---|---|---|
| Active master data | Current customers, vendors, items, GL accounts | Yes | No | No | Required for day-1 operations |
| Open transactions | Unpaid invoices, open POs, pending SOs | Yes | No | No | Must continue processing |
| GL balances | Trial balance as of cutover date | Yes (as opening balances) | No | No | Financial continuity |
| Closed transactions < 3 years | Paid invoices, fulfilled orders 2023-2025 | No | Yes | No | SOX/audit requirement |
| Closed transactions 3-7 years | Historical financials 2019-2022 | No | Yes | No | Tax/regulatory retention |
| Closed transactions > 7 years | Pre-2019 historical data | No | Evaluate | Maybe | Depends on industry retention rules |
| Duplicate/obsolete records | Merged customer records, test data, orphans | No | No | Yes | No business or legal value |
| Attachments & documents | PDFs, images, scanned documents | No | Yes (link) | No | Store in DMS, link from archive |

[src4, src7]

## Step-by-Step Integration Guide

### 1. Inventory all integrations and classify by criticality

Document every integration that touches the legacy ERP. For each, record: source system, target system, data flow direction, frequency, data volume, and business criticality (P1/P2/P3). [src3]

```bash
# Example: integration inventory template (CSV)
cat > integration_inventory.csv << 'EOF'
integration_id,name,source_system,target_system,direction,frequency,daily_volume,protocol,criticality,cutover_strategy
INT-001,Order sync,Shopify,Legacy ERP,inbound,real-time,5000,REST,P1,switch at cutover
INT-002,GL posting,Legacy ERP,Data warehouse,outbound,nightly,50000,SFTP/CSV,P2,dual-run 1 week
INT-003,Inventory sync,WMS,Legacy ERP,bidirectional,every 15min,20000,SOAP,P1,switch at cutover
INT-004,Payroll export,Legacy ERP,ADP,outbound,biweekly,2000,SFTP/CSV,P1,switch after first payroll
INT-005,CRM sync,Salesforce,Legacy ERP,bidirectional,real-time,10000,REST,P2,phased after cutover
EOF
```

**Verify**: `wc -l integration_inventory.csv` → count should match known integration count from legacy ERP documentation.

### 2. Classify data into migrate/archive/abandon

Run data profiling against the legacy ERP to categorize every table and record set: [src4]

```sql
-- Data classification query: identify active vs historical records
-- Run against legacy ERP database

-- Master data: always migrate
SELECT 'MIGRATE' as action, 'Customers' as entity,
       COUNT(*) as total_records,
       SUM(CASE WHEN last_transaction_date > DATEADD(year, -2, GETDATE()) THEN 1 ELSE 0 END) as active,
       SUM(CASE WHEN last_transaction_date <= DATEADD(year, -2, GETDATE()) THEN 1 ELSE 0 END) as inactive
FROM customers;

-- Transactions: split by age
SELECT 'ARCHIVE' as action, 'Invoices' as entity,
       MIN(invoice_date) as oldest,
       MAX(invoice_date) as newest,
       COUNT(*) as total_records,
       SUM(CASE WHEN status = 'OPEN' THEN 1 ELSE 0 END) as open_to_migrate,
       SUM(CASE WHEN status = 'CLOSED' AND invoice_date > DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END) as archive_within_retention,
       SUM(CASE WHEN status = 'CLOSED' AND invoice_date <= DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END) as abandon_candidates
FROM invoices;

-- Duplicates: identify and flag for abandonment
SELECT 'ABANDON' as action, 'Duplicate customers' as entity, COUNT(*) as count
FROM (
  SELECT customer_name, tax_id, COUNT(*) as cnt
  FROM customers
  GROUP BY customer_name, tax_id
  HAVING COUNT(*) > 1
) dupes;
```

**Verify**: Sum of migrate + archive + abandon should equal total legacy record count per entity.

### 3. Set up middleware abstraction layer for integration switching

Configure your iPaaS or middleware to route integrations through a switchable endpoint. This allows cutover without reconfiguring each source/target system: [src2]

```javascript
// integration-router.js — Middleware routing configuration
// Supports instant switching between legacy and new ERP endpoints

const INTEGRATION_ROUTES = {
  'order-sync': {
    legacy: {
      url: 'https://legacy-erp.company.com/api/v1/orders',
      auth: { type: 'basic', credentials: 'LEGACY_ORDER_CREDS' }
    },
    new_erp: {
      url: 'https://new-erp.company.com/api/v2/salesorders',
      auth: { type: 'oauth2', clientId: 'NEW_ERP_CLIENT_ID' }
    },
    active_target: 'legacy',  // Switch to 'new_erp' during cutover
    enable_dual_write: false   // Set true for parallel run
  },
  'inventory-sync': {
    legacy: {
      url: 'https://legacy-erp.company.com/api/v1/inventory',
      auth: { type: 'api_key', key: 'LEGACY_INV_KEY' }
    },
    new_erp: {
      url: 'https://new-erp.company.com/odata/v4/InventoryItems',
      auth: { type: 'oauth2', clientId: 'NEW_ERP_CLIENT_ID' }
    },
    active_target: 'legacy',
    enable_dual_write: false
  }
};

async function routeIntegration(integrationId, payload) {
  const route = INTEGRATION_ROUTES[integrationId];
  const targets = [];

  // Primary target
  targets.push(route[route.active_target]);

  // Dual-write for parallel run
  if (route.enable_dual_write) {
    const secondary = route.active_target === 'legacy' ? 'new_erp' : 'legacy';
    targets.push(route[secondary]);
  }

  const results = await Promise.allSettled(
    targets.map(target => sendToErp(target, payload))
  );

  // Log discrepancies for reconciliation during parallel run
  if (route.enable_dual_write && results.length === 2) {
    if (results[0].status !== results[1].status) {
      await logDiscrepancy(integrationId, payload, results);
    }
  }

  return results[0]; // Primary result
}

// Cutover function — called during cutover window
async function switchIntegration(integrationId, newTarget) {
  INTEGRATION_ROUTES[integrationId].active_target = newTarget;
  console.log(`[CUTOVER] ${integrationId} switched to ${newTarget}`);
  // Send test message to verify new endpoint
  const testResult = await routeIntegration(integrationId, { test: true });
  if (testResult.status !== 'fulfilled') {
    throw new Error(`Integration ${integrationId} failed post-switch test`);
  }
}
```

**Verify**: Call `switchIntegration('order-sync', 'new_erp')` in test environment → confirm test message reaches new ERP.

### 4. Execute dress rehearsal (run 2-3 times before real cutover)

The dress rehearsal simulates the entire cutover weekend. Every step, every handoff, every validation must be timed: [src1, src3]

```bash
#!/bin/bash
# dress-rehearsal.sh — Full cutover simulation
# Run in test/sandbox environment, not production

CUTOVER_LOG="cutover_rehearsal_$(date +%Y%m%d_%H%M).log"

echo "=== DRESS REHEARSAL START: $(date) ===" | tee -a "$CUTOVER_LOG"

# Phase 1: System lockdown
echo "[$(date +%H:%M)] Phase 1: Freezing legacy system..." | tee -a "$CUTOVER_LOG"
# Disable all write access to legacy ERP (except migration service account)
# Document: who froze, when, confirmation method

# Phase 2: Delta data extraction
echo "[$(date +%H:%M)] Phase 2: Extracting delta data..." | tee -a "$CUTOVER_LOG"
DELTA_START=$(date +%s)
# Run extraction scripts per module
# Record: record counts per entity, extraction duration
DELTA_END=$(date +%s)
echo "Delta extraction took $((DELTA_END - DELTA_START)) seconds" | tee -a "$CUTOVER_LOG"

# Phase 3: Delta load into new ERP
echo "[$(date +%H:%M)] Phase 3: Loading delta into new ERP..." | tee -a "$CUTOVER_LOG"
LOAD_START=$(date +%s)
# Run migration tool loads
# SAP: Migration Cockpit staging tables
# D365: DMF data package import
# Oracle: FBDI scheduled import
# NetSuite: CSV Import Assistant
LOAD_END=$(date +%s)
echo "Delta load took $((LOAD_END - LOAD_START)) seconds" | tee -a "$CUTOVER_LOG"

# Phase 4: Integration switching
echo "[$(date +%H:%M)] Phase 4: Switching integrations..." | tee -a "$CUTOVER_LOG"
SWITCH_START=$(date +%s)
# Switch each integration per inventory
# Test each integration with smoke message
SWITCH_END=$(date +%s)
echo "Integration switching took $((SWITCH_END - SWITCH_START)) seconds" | tee -a "$CUTOVER_LOG"

# Phase 5: Validation
echo "[$(date +%H:%M)] Phase 5: Running validation..." | tee -a "$CUTOVER_LOG"
# Compare record counts: legacy vs new
# Compare financial balances: legacy GL vs new GL
# Run critical business process end-to-end

echo "=== DRESS REHEARSAL COMPLETE: $(date) ===" | tee -a "$CUTOVER_LOG"
echo "Review $CUTOVER_LOG for timing and issues"
```

**Verify**: Total rehearsal duration fits within cutover window with 25% buffer. If not, identify bottlenecks and optimize.

### 5. Execute production cutover and validate

On cutover day, follow the rehearsed runbook exactly. Key additions beyond rehearsal: [src1, src5]

```python
# Input:  Legacy and new ERP database connections
# Output: Reconciliation report (pass/fail per entity)

import psycopg2
from datetime import datetime

def reconcile_migration(legacy_conn, new_erp_conn, entities):
    """Post-cutover reconciliation: compare legacy vs new ERP."""
    report = []
    all_passed = True

    for entity in entities:
        legacy_count = query_count(legacy_conn, entity['legacy_table'], entity.get('filter'))
        new_count = query_count(new_erp_conn, entity['new_table'], entity.get('filter'))

        # Allow tolerance for known exclusions (abandoned records)
        tolerance = entity.get('tolerance', 0)
        diff = abs(legacy_count - new_count)
        passed = diff <= tolerance

        if not passed:
            all_passed = False

        report.append({
            'entity': entity['name'],
            'legacy_count': legacy_count,
            'new_count': new_count,
            'difference': diff,
            'tolerance': tolerance,
            'status': 'PASS' if passed else 'FAIL'
        })

    # Financial balance reconciliation
    legacy_tb = query_trial_balance(legacy_conn)
    new_tb = query_trial_balance(new_erp_conn)
    balance_diff = abs(legacy_tb - new_tb)
    report.append({
        'entity': 'Trial Balance',
        'legacy_count': legacy_tb,
        'new_count': new_tb,
        'difference': balance_diff,
        'tolerance': 0.01,  # Penny tolerance for rounding
        'status': 'PASS' if balance_diff <= 0.01 else 'FAIL'
    })

    return {'timestamp': datetime.now().isoformat(), 'all_passed': all_passed, 'details': report}

def query_count(conn, table, filter_clause=None):
    query = f"SELECT COUNT(*) FROM {table}"
    if filter_clause:
        query += f" WHERE {filter_clause}"
    cur = conn.cursor()
    cur.execute(query)
    return cur.fetchone()[0]
```

**Verify**: `reconcile_migration()` returns `all_passed: True` for every entity. Any `FAIL` triggers investigation before go/no-go decision.

## Code Examples

### Python: Integration Endpoint Switcher with Health Check

```python
# Input:  Integration inventory (JSON), new ERP endpoint config
# Output: Switch confirmation with health check results per integration

import httpx
import asyncio
from dataclasses import dataclass

@dataclass
class IntegrationConfig:
    id: str
    name: str
    legacy_url: str
    new_url: str
    health_check_path: str
    auth_header: dict

async def switch_and_verify(config: IntegrationConfig, client: httpx.AsyncClient) -> dict:
    """Switch one integration and verify new endpoint responds."""
    # Step 1: Health check new endpoint BEFORE switching
    try:
        health = await client.get(
            f"{config.new_url}{config.health_check_path}",
            headers=config.auth_header,
            timeout=10.0
        )
        if health.status_code >= 400:
            return {'id': config.id, 'status': 'BLOCKED', 'reason': f'Health check failed: {health.status_code}'}
    except httpx.TimeoutException:
        return {'id': config.id, 'status': 'BLOCKED', 'reason': 'New endpoint timeout'}

    # Step 2: Update routing (middleware/DNS/config)
    # Implementation depends on your middleware platform
    await update_route(config.id, config.new_url)

    # Step 3: Send test transaction through switched route
    try:
        test = await client.post(
            f"{config.new_url}/api/test",
            headers=config.auth_header,
            json={'cutover_test': True, 'timestamp': datetime.now().isoformat()},
            timeout=15.0
        )
        return {'id': config.id, 'status': 'SWITCHED', 'test_response': test.status_code}
    except Exception as e:
        # Rollback: revert to legacy endpoint
        await update_route(config.id, config.legacy_url)
        return {'id': config.id, 'status': 'ROLLED_BACK', 'reason': str(e)}

async def cutover_all_integrations(integrations: list[IntegrationConfig]):
    """Switch all integrations with concurrent health checks."""
    async with httpx.AsyncClient() as client:
        results = await asyncio.gather(
            *[switch_and_verify(i, client) for i in integrations]
        )
    failed = [r for r in results if r['status'] != 'SWITCHED']
    if failed:
        print(f"WARNING: {len(failed)} integrations failed cutover: {failed}")
    return results
```

### SQL: Data Archival Pre-Migration Query

```sql
-- Input:  Legacy ERP database access
-- Output: Archival candidates with volume and retention status

-- Step 1: Generate archival manifest
WITH data_age AS (
  SELECT
    'invoices' AS entity,
    COUNT(*) AS total_records,
    SUM(CASE WHEN status = 'OPEN' THEN 1 ELSE 0 END) AS migrate_count,
    SUM(CASE WHEN status = 'CLOSED'
      AND invoice_date > DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END) AS archive_count,
    SUM(CASE WHEN status = 'CLOSED'
      AND invoice_date <= DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END) AS abandon_count,
    MIN(invoice_date) AS oldest_record,
    SUM(DATALENGTH(attachment_blob)) / 1048576.0 AS attachment_size_mb
  FROM invoices
  UNION ALL
  SELECT
    'purchase_orders', COUNT(*),
    SUM(CASE WHEN status IN ('OPEN','PARTIAL') THEN 1 ELSE 0 END),
    SUM(CASE WHEN status = 'CLOSED'
      AND order_date > DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END),
    SUM(CASE WHEN status = 'CLOSED'
      AND order_date <= DATEADD(year, -7, GETDATE()) THEN 1 ELSE 0 END),
    MIN(order_date), 0
  FROM purchase_orders
)
SELECT entity, total_records, migrate_count, archive_count, abandon_count,
       oldest_record, attachment_size_mb,
       ROUND(100.0 * (archive_count + abandon_count) / NULLIF(total_records, 0), 1) AS pct_not_migrated
FROM data_age
ORDER BY total_records DESC;

-- Expected: 60-80% of records are archive or abandon candidates
-- This directly reduces cutover window duration
```

### Bash: SAP Migration Cockpit Delta Load

```bash
# Input:  SAP S/4HANA system with Migration Cockpit configured
# Output: Delta data loaded into staging tables

# Step 1: Extract delta from legacy SAP (transactions since last full load)
# Uses DMIS (Data Migration Server) for CDC-based delta extraction

# Export delta using SAP standard programs
# Transaction: LTMOM (Migration Object Modeler)
# Select migration objects: CUSTOMER, VENDOR, GL_ACCOUNT, SALES_ORDER, PURCHASE_ORDER

# Step 2: Upload staging files to Migration Cockpit
# Format: XML or CSV per migration object template

curl -X POST "https://s4hana.company.com/sap/opu/odata/sap/API_MIG_OBJ_STAGING_SRV/StagingFiles" \
  -H "Authorization: Bearer $SAP_TOKEN" \
  -H "x-csrf-token: $CSRF_TOKEN" \
  -H "Content-Type: application/octet-stream" \
  -H "slug: CUSTOMER_DELTA_20260307.xml" \
  --data-binary @customer_delta.xml

# Step 3: Trigger migration run
curl -X POST "https://s4hana.company.com/sap/opu/odata/sap/API_MIG_OBJ_STAGING_SRV/MigrationRuns" \
  -H "Authorization: Bearer $SAP_TOKEN" \
  -H "x-csrf-token: $CSRF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"MigrationObject": "CUSTOMER", "RunMode": "SIMULATE"}'

# Step 4: Check for errors before switching to MIGRATE mode
# RunMode: SIMULATE → validates without committing
# RunMode: MIGRATE → commits to target tables
```

## Data Mapping

### Cutover Data Flow Mapping

| Data Flow | Source (Legacy) | Action During Cutover | Target (New ERP) | Validation |
|---|---|---|---|---|
| Master data (final delta) | Legacy DB extract | Cleanse + transform + load | New ERP master tables | Record count + field sampling |
| Open POs | Legacy PO tables | Map to new PO format + load | New ERP PO module | Open PO count + total value match |
| Open invoices | Legacy AR/AP tables | Convert to opening balances | New ERP sub-ledger | Balance match to penny |
| GL trial balance | Legacy GL | Load as opening balance journal | New ERP GL | Total debits = total credits |
| Integration config | Legacy endpoints | Remap to new ERP endpoints | Middleware routing table | Smoke test per integration |
| User access/roles | Legacy user directory | Map to new ERP role model | New ERP IAM | User count + permission audit |

### Data Type Gotchas

- **Chart of accounts restructuring**: If the new ERP uses a different COA structure, every historical transaction must be re-mapped to new account codes. This mapping must be defined before archival — archived data needs the mapping table for future queries. [src4]
- **Currency amounts with different precision**: Legacy systems may store amounts with 2 decimal places. New ERPs (especially SAP) may use 3+ decimals or store in smallest unit (cents). Rounding differences during conversion create reconciliation failures. [src6]
- **Timezone handling during cutover**: If legacy and new ERP are in different timezones (or handle UTC differently), transactions near the cutover timestamp may appear to have incorrect dates. Normalize all timestamps to UTC before migration. [src1]
- **Document numbering gaps**: The new ERP may start document numbering from a different sequence. Integrations that reference legacy document numbers (e.g., PO numbers in supplier EDI) must maintain a cross-reference lookup. [src3]
- **Multi-language/multi-currency descriptions**: Legacy systems may store translated descriptions differently than the new ERP's localization model. Bulk text field migration often truncates or corrupts non-ASCII characters. [src7]

## Error Handling & Failure Points

### Common Cutover Errors

| Error | Meaning | Cause | Resolution |
|---|---|---|---|
| Migration load timeout | Data package import hangs | File too large, lock contention | Split into smaller batches; check for blocking processes |
| Duplicate key violation | Record already exists in target | Delta extract includes records from previous load | Use upsert logic or pre-check for existence before insert |
| Foreign key constraint failure | Dependent record missing | Load order incorrect (transactions before masters) | Enforce load sequence: masters first, then transactions |
| Integration auth failure post-switch | 401/403 from new ERP | Credentials not provisioned or expired | Verify credential provisioning checklist; regenerate tokens |
| Data type mismatch | Field value rejected by target | Source format incompatible with target schema | Add transform step in ETL pipeline; validate before load |
| Rate limit exceeded during bulk load | 429 from new ERP API | Loading too fast via API endpoints | Use vendor migration tools (staging tables) instead of API |
| Sequence number collision | New ERP generates conflicting IDs | ID range overlap between migrated and new records | Configure new ERP to start sequences above migrated max |

[src1, src3, src6]

### Failure Points in Production

- **Delta data loss during freeze window**: Transactions created between the last extract and system freeze are lost. Fix: `Implement a tight freeze-extract-validate loop: freeze → extract delta → count records → validate all transactions captured before proceeding.` [src1]
- **Integration switch-back fails on rollback**: Reverting integrations to legacy endpoints works technically, but messages queued during the cutover window are lost. Fix: `Queue all messages in middleware dead-letter queue during cutover; replay on rollback.` [src2]
- **Dress rehearsal timing does not match production**: Production has more data, slower networks, and concurrent user activity. Fix: `Use full production-volume data in dress rehearsals; run during representative business hours if possible.` [src3]
- **Archived data inaccessible after legacy decommission**: Archive stored in legacy system format without query interface. Fix: `Deploy a read-only archival system (data warehouse, Archon, SAP ILM) with query access before decommissioning legacy.` [src4]
- **Audit trail broken at cutover boundary**: Auditors cannot trace a transaction from legacy to new ERP. Fix: `Maintain a cross-reference table (legacy_doc_id → new_doc_id) and include it in the archival system.` [src4]
- **Parallel run reconciliation overwhelms team**: Daily reconciliation of two systems produces hundreds of discrepancies, most of which are timing differences. Fix: `Define reconciliation tolerance bands; auto-dismiss timing differences < 24h; focus manual effort on amount discrepancies.` [src5]

## Anti-Patterns

### Wrong: Migrating all historical data into the new ERP

```
// BAD — migrating 10+ years of closed transactions into new ERP
MIGRATE: Active customers (50,000)
MIGRATE: Active vendors (10,000)
MIGRATE: ALL invoices 2010-2025 (5,000,000)      ← 80% are closed, never accessed
MIGRATE: ALL purchase orders 2010-2025 (3,000,000) ← 75% are closed, never accessed
MIGRATE: ALL journal entries 2010-2025 (20,000,000) ← historical, not operational

Result:
- Cutover window: 96+ hours (business rejected)
- New ERP performance: degraded from day 1
- Migration effort: 6 months instead of 6 weeks
- Cost: $500K+ in migration consulting
```

### Correct: Archive historical, migrate only active + open

```
// GOOD — archive first, migrate lean
MIGRATE: Active customers (50,000)
MIGRATE: Active vendors (10,000)
MIGRATE: Open invoices only (15,000)
MIGRATE: Open POs only (8,000)
MIGRATE: Trial balance as opening journal (1 entry per GL account)
ARCHIVE: Closed transactions 2019-2025 → data warehouse (retention compliant)
ABANDON: Closed transactions pre-2019 + duplicates (after legal review)

Result:
- Cutover window: 36 hours (fits weekend)
- New ERP performance: optimal from day 1
- Migration effort: 6 weeks
- Historical data accessible via archive query tool
```

[src4, src7]

### Wrong: No parallel run or validation for financial integrations

```
// BAD — switch GL integration without verification
Friday 8 PM:  Switch GL posting integration from legacy to new ERP
Saturday 8 AM: "Looks fine" — no validation
Monday 8 AM:  Finance discovers GL out of balance by $2.3M
Monday 5 PM:  Root cause: currency conversion logic different in new ERP
Week 2:       Manual journal entries to correct — auditors flagged

// No reconciliation, no parallel run, no smoke test
```

### Correct: Parallel run financial integrations with daily reconciliation

```
// GOOD — parallel run GL integration for 1-2 weeks
Week 1: GL integration posts to BOTH legacy and new ERP
         Daily reconciliation: compare balances per GL account
         Investigate and fix discrepancies (currency, rounding, mapping)
Week 2: Discrepancies < 0.01% tolerance for 3 consecutive days
         → Deactivate legacy GL integration
         → New ERP is sole system of record

// Cost: 2 weeks of dual effort
// Benefit: $0 in financial restatements
```

[src3, src5]

### Wrong: Switching integrations without a middleware abstraction layer

```
// BAD — reconfiguring each source system individually during cutover
Cutover hour 1: SSH into Shopify app → change ERP endpoint → restart
Cutover hour 2: SSH into WMS server → update config file → restart
Cutover hour 3: Log into Salesforce → update named credential → restart
Cutover hour 4: Edit EDI translator → change partner profile → restart
...
Cutover hour 12: Discover WMS config typo → WMS sending to wrong endpoint for 8 hours
                 → 8 hours of inventory data lost
```

### Correct: Route all integrations through middleware with single-point switching

```
// GOOD — middleware handles routing; source systems unchanged
Pre-cutover: All integrations already route through middleware (MuleSoft, Boomi, etc.)
             Source systems point to middleware endpoints (unchanged during cutover)
             Middleware routes to legacy ERP

Cutover: Middleware config change: target = new ERP (per integration)
         Each switch: < 1 minute
         Each switch: includes automated health check
         Rollback: change middleware config back (< 1 minute per integration)

// Source systems never change configuration
// Cutover is a middleware-only operation
// Rollback is instant
```

[src2]

## Common Pitfalls

- **Skipping dress rehearsals or doing only one**: Production cutover always reveals issues not found in a single test. The first rehearsal identifies process gaps, the second tests fixes, the third validates timing. Fix: `Mandate 2-3 full dress rehearsals in production-equivalent environment. Time every step.` [src1, src3]
- **Underestimating delta data volume**: Between the last pre-load and system freeze, business continues. A 2-week gap can produce 100K+ new transactions that must be extracted and loaded during the cutover window. Fix: `Run pre-loads as close to cutover as possible. Calculate expected delta volume from daily transaction rates.` [src1]
- **Forgetting timezone conversion during cutover weekend**: Cutover teams in different timezones report timestamps inconsistently. A 5 PM freeze in New York is different from 5 PM in Singapore. Fix: `Use UTC for all cutover timestamps. Include timezone in every log entry and communication.` [src3]
- **No defined point-of-no-return**: Teams discover issues Monday morning but have no decision framework for whether to rollback or fix-forward. Fix: `Define rollback criteria (severity, financial impact, user impact) before cutover. Decision gate at T+24h with steering committee.` [src5]
- **Legacy system decommissioned before archival access verified**: IT decommissions legacy servers to save hosting costs, then finance discovers they need 2019 tax records. Fix: `Do not decommission legacy system until archival query access is verified by all stakeholders. Minimum 90-day verification period.` [src4]
- **Sequence number gaps confuse downstream systems**: New ERP starts invoice numbering at 1 or at a configured offset. Downstream systems (EDI partners, tax authorities) may reject gaps in sequence. Fix: `Configure new ERP to start sequences above legacy max + buffer (e.g., legacy max SO = 500000, new ERP starts at 600000).` [src6]
- **Ignoring integration message ordering during cutover**: Messages queued in middleware during the switch arrive out of order at the new ERP. Fix: `Implement message ordering guarantees (FIFO queues) or design integrations to handle out-of-order processing via timestamps.` [src2]
- **Testing individual integrations but not end-to-end flows**: Each integration passes its smoke test, but the end-to-end business process (order → fulfillment → invoice → payment) fails because of data mapping inconsistencies between integrations. Fix: `Run end-to-end business process tests during dress rehearsal, not just integration unit tests.` [src3]

## Diagnostic Commands

```bash
# Compare record counts between legacy and new ERP (post-cutover validation)
# Adjust SQL syntax for your specific databases
psql -h legacy-erp -d erp -c "SELECT 'customers' AS entity, COUNT(*) FROM customers WHERE status='ACTIVE'" \
  && psql -h new-erp -d erp -c "SELECT 'customers' AS entity, COUNT(*) FROM bp_master WHERE bp_type='CUSTOMER' AND status='ACTIVE'"

# Check D365 DMF import job status
curl -s "https://d365-env.operations.dynamics.com/data/DataManagementJobs" \
  -H "Authorization: Bearer $D365_TOKEN" | jq '.value[] | {jobId: .JobId, status: .Status, entity: .Entity}'

# Check SAP Migration Cockpit run status
curl -s "https://s4hana.company.com/sap/opu/odata/sap/API_MIG_OBJ_STAGING_SRV/MigrationRuns?\$filter=RunStatus eq 'ERROR'" \
  -H "Authorization: Bearer $SAP_TOKEN" | jq '.d.results[] | {object: .MigrationObject, status: .RunStatus, errors: .ErrorCount}'

# Monitor integration message flow post-cutover (middleware health)
# MuleSoft example
curl -s "https://anypoint.mulesoft.com/cloudhub/api/v2/applications" \
  -H "Authorization: Bearer $MULESOFT_TOKEN" | jq '.[] | {name: .domain, status: .status, workers: .workers.type.weight}'

# Check NetSuite CSV import status
curl -s "https://ACCOUNT.suitetalk.api.netsuite.com/services/rest/record/v1/csvImport" \
  -H "Authorization: OAuth $NETSUITE_AUTH" | jq '.items[] | {id, status, recordsProcessed, recordsFailed}'

# Validate trial balance match post-cutover
psql -h legacy-erp -d erp -c "SELECT SUM(debit) as total_debit, SUM(credit) as total_credit FROM gl_entries WHERE period <= '2026-03'" \
  && psql -h new-erp -d erp -c "SELECT SUM(debit) as total_debit, SUM(credit) as total_credit FROM gl_journal_entries WHERE entry_type = 'OPENING_BALANCE'"
```

## Version History & Compatibility

### Per-ERP Migration Tool Versions

| ERP System | Migration Tool | Current Version | Status | Key Capability | Notes |
|---|---|---|---|---|---|
| SAP S/4HANA | Migration Cockpit | 2408 (2024) | GA | Staging table approach, migration objects, simulate mode | Replaced LSMW for cloud migrations |
| SAP S/4HANA | DMIS (Data Migration Server) | 2011 SP29 | GA | SLT replication, CDC-based delta extraction | Used alongside Migration Cockpit |
| D365 F&O | Data Management Framework | 10.0.40+ | GA | Data packages, entity-based, composite entities | Successor to DIXF |
| D365 F&O | Dual-write | 10.0.40+ | GA | Real-time sync between F&O and Dataverse | For phased cutover with CE apps |
| Oracle ERP Cloud | FBDI | 24B (2024) | GA | File-based, CSV templates, ESS scheduler | Primary import mechanism |
| Oracle ERP Cloud | ADFdi (Application Development Framework Desktop Integrator) | 24B | GA | Excel-based data entry for corrections | Not suited for bulk migration |
| NetSuite | CSV Import Assistant | 2024.1 | GA | Record-type templates, 25K record limit | Most common migration method |
| NetSuite | SuiteCloud Data Loader | 2024.1 | GA | Bulk SOAP-based import | Higher volume than CSV |

[src1, src6, src7]

### Migration Tool Selection Guide

SAP Migration Cockpit is the recommended tool for SAP S/4HANA migrations (both greenfield and brownfield). D365 DMF provides entity-based import with composite entity support for complex data structures. Oracle FBDI uses CSV templates aligned with Oracle's interface tables. NetSuite CSV Import is simple but limited to 25K records per import — use SuiteCloud Data Loader for larger volumes. All tools are actively maintained and receive updates with each ERP release.

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Migrating from one ERP to another (or upgrading major version) | Ongoing data synchronization between two live ERPs | business/erp-integration/change-data-capture-erp/2026 |
| Need to decide what historical data to carry forward | Already decided on data scope and need API integration guidance | business/erp-integration/{system}-rest-api/2026 |
| Planning integration cutover strategy for go-live | Building new integrations from scratch (no legacy) | business/erp-integration/batch-vs-realtime-integration/2026 |
| Managing rollback risk during ERP transition | System is already live and stable | business/erp-integration/error-handling-dead-letter-queues/2026 |
| Need per-ERP migration tool guidance | Need iPaaS/middleware platform comparison | business/erp-integration/ipaas-platform-comparison/2026 |

## Cross-System Comparison

### Migration Tool Comparison

| Capability | SAP Migration Cockpit | D365 DMF | Oracle FBDI | NetSuite CSV Import | Notes |
|---|---|---|---|---|---|
| **Approach** | Staging tables + migration objects | Data entities + data packages | CSV/XML templates + ESS jobs | CSV templates + Import Assistant | SAP most structured |
| **Max batch size** | 50,000 records/object | 500,000 records/package | 250 MB/file | 25,000 records/import | D365 highest volume |
| **Simulate mode** | Yes (validate without commit) | Yes (validate only) | Yes (validate import) | Yes (preview) | All support dry-run |
| **Delta/incremental** | Yes (via DMIS/SLT) | Yes (change tracking) | Yes (differential FBDI) | No (full replace) | NetSuite lacks delta |
| **Custom objects** | Yes (custom migration objects) | Yes (custom entities) | Yes (custom FBDI templates) | Limited (custom records) | Varies by complexity |
| **Error handling** | Per-record error log | Per-record exception handling | ESS job log + error rows | Error CSV download | All provide record-level errors |
| **Rollback support** | Delete imported records (manual) | Delete via DMF | No built-in rollback | No built-in rollback | Plan rollback externally |
| **Learning curve** | High (ABAP knowledge helps) | Medium (entity model knowledge) | Medium (FBDI template knowledge) | Low (CSV editing) | NetSuite easiest |

[src1, src6, src7]

## Important Caveats

- **This card describes patterns, not product-specific step-by-step procedures**: Each ERP vendor's migration tool has its own detailed documentation. Use this card for strategy and decision-making, then consult vendor docs for implementation. [src1, src6]
- **Cutover timing is business-dependent**: A 48-hour weekend window works for many enterprises, but retail companies may need holiday-adjacent cutover (January), manufacturing may need plant shutdown periods, and 24/7 operations may have no acceptable downtime window. [src3]
- **Regulatory requirements vary by industry and jurisdiction**: SOX requires 7 years of financial records. GDPR requires deletion capability. HIPAA requires specific access controls on archived health data. Consult legal/compliance before defining archival scope. [src4]
- **Migration consulting costs are separate from ERP licensing**: Budget $150-500K for migration-specific consulting for mid-size enterprises. Large multi-country migrations can exceed $1M in migration effort alone. [src7]
- **Parallel run is the gold standard but is often infeasible**: The 2x effort and 2x integration capacity required for parallel run is only justified for mission-critical financial processes. Most organizations use big-bang or phased for non-financial modules. [src5]
- **This card reflects tooling and practices current as of March 2026**: Migration tools receive updates with each ERP release cycle. Always verify current tool capabilities against vendor documentation.

## Related Units

- [Master data management for ERP integrations](/business/erp-integration/master-data-management-erp/2026)
- [Change data capture patterns for ERPs](/business/erp-integration/change-data-capture-erp/2026)
- [Batch vs real-time integration pattern selection](/business/erp-integration/batch-vs-realtime-integration/2026)
- [Idempotency patterns for ERP integrations](/business/erp-integration/idempotency-erp-integrations/2026)
- [Error handling and dead letter queues](/business/erp-integration/error-handling-dead-letter-queues/2026)
