agmission/Development/server/scripts/MIGRATION_APPROACH_UPDATED.md

6.6 KiB

Customer Migration Script - Simplified Approach (Updated)

Date: 2025-01-14

Summary of Changes

The customer migration script has been updated with a simplified approach that eliminates username conflicts by simply updating the parent references of sub-accounts instead of trying to move or merge them.

Previous Approach (Removed)

The old approach attempted to:

  1. Move sub-accounts from source to destination customer
  2. Detect username conflicts between source and destination sub-accounts
  3. Offer a --merge-conflicts flag to merge conflicting accounts
  4. Mark old accounts as deleted and update all references

Problem: Since usernames are globally unique in the system, attempting to move accounts between customers created conflicts.

New Approach (Current)

The new simplified approach:

  1. Sub-accounts stay in place with their original usernames
  2. Only the .parent field is updated to point to the destination customer
  3. No username conflicts because accounts aren't moving, just being re-parented
  4. Much simpler code with no merging logic needed

Key Changes

1. Conflict Detection Simplified

  • Before: Checked for username conflicts across all sub-account types
  • After: Only checks for circular references (migrating a customer to itself)
// Old: Complex username conflict checking
// New: Simple validation
async function detectConflicts(sourceCustomers, targetCustomer, migrationRecord) {
  const conflicts = [];
  
  // Only check for circular reference
  for (const sourceCustomer of sourceCustomers) {
    if (sourceCustomer._id.toString() === targetCustomer._id.toString()) {
      conflicts.push({
        type: 'circular_reference',
        message: `Cannot migrate customer ${sourceCustomer.username} to itself`
      });
    }
  }
  
  return conflicts;
}

2. Sub-Account Migration Rewritten

  • Before: migrateSubAccounts() with conflict map, merging logic, reference updates
  • After: updateSubAccountsParent() with simple parent field update
// Old: ~100 lines of merge/conflict handling
// New: ~40 lines of simple parent update
async function updateSubAccountsParent(sourceId, targetId, session, migrationRecord) {
  const subAccounts = await models.User.find({
    parent: sourceId,
    kind: { $in: [UserTypes.CLIENT, UserTypes.PILOT, UserTypes.DEVICE, 
                  UserTypes.OFFICER, UserTypes.INSPECTOR] }
  }).session(session);

  for (const account of subAccounts) {
    // Simply update the parent reference
    account.parent = targetId;
    await account.save({ session });
    
    // Track in migration record
    migrationRecord.details.changes.push({
      action: 'update_parent_reference',
      kind: account.kind,
      username: account.username,
      accountId: account._id,
      fromParent: sourceId,
      toParent: targetId
    });
  }
}

3. Removed Options

  • Removed: --merge-conflicts flag (no longer needed)
  • Removed: All conflict merging logic
  • Removed: Reference update functions for merged accounts

4. Documentation Updates

  • Updated header comments to explain the parent-reference approach
  • Simplified help text
  • Removed merge-related warnings and examples

Migration Behavior

What Happens During Migration

  1. Source Customer Account:

    • If --convert-to-admin flag: Converted to an admin (kind='2') under destination
    • Otherwise: Marked as deleted and inactive
  2. Sub-Accounts (Clients, Pilots, Vehicles, Officers, Inspectors):

    • Parent field updated from sourceCustomerId to targetCustomerId
    • Usernames remain unchanged (globally unique)
    • All other fields unchanged
  3. Jobs, Products, Crops:

    • byPuid field updated from sourceCustomerId to targetCustomerId
  4. Invoices (if --update-invoices flag):

    • customer field updated from sourceCustomerId to targetCustomerId

Example

Before migration:

Customer: trungh1@agnav.com (ID: 67ae...)
  ├─ Client: client1@example.com (parent: 67ae...)
  ├─ Pilot: pilot1@example.com (parent: 67ae...)
  └─ Vehicle: vehicle1 (parent: 67ae...)

Customer: trungh@agnav.com (ID: 6786...)
  └─ (existing sub-accounts)

After migration:

Customer: trungh1@agnav.com (ID: 67ae...) [DELETED]

Customer: trungh@agnav.com (ID: 6786...)
  ├─ Client: client1@example.com (parent: 6786...) ← Updated
  ├─ Pilot: pilot1@example.com (parent: 6786...)  ← Updated
  ├─ Vehicle: vehicle1 (parent: 6786...)          ← Updated
  └─ (existing sub-accounts)

Testing Results

Successfully tested with:

# Preview mode
node scripts/migrateCustomerData.js \
  --sources trungh1@agnav.com \
  --destination trungh@agnav.com \
  --preview

# Execution
node scripts/migrateCustomerData.js \
  --sources trungh1@agnav.com \
  --destination trungh@agnav.com

Results:

  • No conflicts detected
  • 2 clients migrated (parent updated)
  • 1 pilot migrated (parent updated)
  • 5 vehicles migrated (parent updated)
  • 1 job migrated (byPuid updated)
  • 1 product migrated (byPuid updated)
  • 1 crop migrated (byPuid updated)
  • Transaction completed successfully
  • Migration history saved

Benefits of New Approach

  1. Eliminates Conflicts: No username conflicts since accounts stay in place
  2. Simpler Code: Removed ~200 lines of conflict/merge logic
  3. Safer: No account deletion or reference rewiring
  4. Clearer Intent: Sub-accounts are "re-parented", not "moved"
  5. Faster Execution: No conflict checking for thousands of sub-accounts

Files Modified

  • scripts/migrateCustomerData.js:
    • Simplified detectConflicts() function
    • Replaced migrateSubAccounts() with updateSubAccountsParent()
    • Removed updateAccountReferences() function
    • Removed --merge-conflicts option parsing
    • Updated documentation strings
    • Added display name sanitization for corrupt data

Backward Compatibility

This is a breaking change if anyone was using the --merge-conflicts flag. However:

  • The new approach is the correct approach for this data model
  • Old approach would have caused data inconsistencies
  • Migration history format remains compatible
  • scripts/README_CUSTOMER_MIGRATION.md - Full documentation (needs minor update)
  • scripts/MIGRATION_QUICK_REFERENCE.md - Quick reference (needs minor update)
  • MIGRATION_SUMMARY.md - Implementation overview (needs minor update)

Next Steps

Consider updating the documentation files to reflect:

  1. The simplified conflict detection
  2. Removal of merge-conflicts flag
  3. Clarification that sub-accounts are "re-parented" not "moved"