202 lines
6.6 KiB
Markdown
202 lines
6.6 KiB
Markdown
# 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)
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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:
|
|
```bash
|
|
# 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
|
|
|
|
## Related Files
|
|
|
|
- `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"
|