17 KiB
Customer Data Migration Script
Overview
This script provides a comprehensive solution for migrating data from multiple source customer master accounts to a single destination customer account. It includes robust conflict detection, detailed preview capabilities, transaction safety, and complete migration history tracking.
Features
✅ Multiple Source Support - Migrate from multiple source customer accounts simultaneously
✅ Conflict Detection - Automatically detects username conflicts and aborts by default
✅ Detailed Preview - Visualizes all changes before execution
✅ Transaction Safety - Uses mongo_enhanced.js for robust transaction handling
✅ Admin Conversion - Optionally convert source customers to admin accounts
✅ Merge Capability - Can merge conflicting accounts when explicitly requested
✅ History Tracking - Maintains complete migration history in JSON format
✅ Comprehensive Migration - Handles all related data including:
- Sub-accounts (clients, pilots, vehicles)
- Jobs and applications
- Products and crops
- Invoices and billing data
- Costing items
Usage
Preview Migration (Recommended First Step)
Always run a preview first to see what will be migrated and detect conflicts:
# Using customer usernames (recommended - easier to read)
node scripts/migrateCustomerData.js \
--preview \
--sources acme_aviation,regional_ag \
--destination consolidated_ag
# Using customer IDs (also supported)
node scripts/migrateCustomerData.js \
--preview \
--sources SOURCE_CUSTOMER_ID1,SOURCE_CUSTOMER_ID2 \
--destination DESTINATION_CUSTOMER_ID
# Mixed: usernames and IDs can be combined
node scripts/migrateCustomerData.js \
--preview \
--sources acme_aviation,507f1f77bcf86cd799439012 \
--destination consolidated_ag
# With custom environment file (e.g., production)
node scripts/migrateCustomerData.js \
--env ../environment_prod.env \
--preview \
--sources acme_aviation,regional_ag \
--destination consolidated_ag
Execute Migration
After reviewing the preview, execute the migration:
# Using usernames (source customers converted to admin by default)
node scripts/migrateCustomerData.js \
--sources acme_aviation,regional_ag \
--destination consolidated_ag
# Using IDs
node scripts/migrateCustomerData.js \
--sources SOURCE_CUSTOMER_ID1,SOURCE_CUSTOMER_ID2 \
--destination DESTINATION_CUSTOMER_ID
# With entity reuse to avoid duplicates
node scripts/migrateCustomerData.js \
--sources acme_aviation,regional_ag \
--destination consolidated_ag \
--reuse-existing-entities
# With production environment
node scripts/migrateCustomerData.js \
--env ../environment_prod.env \
--sources acme_aviation,regional_ag \
--destination consolidated_ag \
--reuse-existing-entities
Convert Source Customers to Admins
Source customers are converted to admin accounts by default. To disable this:
# Deactivate source customers instead of converting to admin
node scripts/migrateCustomerData.js \
--sources acme_aviation \
--destination consolidated_ag \
--deactivate-source
# Explicit admin conversion (this is the default behavior)
node scripts/migrateCustomerData.js \
--sources acme_aviation \
--destination consolidated_ag \
--convert-to-admin
Reuse Existing Entities
To avoid creating duplicate products/crops with the same names:
node scripts/migrateCustomerData.js \
--sources acme_aviation \
--destination consolidated_ag \
--reuse-existing-entities
This will:
- Compare product/crop names between source and destination
- Reuse destination entities when names match
- Delete source entities that were matched
- Only migrate entities with unique names
Command-Line Options
| Option | Description | Required | Default |
|---|---|---|---|
--sources |
Comma-separated list of source customer IDs or usernames | ✅ Yes | - |
--destination |
Destination customer ID or username | ✅ Yes | - |
--preview |
Show migration plan without executing | No | false |
--convert-to-admin |
Convert source customers to admin accounts | No | true |
--deactivate-source |
Deactivate source customers instead of converting to admin | No | false |
--reuse-existing-entities |
Reuse destination's products/crops with matching names | No | false |
--skip-invoices |
Don't migrate invoice references | No | false |
--output-file |
Custom output file path | No | ./migration_history.json |
--env |
Custom environment file path | No | ../environment.env |
--help, -h |
Show help message | No | - |
Note: You can use either customer usernames (e.g., acme_aviation) or MongoDB ObjectIds (e.g., 507f1f77bcf86cd799439011). Usernames are recommended as they're easier to read and remember.
Migration Process
1. Validation Phase
The script first validates:
- All source customer IDs exist
- None of the source customers are already deleted
- Destination customer exists and is active
- Gathers statistics for all customers
2. Conflict Detection Phase
The script checks for username conflicts between:
- Source clients vs destination clients
- Source pilots vs destination pilots
- Source vehicles vs destination vehicles
Default Behavior: If conflicts are found, the migration ABORTS immediately.
With --merge-conflicts: Conflicting accounts are merged:
- References updated to point to existing accounts
- Old accounts marked as deleted
- Username changed to prevent future conflicts
3. Preview Phase
Shows a detailed breakdown of:
- Source customer information and statistics
- Destination customer information
- All conflicts (if any)
- Summary of data to be migrated
- Actions that will be taken
4. Execution Phase (if not preview mode)
Within a single MongoDB transaction:
-
Convert Customer Accounts (if
--convert-to-admin)- Creates new admin user from customer data
- Marks original customer as migrated
-
Migrate Sub-Accounts
- Clients, pilots, vehicles
- Updates parent references or merges conflicts
-
Migrate Entities
- Products
- Crops
- Costing items
-
Migrate Jobs
- Updates
byPuidreferences - All related data (logs, assignments, applications) automatically follow
- Updates
-
Migrate Billing Data
- Updates invoice customer references
-
Deactivate Source Customers (unless converted to admin)
- Marks as deleted
- Changes username to prevent conflicts
5. History Logging
All migrations are logged to a JSON file with:
- Timestamp
- Source and destination IDs
- Complete statistics
- Detailed changes list
- Conflict information
- Success/failure status
- Error details (if failed)
Migration History File
The script maintains a complete history of all migrations in migration_history.json (or custom path via --output-file).
Structure
[
{
"timestamp": "2025-10-21T10:30:00.000Z",
"sourceCustomerIds": ["507f1f77bcf86cd799439011"],
"targetCustomerId": "507f191e810c19729de860ea",
"options": {
"preview": false,
"convertToAdmin": false,
"mergeConflicts": false,
"updateInvoices": true
},
"status": "completed",
"completedAt": "2025-10-21T10:30:45.000Z",
"stats": {
"sourceCustomers": { ... },
"totalJobs": 150,
"totalClients": 25,
"totalPilots": 10,
"totalVehicles": 8,
"totalProducts": 45,
"totalCrops": 12,
"totalApplications": 890,
"totalInvoices": 75,
"conflicts": [],
"skipped": [],
"errors": []
},
"details": {
"sources": [ ... ],
"conflicts": [],
"changes": [ ... ]
}
}
]
Examples
Example 1: Simple Migration (Single Source)
# Preview first (using username)
node scripts/migrateCustomerData.js \
--preview \
--sources acme_aviation \
--destination consolidated_ag
# Execute after reviewing
node scripts/migrateCustomerData.js \
--sources acme_aviation \
--destination consolidated_ag
# Alternative: Using IDs
node scripts/migrateCustomerData.js \
--sources 507f1f77bcf86cd799439011 \
--destination 507f191e810c19729de860ea
Example 2: Multiple Sources with Admin Conversion
# Using usernames (recommended)
node scripts/migrateCustomerData.js \
--sources acme_west,acme_east,acme_south \
--destination acme_corporate \
--convert-to-admin
# Using IDs
node scripts/migrateCustomerData.js \
--sources 507f1f77bcf86cd799439011,507f1f77bcf86cd799439012,507f1f77bcf86cd799439013 \
--destination 507f191e810c19729de860ea \
--convert-to-admin
Example 3: Migration with Conflict Merging
# Preview to see conflicts
node scripts/migrateCustomerData.js \
--preview \
--sources 507f1f77bcf86cd799439011 \
--destination 507f191e810c19729de860ea
# Execute with merge if conflicts are acceptable
node scripts/migrateCustomerData.js \
--sources 507f1f77bcf86cd799439011 \
--destination 507f191e810c19729de860ea \
--merge-conflicts
Example 4: Custom Output File
node scripts/migrateCustomerData.js \
--sources 507f1f77bcf86cd799439011 \
--destination 507f191e810c19729de860ea \
--output-file /path/to/custom/migration_log.json
Preview Output Example
================================================================================
MIGRATION PREVIEW
================================================================================
Source Customers:
• acme_aviation (507f1f77bcf86cd799439011)
Email: contact@acme.com
Country: US
Clients: 25
Pilots: 10
Vehicles: 8
Jobs: 150
Products: 45
Crops: 12
Applications: 890
Invoices: 75
⚠️ Will be DEACTIVATED after migration
Destination Customer:
• consolidated_ag (507f191e810c19729de860ea)
Email: admin@consolidated.com
Country: US
Migration Summary:
Total Clients: 25
Total Pilots: 10
Total Vehicles: 8
Total Jobs: 150
Total Products: 45
Total Crops: 12
Total Applications: 890
Total Invoices: 75
Preview mode - no changes made
Preview saved to: ./migration_history.json
Conflict Output Example
When conflicts are detected:
⚠️ MIGRATION ABORTED - CONFLICTS DETECTED
The following conflicts were found:
From customer: acme_aviation
• Client: john_farmer
Source ID: 507f1f77bcf86cd799439015
Conflicts with existing ID: 507f191e810c19729de86100
Email: john@farm.com
• Pilot: mike_pilot
Source ID: 507f1f77bcf86cd799439016
Conflicts with existing ID: 507f191e810c19729de86101
Email: mike@pilots.com
To proceed with merging conflicts, use --merge-conflicts flag
WARNING: Merging will link old accounts to existing ones and mark old accounts as deleted
Safety Features
1. Transaction Rollback
If any error occurs during migration, the entire transaction is rolled back. No partial migrations.
2. Validation Before Execution
- Verifies all customers exist
- Checks for deleted customers
- Validates data integrity
3. Conflict Detection
- Detects username conflicts across all sub-account types
- Aborts by default to prevent data loss
4. History Tracking
- Every migration attempt is logged
- Includes success/failure status
- Complete audit trail
5. Preview Mode
- Test migration without making changes
- See exactly what will happen
- Review conflicts before deciding
What Gets Migrated
✅ Migrated Collections
| Collection | Migration Method | Notes |
|---|---|---|
| Clients | Parent reference updated | Merged if conflicts |
| Pilots | Parent reference updated | Merged if conflicts |
| Vehicles | Parent reference updated | Merged if conflicts |
| Jobs | byPuid reference updated |
All related data follows |
| Applications | Via job relationship | No direct update needed |
| App Files | Via application relationship | No direct update needed |
| App Details | Via app file relationship | No direct update needed |
| Job Logs | Via job relationship | No direct update needed |
| Job Assigns | Via job relationship | Updated if account merge |
| Products | byPuid reference updated |
- |
| Crops | byPuid reference updated |
- |
| Costing Items | byPuid reference updated |
- |
| Invoices | Customer reference updated | Optional via --skip-invoices |
❌ Not Migrated
- Stripe subscriptions (requires manual handling)
- Bill periods
- Payment logs
- User authentication tokens
- Session data
Troubleshooting
Issue: Transaction Timeout
Symptom: Migration fails with transaction timeout error
Solution: The script uses enhanced transaction handling with 60-second timeout. For very large migrations:
- Ensure database connection is stable
- Consider migrating in smaller batches (fewer source customers at once)
Issue: Conflicts Detected
Symptom: Migration aborted with conflict list
Solutions:
- Recommended: Manually resolve conflicts by renaming accounts in source or destination
- Use
--merge-conflictsflag (USE WITH CAUTION) - Migrate source customers without conflicting accounts first
Issue: Validation Failed
Symptom: "Source or target customer not found"
Solutions:
- Verify customer IDs are correct
- Ensure customers are not already deleted
- Check database connection
Issue: Permission Denied
Symptom: MongoDB permission errors
Solutions:
- Ensure database user has write permissions
- Check that user can create transactions
- Verify user has access to all collections
Best Practices
1. Always Preview First
# ALWAYS run preview before execution
node scripts/migrateCustomerData.js --preview --sources ... --destination ...
2. Backup Database
Before running any migration, ensure you have a recent database backup.
3. Test on Non-Production First
Test the migration process on a development or staging environment first.
4. Review Conflicts Carefully
If conflicts are detected, review them carefully. Consider manually resolving rather than using --merge-conflicts.
5. Monitor History File
Regularly review migration_history.json to track all migrations.
6. Plan Stripe Subscriptions
Customer subscriptions cannot be automatically migrated and require manual intervention through Stripe.
7. Use Custom Environment Files
For production migrations, use the --env flag to specify the production environment file:
# Production migration
node scripts/migrateCustomerData.js \
--env ../environment_prod.env \
--sources acme_aviation \
--destination consolidated_ag \
--preview
# Development/testing (default)
node scripts/migrateCustomerData.js \
--sources acme_aviation \
--destination consolidated_ag \
--preview
Rollback Migrations
If you need to undo a migration, use the rollback script:
# Rollback the last migration (development environment)
node scripts/rollbackMigration.js
# Rollback with production environment
node scripts/rollbackMigration.js --env ../environment_prod.env
What Rollback Does
- ✅ Restores all migrated entities (
byPuidreferences) - ✅ Restores deleted products/crops (if entity reuse was used)
- ✅ Restores invoice references
- ✅ Restores sub-account parent references
- ✅ Restores source customer from admin back to customer
- ✅ Reactivates deactivated source customers
Rollback Limitations
- ⚠️ Can only rollback the last completed migration
- ⚠️ Must be run against the same database as the migration
- ⚠️ Cannot rollback if database has been manually modified since migration
- ⚠️ Check entities exist before restoring (handles already-rolled-back state)
Database Schema Impact
Updated Fields
User.parent- Updated for sub-accountsJob.byPuid- Updated to destination customerProduct.byPuid- Updated to destination customerCrop.byPuid- Updated to destination customerCostingItem.byPuid- Updated to destination customerInvoice.customer- Updated to destination customerJob.client/operator/vehicle- Updated if account merged
Marked for Deletion
Customer.markedDelete- Source customers (unless converted to admin)Customer.username- Suffixed with timestampUser.markedDelete- Merged sub-accountsUser.username- Suffixed with timestamp
Support and Debugging
Enable debug output:
DEBUG=agm:migrate-customer-data node scripts/migrateCustomerData.js ...
For detailed transaction debugging:
DEBUG=agm:migrate-customer-data,agm:mongo_enhanced node scripts/migrateCustomerData.js ...
Related Files
scripts/migrateCustomerData.js- Main migration scriptscripts/rollbackMigration.js- Rollback script for undoing migrationshelpers/mongo_enhanced.js- Transaction utilitieshelpers/constants.js- User types and constantsmodel/job.js- Job model withbyPuidreferencemigration_history.json- Migration history log (tracks all migrations and rollbacks)
License
Internal use only - AgMission Customer Data Management