feat: add messaging toggles, password reset SMS, and payment discounts

Allow SuperAdmin to disable SMS, email, and bot from dashboard settings on top of env flags. Add admin password reset with credentials SMS, plus payment discounts, notes, and payable amount handling.
This commit is contained in:
2026-08-16 06:58:08 +03:30
parent a6c760c3b7
commit 72be01fee3
25 changed files with 601 additions and 43 deletions
+6
View File
@@ -3,11 +3,17 @@
const axios = require('axios');
const config = require('../../config/config');
const logger = require('../logger');
const { isMessagingChannelEnabled } = require('../../components/settings/messagingFlags');
const sendBaleMessage = async ({ chatId, body }) => {
try {
const targetChatId = chatId || 'default_channel';
if (!(await isMessagingChannelEnabled('bot'))) {
logger.info(`[BaleBotSender] Skipped (channel disabled) → ${targetChatId}`);
return { skipped: true, reason: 'channel_disabled' };
}
if (config.BALE_BOT_TOKEN === 'mock_bale_bot_token') {
logger.info(`[BaleBotSender MOCK] ChatID: ${targetChatId} | Message: "${body}"`);
return { success: true, messageId: `bale_mock_${Date.now()}` };
+6
View File
@@ -3,6 +3,7 @@
const nodemailer = require('nodemailer');
const config = require('../../config/config');
const logger = require('../logger');
const { isMessagingChannelEnabled } = require('../../components/settings/messagingFlags');
let transporter = null;
@@ -25,6 +26,11 @@ const sendEmail = async ({ to, subject, body, html }) => {
try {
if (!to) throw new Error('Recipient email is required');
if (!(await isMessagingChannelEnabled('email'))) {
logger.info(`[EmailSender] Skipped (channel disabled) → ${to}`);
return { skipped: true, reason: 'channel_disabled' };
}
const mailOptions = {
from: config.EMAIL_FROM,
to,
+4 -3
View File
@@ -4,6 +4,7 @@
const axios = require('axios');
const config = require('../../config/config');
const logger = require('../logger');
const { isMessagingChannelEnabled } = require('../../components/settings/messagingFlags');
const toBoolean = (value) => {
if (typeof value === 'boolean') return value;
@@ -28,9 +29,9 @@ const sendSingleSms = async (mobile, templateId, params = []) => {
SMS_ENABLED: config.SMS_ENABLED,
});
if (!toBoolean(config.SMS_ENABLED)) {
logger.info(`[SMS] Skipped (SMS_ENABLED=false) → ${mobile} template=${templateId}`);
return { skipped: true };
if (!(await isMessagingChannelEnabled('sms'))) {
logger.info(`[SMS] Skipped (channel disabled) → ${mobile} template=${templateId}`);
return { skipped: true, reason: 'channel_disabled' };
}
if (!templateId) {
+37 -4
View File
@@ -13,7 +13,15 @@ const resolveUserIdByPhone = async (phoneNumber) => {
return user?._id || null;
};
const sendAccountCreatedSms = async (receiver, username, password, userId = null) => {
const sendAccountCredentialsSms = async ({
receiver,
username,
password,
userId = null,
subject,
body,
relatedEvent
}) => {
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
let fullName = '';
if (resolvedUserId) {
@@ -24,9 +32,9 @@ const sendAccountCreatedSms = async (receiver, username, password, userId = null
return recordAndSend({
userId: resolvedUserId,
channel: 'sms',
subject: 'ایجاد حساب کاربری',
body: `حساب کاربری شما ایجاد شد. نام کاربری: ${username}`,
relatedEvent: 'user.created',
subject,
body,
relatedEvent,
sendFn: () => sendSingleSms(receiver, templateId, buildSmsParameters(variables, {
username,
password,
@@ -38,6 +46,30 @@ const sendAccountCreatedSms = async (receiver, username, password, userId = null
});
};
const sendAccountCreatedSms = async (receiver, username, password, userId = null) => {
return sendAccountCredentialsSms({
receiver,
username,
password,
userId,
subject: 'ایجاد حساب کاربری',
body: `حساب کاربری شما ایجاد شد. نام کاربری: ${username}`,
relatedEvent: 'user.created'
});
};
const sendPasswordResetSms = async (receiver, username, password, userId = null) => {
return sendAccountCredentialsSms({
receiver,
username,
password,
userId,
subject: 'بازنشانی رمز عبور',
body: `رمز عبور شما بازنشانی شد. نام کاربری: ${username}`,
relatedEvent: 'user.password_reset'
});
};
const sendClassRegisteredSms = async (receiver, className, userId = null, extraContext = {}) => {
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
let fullName = extraContext.fullName || '';
@@ -143,6 +175,7 @@ const sendInvoiceCreatedSms = async (receiver, payload, userId = null) => {
module.exports = {
sendAccountCreatedSms,
sendPasswordResetSms,
sendClassRegisteredSms,
sendClassReminderSms,
sendInvoiceCreatedSms