fix(settings): persist every SMS template variable on save
Mongoose Mixed updates were dropping extra invoiceCreated variables after a successful save.
This commit is contained in:
@@ -5,11 +5,10 @@ const { Setting, SETTINGS_KEY } = require('./settingModel');
|
||||
const {
|
||||
SMS_TEMPLATE_DEFS,
|
||||
envFallbackFor,
|
||||
sanitizeTemplateId,
|
||||
sanitizeVariableName,
|
||||
emptyTemplateEntry,
|
||||
normalizeStoredEntry,
|
||||
resolveVariablesList
|
||||
resolveVariablesList,
|
||||
mergeTemplateEntry
|
||||
} = require('./smsTemplates');
|
||||
|
||||
const emptyTemplateMap = () => {
|
||||
@@ -103,75 +102,39 @@ const saveSettings = async (body = {}) => {
|
||||
? Object.fromEntries(incoming.map((item) => [item.key, item]))
|
||||
: incoming;
|
||||
|
||||
const existing = await Setting.findOne({ key: SETTINGS_KEY }).lean();
|
||||
const storedMap = existing ? readStoredMap(existing) : emptyTemplateMap();
|
||||
const existing = await Setting.findOne({ key: SETTINGS_KEY });
|
||||
const storedMap = existing ? readStoredMap(existing.toObject ? existing.toObject() : existing) : emptyTemplateMap();
|
||||
const nextMap = {};
|
||||
|
||||
for (const def of SMS_TEMPLATE_DEFS) {
|
||||
const storedEntry = normalizeStoredEntry(storedMap[def.key], def);
|
||||
const hasIncoming = Object.prototype.hasOwnProperty.call(incomingMap, def.key);
|
||||
const incomingEntry = hasIncoming ? parseIncomingEntry(incomingMap[def.key]) : null;
|
||||
|
||||
let templateId = storedEntry.templateId || envFallbackFor(def);
|
||||
let variables = storedEntry.variables;
|
||||
|
||||
if (incomingEntry) {
|
||||
if (incomingEntry.templateId !== undefined) {
|
||||
const sanitized = sanitizeTemplateId(incomingEntry.templateId);
|
||||
if (sanitized === null) {
|
||||
throw new AppError('VALIDATION_FAILED', { field: def.key }, `شناسه قالب پیامک برای ${def.label} نامعتبر است`);
|
||||
}
|
||||
templateId = sanitized;
|
||||
try {
|
||||
nextMap[def.key] = mergeTemplateEntry(def, storedMap[def.key], incomingEntry);
|
||||
} catch (err) {
|
||||
if (err.code === 'INVALID_TEMPLATE_ID') {
|
||||
throw new AppError('VALIDATION_FAILED', { field: def.key }, `شناسه قالب پیامک برای ${def.label} نامعتبر است`);
|
||||
}
|
||||
|
||||
if (incomingEntry.variables !== undefined) {
|
||||
let rawList = [];
|
||||
if (Array.isArray(incomingEntry.variables)) {
|
||||
rawList = incomingEntry.variables;
|
||||
} else if (incomingEntry.variables && typeof incomingEntry.variables === 'object') {
|
||||
rawList = Object.entries(incomingEntry.variables).map(([slot, name]) => ({ slot, name }));
|
||||
}
|
||||
|
||||
const validVars = [];
|
||||
for (const item of rawList) {
|
||||
if (!item || typeof item !== 'object') continue;
|
||||
const slot = String(item.slot || '').trim();
|
||||
if (!slot) continue;
|
||||
const rawName = String(item.name || '').trim();
|
||||
const sanitized = sanitizeVariableName(rawName);
|
||||
if (sanitized === null) {
|
||||
throw new AppError(
|
||||
'VALIDATION_FAILED',
|
||||
{ field: `${def.key}.${slot}` },
|
||||
`نام متغیر «${rawName}» برای ${def.label} نامعتبر است`
|
||||
);
|
||||
}
|
||||
const slotDef = (def?.slots || []).find((s) => s.key === slot);
|
||||
validVars.push({
|
||||
slot,
|
||||
name: sanitized || slotDef?.defaultName || slot
|
||||
});
|
||||
}
|
||||
variables = validVars;
|
||||
if (err.code === 'INVALID_VARIABLE_NAME') {
|
||||
throw new AppError(
|
||||
'VALIDATION_FAILED',
|
||||
{ field: err.field || def.key },
|
||||
`نام متغیر «${err.rawName}» برای ${def.label} نامعتبر است`
|
||||
);
|
||||
}
|
||||
} else if (!storedEntry.templateId) {
|
||||
templateId = envFallbackFor(def);
|
||||
throw err;
|
||||
}
|
||||
|
||||
nextMap[def.key] = {
|
||||
templateId,
|
||||
variables: resolveVariablesList(def, variables)
|
||||
};
|
||||
}
|
||||
|
||||
const doc = await Setting.findOneAndUpdate(
|
||||
{ key: SETTINGS_KEY },
|
||||
{ $set: { smsTemplates: nextMap } },
|
||||
{ upsert: true, new: true, setDefaultsOnInsert: true }
|
||||
).lean();
|
||||
const doc = existing || new Setting({ key: SETTINGS_KEY });
|
||||
doc.set('smsTemplates', JSON.parse(JSON.stringify(nextMap)));
|
||||
doc.markModified('smsTemplates');
|
||||
await doc.save();
|
||||
|
||||
const saved = await Setting.findOne({ key: SETTINGS_KEY }).lean();
|
||||
return {
|
||||
smsTemplates: toPublicTemplates(readStoredMap(doc))
|
||||
smsTemplates: toPublicTemplates(readStoredMap(saved))
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user