140 lines
5.4 KiB
JavaScript
140 lines
5.4 KiB
JavaScript
// /components/professors/professorMigration.js
|
|
'use strict';
|
|
|
|
const mongoose = require('mongoose');
|
|
const Professor = require('./professorModel');
|
|
const User = require('../users/userModel');
|
|
const Role = require('../roles/roleModel');
|
|
const logger = require('../../utils/logger');
|
|
const { allocatePlaceholderNationalId } = require('../../utils/nationalId');
|
|
const { generateUsername, generateSimplePassword } = require('../../utils/credentials');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
const ensureProfessorRole = async () => {
|
|
let role = await Role.findOne({ name: 'Professor' });
|
|
if (!role) {
|
|
role = await Role.create({
|
|
name: 'Professor',
|
|
description: 'استاد و مدرس دورهها',
|
|
permissions: ['professors:read', 'classes:read', 'sessions:read', 'attendances:read'],
|
|
isSystem: true
|
|
});
|
|
}
|
|
return role;
|
|
};
|
|
|
|
/**
|
|
* Automatically migrate and link all legacy Professor records to the Users table.
|
|
* Ensures every Professor document has a valid, populated `user` reference.
|
|
*/
|
|
const migrateAndLinkProfessorsToUsers = async () => {
|
|
try {
|
|
const professorRole = await ensureProfessorRole();
|
|
const professors = await Professor.find({}).setOptions({ _skipUserPopulate: true });
|
|
|
|
if (!professors.length) {
|
|
logger.info('Professor migration: No professor records found.');
|
|
return { total: 0, migrated: 0, alreadyLinked: 0 };
|
|
}
|
|
|
|
let migratedCount = 0;
|
|
let alreadyLinkedCount = 0;
|
|
|
|
for (const prof of professors) {
|
|
const rawDoc = prof._doc || prof;
|
|
let linkedUser = null;
|
|
|
|
// 1. Check if professor already has a valid user reference
|
|
if (prof.user && mongoose.Types.ObjectId.isValid(prof.user)) {
|
|
linkedUser = await User.findById(prof.user);
|
|
}
|
|
|
|
if (linkedUser) {
|
|
// Ensure user role is Professor if not an admin
|
|
if (!linkedUser.role || (String(linkedUser.role) !== String(professorRole._id) && linkedUser.role?.name !== 'SuperAdmin' && linkedUser.role?.name !== 'Admin')) {
|
|
linkedUser.role = professorRole._id;
|
|
await linkedUser.save();
|
|
}
|
|
alreadyLinkedCount++;
|
|
continue;
|
|
}
|
|
|
|
// 2. Professor is not linked to a user. Find matching user by phone or national ID
|
|
const rawPhone = String(rawDoc.phoneNumber || rawDoc.phone || '').trim();
|
|
const rawNationalId = String(rawDoc.nationalIdCode || rawDoc.nationalId || '').trim();
|
|
const rawName = String(rawDoc.name || '').trim();
|
|
const rawSurname = String(rawDoc.surname || '').trim();
|
|
const fullName = [rawName, rawSurname].filter(Boolean).join(' ').trim() || 'استاد بدون نام';
|
|
|
|
if (rawPhone) {
|
|
linkedUser = await User.findOne({ phoneNumber: rawPhone });
|
|
}
|
|
if (!linkedUser && rawNationalId) {
|
|
linkedUser = await User.findOne({ nationalIdCode: rawNationalId });
|
|
}
|
|
|
|
// 3. If matching user found, link and update role
|
|
if (linkedUser) {
|
|
if (!linkedUser.role || (String(linkedUser.role) !== String(professorRole._id) && linkedUser.role?.name !== 'SuperAdmin' && linkedUser.role?.name !== 'Admin')) {
|
|
linkedUser.role = professorRole._id;
|
|
}
|
|
if (!linkedUser.name && fullName) {
|
|
linkedUser.name = fullName;
|
|
}
|
|
if (!linkedUser.cardNumber && rawDoc.cardNumber) {
|
|
linkedUser.cardNumber = String(rawDoc.cardNumber).trim();
|
|
}
|
|
if (!linkedUser.shabaNumber && (rawDoc.shabaNumber || rawDoc.iban)) {
|
|
linkedUser.shabaNumber = String(rawDoc.shabaNumber || rawDoc.iban).trim();
|
|
}
|
|
await linkedUser.save();
|
|
|
|
prof.user = linkedUser._id;
|
|
await prof.save();
|
|
migratedCount++;
|
|
logger.info(`Professor migration: Linked professor "${fullName}" (${prof._id}) to existing user (${linkedUser._id}).`);
|
|
continue;
|
|
}
|
|
|
|
// 4. If no matching user found, create a new User for this professor
|
|
let nationalIdCode = rawNationalId;
|
|
if (!nationalIdCode) {
|
|
nationalIdCode = await allocatePlaceholderNationalId(rawPhone || fullName);
|
|
}
|
|
|
|
const generatedUsername = await generateUsername(fullName);
|
|
const plainPassword = generateSimplePassword();
|
|
const passwordHash = await bcrypt.hash(plainPassword, 10);
|
|
|
|
const newUser = await User.create({
|
|
name: fullName,
|
|
nationalIdCode,
|
|
phoneNumber: rawPhone || undefined,
|
|
email: rawDoc.email ? String(rawDoc.email).trim().toLowerCase() : undefined,
|
|
role: professorRole._id,
|
|
username: generatedUsername,
|
|
passwordHash,
|
|
cardNumber: rawDoc.cardNumber ? String(rawDoc.cardNumber).trim() : undefined,
|
|
shabaNumber: (rawDoc.shabaNumber || rawDoc.iban) ? String(rawDoc.shabaNumber || rawDoc.iban).trim() : undefined,
|
|
isActive: prof.isActive !== false
|
|
});
|
|
|
|
prof.user = newUser._id;
|
|
await prof.save();
|
|
migratedCount++;
|
|
logger.info(`Professor migration: Created new user (${newUser._id}) for legacy professor "${fullName}" (${prof._id}).`);
|
|
}
|
|
|
|
logger.info(`Professor migration complete: ${professors.length} total, ${migratedCount} newly linked/created, ${alreadyLinkedCount} already linked.`);
|
|
return { total: professors.length, migrated: migratedCount, alreadyLinked: alreadyLinkedCount };
|
|
} catch (error) {
|
|
logger.error('Error during migrateAndLinkProfessorsToUsers:', error);
|
|
return { error: error.message };
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
migrateAndLinkProfessorsToUsers,
|
|
ensureProfessorRole
|
|
};
|