feat: send account and invoice SMS from stored template IDs
Move sms.ir template IDs out of env into settings so SuperAdmin can manage them, and record every outbound SMS status in notifications.
This commit is contained in:
+1
-7
@@ -71,13 +71,7 @@ EMAIL_FROM=no-reply@institution.com
|
|||||||
SMS_ENABLED=false
|
SMS_ENABLED=false
|
||||||
SMS_PANEL_TOKEN=your_sms_ir_api_key
|
SMS_PANEL_TOKEN=your_sms_ir_api_key
|
||||||
SMS_SENDER_NUMBER=10001000
|
SMS_SENDER_NUMBER=10001000
|
||||||
# Template IDs from sms.ir panel (verify templates)
|
# SMS verify template IDs are stored in the database (SuperAdmin → Settings).
|
||||||
# Account created vars: username, password
|
|
||||||
SMS_TEMPLATE_ACCOUNT_CREATED=
|
|
||||||
# Class registered vars: className
|
|
||||||
SMS_TEMPLATE_CLASS_REGISTERED=
|
|
||||||
# Class reminder (30 min before) vars: className, time, place
|
|
||||||
SMS_TEMPLATE_CLASS_REMINDER=
|
|
||||||
|
|
||||||
# Bale Messenger Bot Token
|
# Bale Messenger Bot Token
|
||||||
BALE_BOT_TOKEN=mock_bale_bot_token
|
BALE_BOT_TOKEN=mock_bale_bot_token
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ app.get('/api/health', (req, res) => {
|
|||||||
const seedDatabase = require('./seed');
|
const seedDatabase = require('./seed');
|
||||||
const seedRoutes = require('./components/seed/seedRoutes');
|
const seedRoutes = require('./components/seed/seedRoutes');
|
||||||
const dataImportRoutes = require('./components/dataImport/dataImportRoutes');
|
const dataImportRoutes = require('./components/dataImport/dataImportRoutes');
|
||||||
|
const settingRoutes = require('./components/settings/settingRoutes');
|
||||||
|
|
||||||
app.use('/api/auth', authRoutes);
|
app.use('/api/auth', authRoutes);
|
||||||
app.use('/api/users', userRoutes);
|
app.use('/api/users', userRoutes);
|
||||||
@@ -99,6 +100,7 @@ app.use('/api/activity-logs', activityLogRoutes);
|
|||||||
app.use('/api/contact-inquiries', contactInquiryRoutes);
|
app.use('/api/contact-inquiries', contactInquiryRoutes);
|
||||||
app.use('/api/seed', seedRoutes);
|
app.use('/api/seed', seedRoutes);
|
||||||
app.use('/api/data-import', dataImportRoutes);
|
app.use('/api/data-import', dataImportRoutes);
|
||||||
|
app.use('/api/settings', settingRoutes);
|
||||||
|
|
||||||
// ── Error Handlers ────────────────────────────────────────────────────────────
|
// ── Error Handlers ────────────────────────────────────────────────────────────
|
||||||
app.use(notFoundHandler);
|
app.use(notFoundHandler);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const eventEmitter = require('../../events/eventEmitter');
|
|||||||
const EVENT_NAMES = require('../../constants/eventNames');
|
const EVENT_NAMES = require('../../constants/eventNames');
|
||||||
const { calculateMeta, escapeRegex, getSearchTerm } = require('../../utils/pagination');
|
const { calculateMeta, escapeRegex, getSearchTerm } = require('../../utils/pagination');
|
||||||
const User = require('../users/userModel');
|
const User = require('../users/userModel');
|
||||||
|
const { sendInvoiceCreatedSms } = require('../../utils/senders/smsMessages');
|
||||||
|
const logger = require('../../utils/logger');
|
||||||
|
|
||||||
const getAllPayments = async (query) => {
|
const getAllPayments = async (query) => {
|
||||||
const page = parseInt(query.page) || 1;
|
const page = parseInt(query.page) || 1;
|
||||||
@@ -71,6 +73,15 @@ const createPayment = async (body, actorId = null) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await User.findById(payment.user).select('phoneNumber').lean();
|
||||||
|
if (user?.phoneNumber) {
|
||||||
|
await sendInvoiceCreatedSms(user.phoneNumber, payment.amount, user._id);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[createPayment] Invoice SMS failed for payment ${payment._id}: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
return getPaymentById(payment._id);
|
return getPaymentById(payment._id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const catchAsync = require('../../utils/catchAsync');
|
||||||
|
const settingService = require('./settingService');
|
||||||
|
const { successResponse } = require('../../utils/apiResponse');
|
||||||
|
|
||||||
|
exports.getSettings = catchAsync(async (req, res) => {
|
||||||
|
const settings = await settingService.getSettings();
|
||||||
|
return successResponse(res, 200, 'Settings retrieved', settings);
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.saveSettings = catchAsync(async (req, res) => {
|
||||||
|
const settings = await settingService.saveSettings(req.body);
|
||||||
|
return successResponse(res, 200, 'Settings saved', settings);
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const SETTINGS_KEY = 'app';
|
||||||
|
|
||||||
|
const settingSchema = new mongoose.Schema({
|
||||||
|
key: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
default: SETTINGS_KEY,
|
||||||
|
index: true
|
||||||
|
},
|
||||||
|
smsTemplates: {
|
||||||
|
type: mongoose.Schema.Types.Mixed,
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
timestamps: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const Setting = mongoose.model('Setting', settingSchema);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Setting,
|
||||||
|
SETTINGS_KEY
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const settingController = require('./settingController');
|
||||||
|
const authMiddleware = require('../../middlewares/authMiddleware');
|
||||||
|
const requireSuperAdmin = require('../../middlewares/requireSuperAdmin');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.use(authMiddleware);
|
||||||
|
router.use(requireSuperAdmin);
|
||||||
|
|
||||||
|
router.get('/', settingController.getSettings);
|
||||||
|
router.put('/', settingController.saveSettings);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const AppError = require('../../utils/AppError');
|
||||||
|
const { Setting, SETTINGS_KEY } = require('./settingModel');
|
||||||
|
const {
|
||||||
|
SMS_TEMPLATE_DEFS,
|
||||||
|
envFallbackFor,
|
||||||
|
sanitizeTemplateId
|
||||||
|
} = require('./smsTemplates');
|
||||||
|
|
||||||
|
const emptyTemplateMap = () => {
|
||||||
|
const map = {};
|
||||||
|
SMS_TEMPLATE_DEFS.forEach((def) => {
|
||||||
|
map[def.key] = '';
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readStoredMap = (doc) => {
|
||||||
|
const stored = doc?.smsTemplates || {};
|
||||||
|
if (stored instanceof Map) {
|
||||||
|
return Object.fromEntries(stored.entries());
|
||||||
|
}
|
||||||
|
return { ...stored };
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveTemplateId = (storedMap, def) => {
|
||||||
|
const fromDb = String(storedMap[def.key] || '').trim();
|
||||||
|
if (fromDb) return fromDb;
|
||||||
|
return envFallbackFor(def);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toPublicTemplates = (storedMap) => {
|
||||||
|
return SMS_TEMPLATE_DEFS.map((def) => ({
|
||||||
|
key: def.key,
|
||||||
|
label: def.label,
|
||||||
|
variables: def.variables,
|
||||||
|
templateId: resolveTemplateId(storedMap, def)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSettings = async () => {
|
||||||
|
const doc = await Setting.findOne({ key: SETTINGS_KEY }).lean();
|
||||||
|
const storedMap = readStoredMap(doc);
|
||||||
|
return {
|
||||||
|
smsTemplates: toPublicTemplates(storedMap)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSmsTemplateId = async (key) => {
|
||||||
|
const def = SMS_TEMPLATE_DEFS.find((item) => item.key === key);
|
||||||
|
if (!def) return '';
|
||||||
|
const doc = await Setting.findOne({ key: SETTINGS_KEY }).lean();
|
||||||
|
return resolveTemplateId(readStoredMap(doc), def);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveSettings = async (body = {}) => {
|
||||||
|
const incoming = body.smsTemplates || {};
|
||||||
|
const incomingMap = Array.isArray(incoming)
|
||||||
|
? Object.fromEntries(incoming.map((item) => [item.key, item.templateId]))
|
||||||
|
: incoming;
|
||||||
|
|
||||||
|
const existing = await Setting.findOne({ key: SETTINGS_KEY });
|
||||||
|
const storedMap = existing ? readStoredMap(existing) : emptyTemplateMap();
|
||||||
|
const nextMap = emptyTemplateMap();
|
||||||
|
|
||||||
|
for (const def of SMS_TEMPLATE_DEFS) {
|
||||||
|
const hasIncoming = Object.prototype.hasOwnProperty.call(incomingMap, def.key);
|
||||||
|
if (hasIncoming) {
|
||||||
|
const sanitized = sanitizeTemplateId(incomingMap[def.key]);
|
||||||
|
if (sanitized === null) {
|
||||||
|
throw new AppError('VALIDATION_FAILED', { field: def.key }, `Invalid SMS template ID for ${def.key}`);
|
||||||
|
}
|
||||||
|
nextMap[def.key] = sanitized;
|
||||||
|
} else if (storedMap[def.key]) {
|
||||||
|
nextMap[def.key] = String(storedMap[def.key]).trim();
|
||||||
|
} else {
|
||||||
|
nextMap[def.key] = envFallbackFor(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = await Setting.findOneAndUpdate(
|
||||||
|
{ key: SETTINGS_KEY },
|
||||||
|
{ $set: { smsTemplates: nextMap } },
|
||||||
|
{ upsert: true, new: true, setDefaultsOnInsert: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
smsTemplates: toPublicTemplates(readStoredMap(doc))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getSettings,
|
||||||
|
saveSettings,
|
||||||
|
getSmsTemplateId
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const SMS_TEMPLATE_DEFS = [
|
||||||
|
{
|
||||||
|
key: 'accountCreated',
|
||||||
|
label: 'ایجاد حساب کاربری',
|
||||||
|
variables: ['user', 'password'],
|
||||||
|
envKey: 'SMS_TEMPLATE_ACCOUNT_CREATED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'classRegistered',
|
||||||
|
label: 'ثبتنام در کلاس',
|
||||||
|
variables: ['className'],
|
||||||
|
envKey: 'SMS_TEMPLATE_CLASS_REGISTERED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'classReminder',
|
||||||
|
label: 'یادآوری کلاس',
|
||||||
|
variables: ['className', 'time', 'place'],
|
||||||
|
envKey: 'SMS_TEMPLATE_CLASS_REMINDER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'invoiceCreated',
|
||||||
|
label: 'ایجاد صورتحساب',
|
||||||
|
variables: ['amount'],
|
||||||
|
envKey: 'SMS_TEMPLATE_INVOICE_CREATED'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const SMS_TEMPLATE_KEYS = SMS_TEMPLATE_DEFS.map((def) => def.key);
|
||||||
|
|
||||||
|
const envFallbackFor = (def) => {
|
||||||
|
if (!def?.envKey) return '';
|
||||||
|
return String(process.env[def.envKey] || '').trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitizeTemplateId = (value) => {
|
||||||
|
if (value == null) return '';
|
||||||
|
const digits = String(value).trim();
|
||||||
|
if (!digits) return '';
|
||||||
|
if (!/^\d{1,20}$/.test(digits)) return null;
|
||||||
|
return digits;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
SMS_TEMPLATE_DEFS,
|
||||||
|
SMS_TEMPLATE_KEYS,
|
||||||
|
envFallbackFor,
|
||||||
|
sanitizeTemplateId
|
||||||
|
};
|
||||||
@@ -175,7 +175,7 @@ const createUserAdmin = async (body) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendAccountCreatedSms(profile.phoneNumber, username, plainPassword);
|
await sendAccountCreatedSms(profile.phoneNumber, username, plainPassword, user._id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`[createUserAdmin] Account SMS failed for ${profile.phoneNumber}: ${err.message}`);
|
logger.error(`[createUserAdmin] Account SMS failed for ${profile.phoneNumber}: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,10 +106,6 @@ const config = {
|
|||||||
SMS_PANEL_TOKEN: process.env.SMS_PANEL_TOKEN || process.env.SMS_API_KEY || '',
|
SMS_PANEL_TOKEN: process.env.SMS_PANEL_TOKEN || process.env.SMS_API_KEY || '',
|
||||||
SMS_API_KEY: process.env.SMS_PANEL_TOKEN || process.env.SMS_API_KEY || 'mock_sms_key',
|
SMS_API_KEY: process.env.SMS_PANEL_TOKEN || process.env.SMS_API_KEY || 'mock_sms_key',
|
||||||
SMS_SENDER_NUMBER: process.env.SMS_SENDER_NUMBER || '10001000',
|
SMS_SENDER_NUMBER: process.env.SMS_SENDER_NUMBER || '10001000',
|
||||||
// Template IDs configured in the sms.ir panel
|
|
||||||
SMS_TEMPLATE_ACCOUNT_CREATED: process.env.SMS_TEMPLATE_ACCOUNT_CREATED || '',
|
|
||||||
SMS_TEMPLATE_CLASS_REGISTERED: process.env.SMS_TEMPLATE_CLASS_REGISTERED || '',
|
|
||||||
SMS_TEMPLATE_CLASS_REMINDER: process.env.SMS_TEMPLATE_CLASS_REMINDER || '',
|
|
||||||
|
|
||||||
// Bale Messenger Bot Settings
|
// Bale Messenger Bot Settings
|
||||||
BALE_BOT_TOKEN: process.env.BALE_BOT_TOKEN || 'mock_bale_bot_token',
|
BALE_BOT_TOKEN: process.env.BALE_BOT_TOKEN || 'mock_bale_bot_token',
|
||||||
|
|||||||
@@ -12,11 +12,19 @@ const toBoolean = (value) => {
|
|||||||
return ['true', '1', 'yes', 'on'].includes(normalized);
|
return ['true', '1', 'yes', 'on'].includes(normalized);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const redactSmsParams = (params = []) => (
|
||||||
|
params.map((param) => (
|
||||||
|
String(param?.name || '').toLowerCase() === 'password'
|
||||||
|
? { ...param, value: '[redacted]' }
|
||||||
|
: param
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
const sendSingleSms = async (mobile, templateId, params = []) => {
|
const sendSingleSms = async (mobile, templateId, params = []) => {
|
||||||
console.log('[SMS] About to send notification:', {
|
console.log('[SMS] About to send notification:', {
|
||||||
mobile,
|
mobile,
|
||||||
templateId,
|
templateId,
|
||||||
params,
|
params: redactSmsParams(params),
|
||||||
SMS_ENABLED: config.SMS_ENABLED,
|
SMS_ENABLED: config.SMS_ENABLED,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// /utils/senders/smsMessages.js
|
// /utils/senders/smsMessages.js
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('../../config/config');
|
|
||||||
const { sendSingleSms } = require('./sms.base');
|
const { sendSingleSms } = require('./sms.base');
|
||||||
const { recordAndSend } = require('./notificationRecorder');
|
const { recordAndSend } = require('./notificationRecorder');
|
||||||
|
const { getSmsTemplateId } = require('../../components/settings/settingService');
|
||||||
const User = require('../../components/users/userModel');
|
const User = require('../../components/users/userModel');
|
||||||
|
|
||||||
const resolveUserIdByPhone = async (phoneNumber) => {
|
const resolveUserIdByPhone = async (phoneNumber) => {
|
||||||
@@ -12,16 +12,17 @@ const resolveUserIdByPhone = async (phoneNumber) => {
|
|||||||
return user?._id || null;
|
return user?._id || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendAccountCreatedSms = async (receiver, username, password) => {
|
const sendAccountCreatedSms = async (receiver, username, password, userId = null) => {
|
||||||
const userId = await resolveUserIdByPhone(receiver);
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
||||||
|
const templateId = await getSmsTemplateId('accountCreated');
|
||||||
return recordAndSend({
|
return recordAndSend({
|
||||||
userId,
|
userId: resolvedUserId,
|
||||||
channel: 'sms',
|
channel: 'sms',
|
||||||
subject: 'ایجاد حساب کاربری',
|
subject: 'ایجاد حساب کاربری',
|
||||||
body: `حساب کاربری شما ایجاد شد. نام کاربری: ${username}`,
|
body: `حساب کاربری شما ایجاد شد. نام کاربری: ${username}`,
|
||||||
relatedEvent: 'user.created',
|
relatedEvent: 'user.created',
|
||||||
sendFn: () => sendSingleSms(receiver, config.SMS_TEMPLATE_ACCOUNT_CREATED, [
|
sendFn: () => sendSingleSms(receiver, templateId, [
|
||||||
{ name: 'username', value: String(username) },
|
{ name: 'user', value: String(username) },
|
||||||
{ name: 'password', value: String(password) }
|
{ name: 'password', value: String(password) }
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
@@ -29,13 +30,14 @@ const sendAccountCreatedSms = async (receiver, username, password) => {
|
|||||||
|
|
||||||
const sendClassRegisteredSms = async (receiver, className, userId = null) => {
|
const sendClassRegisteredSms = async (receiver, className, userId = null) => {
|
||||||
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
||||||
|
const templateId = await getSmsTemplateId('classRegistered');
|
||||||
return recordAndSend({
|
return recordAndSend({
|
||||||
userId: resolvedUserId,
|
userId: resolvedUserId,
|
||||||
channel: 'sms',
|
channel: 'sms',
|
||||||
subject: 'ثبتنام در کلاس',
|
subject: 'ثبتنام در کلاس',
|
||||||
body: `ثبتنام شما در کلاس «${className}» انجام شد.`,
|
body: `ثبتنام شما در کلاس «${className}» انجام شد.`,
|
||||||
relatedEvent: 'user.enrolled',
|
relatedEvent: 'user.enrolled',
|
||||||
sendFn: () => sendSingleSms(receiver, config.SMS_TEMPLATE_CLASS_REGISTERED, [
|
sendFn: () => sendSingleSms(receiver, templateId, [
|
||||||
{ name: 'className', value: String(className) }
|
{ name: 'className', value: String(className) }
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
@@ -43,13 +45,14 @@ const sendClassRegisteredSms = async (receiver, className, userId = null) => {
|
|||||||
|
|
||||||
const sendClassReminderSms = async (receiver, className, time, place = '', userId = null) => {
|
const sendClassReminderSms = async (receiver, className, time, place = '', userId = null) => {
|
||||||
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
||||||
|
const templateId = await getSmsTemplateId('classReminder');
|
||||||
return recordAndSend({
|
return recordAndSend({
|
||||||
userId: resolvedUserId,
|
userId: resolvedUserId,
|
||||||
channel: 'sms',
|
channel: 'sms',
|
||||||
subject: 'یادآوری کلاس',
|
subject: 'یادآوری کلاس',
|
||||||
body: `یادآوری کلاس «${className}» ساعت ${time} — مکان: ${place || '-'}`,
|
body: `یادآوری کلاس «${className}» ساعت ${time} — مکان: ${place || '-'}`,
|
||||||
relatedEvent: 'session.reminder',
|
relatedEvent: 'session.reminder',
|
||||||
sendFn: () => sendSingleSms(receiver, config.SMS_TEMPLATE_CLASS_REMINDER, [
|
sendFn: () => sendSingleSms(receiver, templateId, [
|
||||||
{ name: 'className', value: String(className) },
|
{ name: 'className', value: String(className) },
|
||||||
{ name: 'time', value: String(time) },
|
{ name: 'time', value: String(time) },
|
||||||
{ name: 'place', value: String(place || '-') }
|
{ name: 'place', value: String(place || '-') }
|
||||||
@@ -57,8 +60,25 @@ const sendClassReminderSms = async (receiver, className, time, place = '', userI
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendInvoiceCreatedSms = async (receiver, amount, userId = null) => {
|
||||||
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
||||||
|
const templateId = await getSmsTemplateId('invoiceCreated');
|
||||||
|
const amountLabel = String(amount);
|
||||||
|
return recordAndSend({
|
||||||
|
userId: resolvedUserId,
|
||||||
|
channel: 'sms',
|
||||||
|
subject: 'ایجاد صورتحساب',
|
||||||
|
body: `صورتحساب جدید به مبلغ ${amountLabel} تومان صادر شد.`,
|
||||||
|
relatedEvent: 'payment.created',
|
||||||
|
sendFn: () => sendSingleSms(receiver, templateId, [
|
||||||
|
{ name: 'amount', value: amountLabel }
|
||||||
|
])
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sendAccountCreatedSms,
|
sendAccountCreatedSms,
|
||||||
sendClassRegisteredSms,
|
sendClassRegisteredSms,
|
||||||
sendClassReminderSms
|
sendClassReminderSms,
|
||||||
|
sendInvoiceCreatedSms
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const {
|
|||||||
sendAccountCreatedSms,
|
sendAccountCreatedSms,
|
||||||
sendClassRegisteredSms,
|
sendClassRegisteredSms,
|
||||||
sendClassReminderSms,
|
sendClassReminderSms,
|
||||||
|
sendInvoiceCreatedSms,
|
||||||
} = require('./smsMessages');
|
} = require('./smsMessages');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,4 +35,5 @@ module.exports = {
|
|||||||
sendAccountCreatedSms,
|
sendAccountCreatedSms,
|
||||||
sendClassRegisteredSms,
|
sendClassRegisteredSms,
|
||||||
sendClassReminderSms,
|
sendClassReminderSms,
|
||||||
|
sendInvoiceCreatedSms,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user