feat: auto generate placeholder nationalId for users and professors, auto calculate class end date
This commit is contained in:
@@ -63,10 +63,6 @@ const findExistingUser = async ({ nationalIdCode, phoneNumber, name }) => {
|
||||
const byId = await User.findOne({ nationalIdCode });
|
||||
if (byId) return byId;
|
||||
}
|
||||
if (phoneNumber) {
|
||||
const byPhone = await User.findOne({ phoneNumber });
|
||||
if (byPhone) return byPhone;
|
||||
}
|
||||
const target = normalizePersonName(name);
|
||||
if (target) {
|
||||
const firstToken = target.split(' ')[0];
|
||||
@@ -76,6 +72,14 @@ const findExistingUser = async ({ nationalIdCode, phoneNumber, name }) => {
|
||||
const match = candidates.find((user) => namesMatch(user.name, target));
|
||||
if (match) return match;
|
||||
}
|
||||
if (phoneNumber) {
|
||||
const byPhone = await User.findOne({ phoneNumber });
|
||||
if (byPhone) {
|
||||
if (!nationalIdCode || !byPhone.nationalIdCode || byPhone.nationalIdCode === nationalIdCode) {
|
||||
return byPhone;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -108,7 +112,7 @@ const applyStudentProfile = (user, student) => {
|
||||
};
|
||||
|
||||
const upsertStudent = async (student, userRole, stats, warnings) => {
|
||||
const phoneNumber = normalizePhone(student.phoneNumber || student.phone || student.parentPhoneNumber);
|
||||
let phoneNumber = normalizePhone(student.phoneNumber || student.phone || student.parentPhoneNumber);
|
||||
let nationalIdCode = normalizeNationalId(student.nationalIdCode || student.nationalId);
|
||||
|
||||
if (!phoneNumber && !nationalIdCode) {
|
||||
@@ -129,23 +133,22 @@ const upsertStudent = async (student, userRole, stats, warnings) => {
|
||||
return user;
|
||||
}
|
||||
|
||||
if (!phoneNumber) {
|
||||
warnings.push({
|
||||
reason: 'missing_phone',
|
||||
student: { name: student.name, nationalIdCode }
|
||||
});
|
||||
stats.studentsSkipped += 1;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!nationalIdCode) {
|
||||
nationalIdCode = await allocatePlaceholderNationalId(phoneNumber);
|
||||
nationalIdCode = await allocatePlaceholderNationalId(phoneNumber || student.name);
|
||||
warnings.push({
|
||||
reason: 'placeholder_national_id',
|
||||
student: { name: student.name, phoneNumber, nationalIdCode }
|
||||
});
|
||||
}
|
||||
|
||||
if (!phoneNumber) {
|
||||
phoneNumber = await allocatePlaceholderPhone(nationalIdCode || student.name);
|
||||
warnings.push({
|
||||
reason: 'placeholder_phone',
|
||||
student: { name: student.name, nationalIdCode, placeholderPhone: phoneNumber }
|
||||
});
|
||||
}
|
||||
|
||||
const conflictById = await User.findOne({ nationalIdCode });
|
||||
if (conflictById) {
|
||||
applyStudentProfile(conflictById, student);
|
||||
@@ -154,6 +157,17 @@ const upsertStudent = async (student, userRole, stats, warnings) => {
|
||||
return conflictById;
|
||||
}
|
||||
|
||||
const conflictByPhone = await User.findOne({ phoneNumber });
|
||||
if (conflictByPhone) {
|
||||
const originalPhone = phoneNumber;
|
||||
phoneNumber = await allocatePlaceholderPhone(nationalIdCode || student.name);
|
||||
if (!student.parentPhoneNumber) student.parentPhoneNumber = originalPhone;
|
||||
warnings.push({
|
||||
reason: 'phone_conflict_placeholder_allocated',
|
||||
student: { name: student.name, originalPhone, placeholderPhone: phoneNumber }
|
||||
});
|
||||
}
|
||||
|
||||
const username = await allocateUniqueUsername();
|
||||
const plainPassword = generateSimplePassword();
|
||||
const passwordHash = await bcrypt.hash(plainPassword, 10);
|
||||
@@ -194,13 +208,56 @@ const enrollUserInClass = async (user, course, cls, stats) => {
|
||||
}
|
||||
};
|
||||
|
||||
const WEEKDAY_STR_TO_NUM = {
|
||||
sunday: 0,
|
||||
monday: 1,
|
||||
tuesday: 2,
|
||||
wednesday: 3,
|
||||
thursday: 4,
|
||||
friday: 5,
|
||||
saturday: 6,
|
||||
'یکشنبه': 0,
|
||||
'یک شنبه': 0,
|
||||
'دوشنبه': 1,
|
||||
'دو شنبه': 1,
|
||||
'سه شنبه': 2,
|
||||
'سهشنبه': 2,
|
||||
'سهشنبه': 2,
|
||||
'چهارشنبه': 3,
|
||||
'چهار شنبه': 3,
|
||||
'پنجشنبه': 4,
|
||||
'پنج شنبه': 4,
|
||||
'جمعه': 5,
|
||||
'شنبه': 6
|
||||
};
|
||||
|
||||
const normalizeWeekdaysInput = (days) => {
|
||||
if (!Array.isArray(days)) return [];
|
||||
const res = [];
|
||||
for (const d of days) {
|
||||
if (typeof d === 'number' && d >= 0 && d <= 6) {
|
||||
if (!res.includes(d)) res.push(d);
|
||||
} else if (typeof d === 'string') {
|
||||
const lower = d.trim().toLowerCase();
|
||||
if (WEEKDAY_STR_TO_NUM[lower] != null) {
|
||||
const num = WEEKDAY_STR_TO_NUM[lower];
|
||||
if (!res.includes(num)) res.push(num);
|
||||
} else if (!Number.isNaN(Number(d))) {
|
||||
const num = Number(d);
|
||||
if (num >= 0 && num <= 6 && !res.includes(num)) res.push(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const applyClassSchedule = (cls, classInput) => {
|
||||
if (classInput.startDate && !cls.startDate) {
|
||||
const startDate = parseImportDate(classInput.startDate);
|
||||
if (startDate) cls.startDate = startDate;
|
||||
}
|
||||
if (Array.isArray(classInput.days) && classInput.days.length) {
|
||||
cls.days = classInput.days;
|
||||
cls.days = normalizeWeekdaysInput(classInput.days);
|
||||
}
|
||||
if (classInput.startTime) cls.startTime = String(classInput.startTime).trim();
|
||||
if (classInput.endTime) cls.endTime = String(classInput.endTime).trim();
|
||||
@@ -581,7 +638,7 @@ const importData = async (rawPayload) => {
|
||||
course: course._id,
|
||||
startDate: parseImportDate(classInput.startDate) || undefined,
|
||||
tuitionFee: Number(classInput.tuitionFee) || Number(courseInput.price) || 0,
|
||||
days: Array.isArray(classInput.days) ? classInput.days : [],
|
||||
days: normalizeWeekdaysInput(classInput.days),
|
||||
startTime: classInput.startTime || '',
|
||||
endTime: classInput.endTime || '',
|
||||
isActive: true
|
||||
|
||||
Reference in New Issue
Block a user