fix: format prices in notifications, change partial payment label, and add session holding notify API

This commit is contained in:
2026-08-21 20:17:30 +03:30
parent 6839685a21
commit 4c07ca2ae1
9 changed files with 145 additions and 14 deletions
+75
View File
@@ -9,6 +9,9 @@ const AppError = require('../../utils/AppError');
const eventEmitter = require('../../events/eventEmitter');
const EVENT_NAMES = require('../../constants/eventNames');
const { parsePaginationAndSort, buildFilterQuery, calculateMeta } = require('../../utils/pagination');
const { notifyAction } = require('../../utils/actionNotify');
const { sendSessionHoldingSms } = require('../../utils/senders/smsMessages');
const logger = require('../../utils/logger');
const STATUS_MAP = {
scheduled: 'scheduled',
@@ -430,6 +433,77 @@ const getMySessions = async (userId, queryParams) => {
return { data: sessions, meta };
};
const notifySessionHolding = async (sessionId, actorId = null) => {
const session = await Session.findById(sessionId)
.populate({
path: 'class',
select: 'name uniqueCode students startTime place'
})
.populate('course', 'title');
if (!session) {
throw new AppError('SESSION_NOT_FOUND');
}
const classDoc = session.class;
let studentIds = classDoc?.students?.length
? classDoc.students
: (await User.find({ courses: session.course?._id || session.course }).select('_id')).map((u) => u._id);
if (!studentIds.length) {
throw new AppError('NOT_FOUND', null, 'هیچ دانشجویی در این کلاس ثبت‌نام نشده است.');
}
const sessionDate = session.day
? new Date(session.day).toLocaleDateString('fa-IR')
: '';
const className = classDoc?.name || session.course?.title || 'کلاس';
const classCode = classDoc?.uniqueCode || '';
const timeLabel = session.startTime || '';
const topicLabel = session.topic || className;
const users = await User.find({ _id: { $in: studentIds } }).select('name phoneNumber email').lean();
const validUsers = users.filter((u) => u.phoneNumber);
if (!validUsers.length) {
throw new AppError('NOT_FOUND', null, 'هیچ دانشجویی با شماره همراه معتبر در این کلاس یافت نشد.');
}
let sentCount = 0;
for (const student of validUsers) {
try {
await notifyAction({
actionKey: 'sessionHolding',
userId: student._id,
phoneNumber: student.phoneNumber,
email: student.email,
subject: 'برگزاری جلسه طبق برنامه',
body: `جلسه «${topicLabel}» کلاس ${className} در تاریخ ${sessionDate} و ساعت ${timeLabel} طبق برنامه برگزار خواهد شد.`,
smsHandler: () => sendSessionHoldingSms(student.phoneNumber, {
fullName: student.name || '',
className,
topic: topicLabel,
sessionDate,
classTime: timeLabel,
courseName: session.course?.title || className,
classCode,
place: session.place || '-'
}, student._id),
requestSource: { notifySms: true, notifyEmail: true, notifyBot: true }
});
sentCount += 1;
} catch (err) {
logger.error(`[notifySessionHolding] Failed for user ${student._id}: ${err.message}`);
}
}
return {
success: true,
sentCount,
totalStudents: validUsers.length
};
};
module.exports = {
createSession,
getSessionById,
@@ -441,6 +515,7 @@ module.exports = {
searchSessions,
updateSessionAttendance,
getMySessions,
notifySessionHolding,
isSessionDue,
hasCompleteAttendance,
isAttendancePending