fix: format prices in notifications, change partial payment label, and add session holding notify API
This commit is contained in:
@@ -57,6 +57,12 @@ exports.updateAttendance = catchAsync(async (req, res, next) => {
|
||||
return successResponse(res, 200, 'Session attendance updated successfully', session);
|
||||
});
|
||||
|
||||
exports.notifyHolding = catchAsync(async (req, res, next) => {
|
||||
const actorId = req.user?._id;
|
||||
const result = await sessionService.notifySessionHolding(req.params.id, actorId);
|
||||
return successResponse(res, 200, 'اطلاعرسانی برگزاری جلسه با موفقیت انجام شد', result);
|
||||
});
|
||||
|
||||
exports.getMySessions = catchAsync(async (req, res, next) => {
|
||||
const { data, meta } = await sessionService.getMySessions(req.user._id, req.query);
|
||||
return listResponse(res, 200, data, meta);
|
||||
|
||||
@@ -28,5 +28,6 @@ router.delete('/admin/delete/:id', perm.requires(PERMISSIONS.SESSIONS_DELETE), s
|
||||
router.post('/admin/bulk-delete', perm.requires(PERMISSIONS.SESSIONS_DELETE), sessionController.bulkDelete);
|
||||
router.post('/admin/bulk-status', perm.requires(PERMISSIONS.SESSIONS_UPDATE), sessionController.bulkUpdateStatus);
|
||||
router.put('/admin/:id/attendance', perm.requires(PERMISSIONS.SESSIONS_ATTENDANCE), validateUpdateAttendanceList, sessionController.updateAttendance);
|
||||
router.post('/admin/:id/notify-holding', perm.requires(PERMISSIONS.SESSIONS_READ), sessionController.notifyHolding);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user