438 lines
10 KiB
Markdown
438 lines
10 KiB
Markdown
# Customer Data Migration - Quick Reference
|
|
|
|
## Quick Start
|
|
|
|
### 1. Preview Migration (Always Do This First!)
|
|
|
|
```bash
|
|
# Using usernames (recommended - more readable)
|
|
node scripts/migrateCustomerData.js \
|
|
--preview \
|
|
--sources acme_aviation,regional_ag \
|
|
--destination consolidated_ag
|
|
|
|
# Using IDs (also supported)
|
|
node scripts/migrateCustomerData.js \
|
|
--preview \
|
|
--sources <SOURCE_ID1>,<SOURCE_ID2> \
|
|
--destination <DEST_ID>
|
|
|
|
# Mixed: usernames and IDs work together
|
|
node scripts/migrateCustomerData.js \
|
|
--preview \
|
|
--sources acme_aviation,507f1f77bcf86cd799439012 \
|
|
--destination consolidated_ag
|
|
```
|
|
|
|
### 2. Execute After Reviewing Preview
|
|
|
|
```bash
|
|
# Using usernames
|
|
node scripts/migrateCustomerData.js \
|
|
--sources acme_aviation,regional_ag \
|
|
--destination consolidated_ag
|
|
|
|
# Using IDs
|
|
node scripts/migrateCustomerData.js \
|
|
--sources <SOURCE_ID1>,<SOURCE_ID2> \
|
|
--destination <DEST_ID>
|
|
```
|
|
|
|
## Common Scenarios
|
|
|
|
### Scenario 1: Simple Single Customer Migration
|
|
|
|
**Use Case**: Merge one customer into another
|
|
|
|
```bash
|
|
# Step 1: Preview (using usernames - easier to read)
|
|
node scripts/migrateCustomerData.js \
|
|
--preview \
|
|
--sources acme_aviation \
|
|
--destination consolidated_ag
|
|
|
|
# Step 2: Execute
|
|
node scripts/migrateCustomerData.js \
|
|
--sources acme_aviation \
|
|
--destination consolidated_ag
|
|
|
|
# Alternative: Using IDs
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011 \
|
|
--destination 507f191e810c19729de860ea
|
|
```
|
|
|
|
**Result**:
|
|
- Source customer deactivated
|
|
- All data moved to destination
|
|
- Aborts if username conflicts detected
|
|
|
|
---
|
|
|
|
### Scenario 2: Consolidate Multiple Regional Accounts
|
|
|
|
**Use Case**: Merge multiple regional accounts into corporate account
|
|
|
|
```bash
|
|
# Using usernames (recommended)
|
|
node scripts/migrateCustomerData.js \
|
|
--sources acme_west,acme_east,acme_south \
|
|
--destination acme_corporate
|
|
|
|
# Using IDs
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011,507f1f77bcf86cd799439012,507f1f77bcf86cd799439013 \
|
|
--destination 507f191e810c19729de860ea
|
|
```
|
|
|
|
**Result**:
|
|
- All 3 regional accounts deactivated
|
|
- All data consolidated under corporate account
|
|
- Regional managers can be converted to admins (see Scenario 4)
|
|
|
|
---
|
|
|
|
### Scenario 3: Merge with Known Conflicts
|
|
|
|
**Use Case**: Known username overlaps, want to merge accounts
|
|
|
|
```bash
|
|
# Preview first to see conflicts (using usernames)
|
|
node scripts/migrateCustomerData.js \
|
|
--preview \
|
|
--sources acme_aviation \
|
|
--destination consolidated_ag
|
|
|
|
# Execute with merge if conflicts are acceptable
|
|
node scripts/migrateCustomerData.js \
|
|
--sources acme_aviation \
|
|
--destination consolidated_ag \
|
|
--merge-conflicts
|
|
```
|
|
|
|
**Result**:
|
|
- Conflicting accounts merged (old accounts marked deleted)
|
|
- Job/data references updated to existing accounts
|
|
- Old account data preserved in history
|
|
|
|
⚠️ **WARNING**: Review conflicts carefully before merging!
|
|
|
|
---
|
|
|
|
### Scenario 4: Convert Customers to Admin Accounts
|
|
|
|
**Use Case**: Keep source customer as admin user under destination
|
|
|
|
```bash
|
|
# Using usernames
|
|
node scripts/migrateCustomerData.js \
|
|
--sources acme_west,acme_east \
|
|
--destination acme_corporate \
|
|
--convert-to-admin
|
|
|
|
# Using IDs
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011,507f1f77bcf86cd799439012 \
|
|
--destination 507f191e810c19729de860ea \
|
|
--convert-to-admin
|
|
```
|
|
|
|
**Result**:
|
|
- Source customers become admin (kind='2') accounts under destination
|
|
- All sub-accounts and data moved to destination
|
|
- Original customer accounts deactivated but admin access preserved
|
|
|
|
**Perfect For**:
|
|
- Regional managers becoming corporate admins
|
|
- Acquired companies maintaining some autonomy
|
|
- Franchise consolidation
|
|
|
|
---
|
|
|
|
### Scenario 5: Data Only Migration (Skip Invoices)
|
|
|
|
**Use Case**: Migrate operational data but keep invoices separate
|
|
|
|
```bash
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011 \
|
|
--destination 507f191e810c19729de860ea \
|
|
--skip-invoices
|
|
```
|
|
|
|
**Result**:
|
|
- All data migrated except invoice customer references
|
|
- Useful for financial separation requirements
|
|
- Invoices remain linked to original customer
|
|
|
|
---
|
|
|
|
### Scenario 6: Custom History File Location
|
|
|
|
**Use Case**: Store migration logs in specific location
|
|
|
|
```bash
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011 \
|
|
--destination 507f191e810c19729de860ea \
|
|
--output-file /var/log/agmission/migrations/customer_migration_2025.json
|
|
```
|
|
|
|
**Result**:
|
|
- Migration history saved to custom location
|
|
- Useful for compliance/audit requirements
|
|
|
|
---
|
|
|
|
## Decision Tree
|
|
|
|
```
|
|
Do you want to migrate customer data?
|
|
│
|
|
├─ Single source customer?
|
|
│ ├─ Yes → Use Scenario 1
|
|
│ └─ No → Continue
|
|
│
|
|
├─ Multiple source customers?
|
|
│ ├─ Yes → Use Scenario 2
|
|
│ └─ No → Continue
|
|
│
|
|
├─ Known username conflicts?
|
|
│ ├─ Yes → Use Scenario 3 (with --merge-conflicts)
|
|
│ └─ No → Continue
|
|
│
|
|
├─ Keep source customers as admins?
|
|
│ ├─ Yes → Use Scenario 4 (with --convert-to-admin)
|
|
│ └─ No → Continue
|
|
│
|
|
├─ Need to preserve invoice separation?
|
|
│ ├─ Yes → Use Scenario 5 (with --skip-invoices)
|
|
│ └─ No → Use Scenario 1 or 2
|
|
│
|
|
└─ Always preview first with --preview!
|
|
```
|
|
|
|
## Conflict Resolution Strategies
|
|
|
|
### Strategy 1: Manual Resolution (RECOMMENDED)
|
|
|
|
**Before migration**:
|
|
1. Run preview to identify conflicts
|
|
2. Manually rename conflicting accounts in source OR destination
|
|
3. Run migration without conflicts
|
|
|
|
```bash
|
|
# Example: Rename client in source database
|
|
db.users.updateOne(
|
|
{ _id: ObjectId("507f1f77bcf86cd799439015") },
|
|
{ $set: { username: "john_farmer_regional" } }
|
|
)
|
|
|
|
# Then run migration
|
|
node scripts/migrateCustomerData.js --sources ... --destination ...
|
|
```
|
|
|
|
### Strategy 2: Automatic Merge
|
|
|
|
**During migration**:
|
|
- Use `--merge-conflicts` flag
|
|
- Old accounts marked as deleted
|
|
- References updated to existing accounts
|
|
|
|
```bash
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011 \
|
|
--destination 507f191e810c19729de860ea \
|
|
--merge-conflicts
|
|
```
|
|
|
|
### Strategy 3: Selective Migration
|
|
|
|
**Migrate in phases**:
|
|
1. Migrate non-conflicting customers first
|
|
2. Resolve conflicts for remaining customers
|
|
3. Migrate remaining customers
|
|
|
|
```bash
|
|
# Phase 1: No conflicts
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439011 \
|
|
--destination 507f191e810c19729de860ea
|
|
|
|
# Phase 2: After resolving conflicts
|
|
node scripts/migrateCustomerData.js \
|
|
--sources 507f1f77bcf86cd799439012 \
|
|
--destination 507f191e810c19729de860ea
|
|
```
|
|
|
|
## Verification Steps
|
|
|
|
### After Migration
|
|
|
|
1. **Check Migration History**
|
|
```bash
|
|
cat migration_history.json | jq '.[-1]'
|
|
```
|
|
|
|
2. **Verify Source Customer Deactivated**
|
|
```javascript
|
|
db.users.findOne({
|
|
_id: ObjectId("507f1f77bcf86cd799439011"),
|
|
kind: "1"
|
|
})
|
|
// Should have: markedDelete: true, active: false
|
|
```
|
|
|
|
3. **Verify Job Migration**
|
|
```javascript
|
|
db.jobs.countDocuments({
|
|
byPuid: ObjectId("507f191e810c19729de860ea")
|
|
})
|
|
// Should equal total jobs from source + existing destination jobs
|
|
```
|
|
|
|
4. **Verify Sub-Account Migration**
|
|
```javascript
|
|
db.users.countDocuments({
|
|
parent: ObjectId("507f191e810c19729de860ea"),
|
|
kind: { $in: ["3", "5", "9"] }
|
|
})
|
|
// Should equal expected count
|
|
```
|
|
|
|
5. **Check for Errors in History**
|
|
```bash
|
|
cat migration_history.json | jq '.[-1].stats.errors'
|
|
```
|
|
|
|
## Troubleshooting Common Issues
|
|
|
|
### Issue: "Source customer(s) not found"
|
|
```bash
|
|
# Verify customer exists
|
|
mongo
|
|
> use agmission
|
|
> db.users.findOne({ _id: ObjectId("YOUR_ID"), kind: "1" })
|
|
```
|
|
|
|
### Issue: "Migration aborted due to conflicts"
|
|
```bash
|
|
# View conflicts in detail
|
|
node scripts/migrateCustomerData.js --preview --sources ... --destination ...
|
|
# Then use Strategy 1 or 2 above
|
|
```
|
|
|
|
### Issue: Transaction timeout
|
|
```bash
|
|
# Reduce number of source customers per migration
|
|
# Instead of migrating 10 at once, do 2-3 at a time
|
|
```
|
|
|
|
### Issue: Need to rollback
|
|
```bash
|
|
# MongoDB transactions auto-rollback on error
|
|
# If completed but need to reverse:
|
|
# 1. Restore from backup
|
|
# OR
|
|
# 2. Manually reverse using migration_history.json details
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# Enable detailed debugging
|
|
DEBUG=agm:migrate-customer-data,agm:mongo_enhanced \
|
|
node scripts/migrateCustomerData.js ...
|
|
|
|
# Custom MongoDB connection
|
|
MONGODB_URI=mongodb://user:pass@host:port/dbname \
|
|
node scripts/migrateCustomerData.js ...
|
|
```
|
|
|
|
## Data Validation Queries
|
|
|
|
### Check Job Distribution
|
|
```javascript
|
|
// Count jobs per customer
|
|
db.jobs.aggregate([
|
|
{ $match: { markedDelete: { $ne: true } } },
|
|
{ $group: { _id: "$byPuid", count: { $sum: 1 } } },
|
|
{ $sort: { count: -1 } }
|
|
])
|
|
```
|
|
|
|
### Check Sub-Account Distribution
|
|
```javascript
|
|
// Count sub-accounts per parent
|
|
db.users.aggregate([
|
|
{ $match: { kind: { $in: ["3", "5", "9"] }, markedDelete: { $ne: true } } },
|
|
{ $group: { _id: "$parent", count: { $sum: 1 } } },
|
|
{ $sort: { count: -1 } }
|
|
])
|
|
```
|
|
|
|
### Find Orphaned Data
|
|
```javascript
|
|
// Jobs without valid customer
|
|
db.jobs.aggregate([
|
|
{ $match: { markedDelete: { $ne: true } } },
|
|
{
|
|
$lookup: {
|
|
from: "users",
|
|
localField: "byPuid",
|
|
foreignField: "_id",
|
|
as: "customer"
|
|
}
|
|
},
|
|
{ $match: { customer: { $size: 0 } } },
|
|
{ $count: "orphaned_jobs" }
|
|
])
|
|
```
|
|
|
|
## Checklist
|
|
|
|
Before Migration:
|
|
- [ ] Database backup completed
|
|
- [ ] Source and destination customer IDs confirmed
|
|
- [ ] Preview completed and reviewed
|
|
- [ ] Conflicts identified and strategy decided
|
|
- [ ] Stakeholders notified
|
|
- [ ] Maintenance window scheduled (if applicable)
|
|
|
|
After Migration:
|
|
- [ ] Migration history file reviewed
|
|
- [ ] No errors in migration log
|
|
- [ ] Job counts verified
|
|
- [ ] Sub-account counts verified
|
|
- [ ] Invoice references checked (if not skipped)
|
|
- [ ] Source customer deactivated (if not converted to admin)
|
|
- [ ] Destination customer has all expected data
|
|
- [ ] Users notified of changes
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
1. Check `migration_history.json` for details
|
|
2. Enable debug logging: `DEBUG=agm:*`
|
|
3. Review this guide and main README
|
|
4. Contact database administrator
|
|
|
|
## Related Commands
|
|
|
|
```bash
|
|
# View migration history
|
|
cat migration_history.json | jq '.'
|
|
|
|
# View latest migration
|
|
cat migration_history.json | jq '.[-1]'
|
|
|
|
# View all failed migrations
|
|
cat migration_history.json | jq '.[] | select(.status == "failed")'
|
|
|
|
# View all completed migrations
|
|
cat migration_history.json | jq '.[] | select(.status == "completed")'
|
|
|
|
# Count migrations by status
|
|
cat migration_history.json | jq 'group_by(.status) | map({status: .[0].status, count: length})'
|
|
```
|