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:
@@ -86,6 +86,42 @@ const emptyTemplateEntry = (def) => ({
|
||||
variables: defaultVariablesList(def)
|
||||
});
|
||||
|
||||
const toVariableItem = (item, fallbackSlot = '') => {
|
||||
if (item == null) return null;
|
||||
if (typeof item === 'object') {
|
||||
const slot = String(item.slot || fallbackSlot || '').trim();
|
||||
if (!slot) return null;
|
||||
return {
|
||||
slot,
|
||||
name: String(item.name || '').trim()
|
||||
};
|
||||
}
|
||||
const slot = String(fallbackSlot || '').trim();
|
||||
if (!slot) return null;
|
||||
return {
|
||||
slot,
|
||||
name: String(item).trim()
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeVariablesInput = (rawVariables, def) => {
|
||||
if (rawVariables == null) {
|
||||
return def ? defaultVariablesList(def) : [];
|
||||
}
|
||||
|
||||
if (Array.isArray(rawVariables)) {
|
||||
return rawVariables.map((item) => toVariableItem(item)).filter(Boolean);
|
||||
}
|
||||
|
||||
if (typeof rawVariables === 'object') {
|
||||
return Object.entries(rawVariables)
|
||||
.map(([key, value]) => toVariableItem(value, key))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return def ? defaultVariablesList(def) : [];
|
||||
};
|
||||
|
||||
const normalizeStoredEntry = (raw, def) => {
|
||||
if (raw == null || raw === '') {
|
||||
return {
|
||||
@@ -106,22 +142,9 @@ const normalizeStoredEntry = (raw, def) => {
|
||||
};
|
||||
}
|
||||
|
||||
let variables = [];
|
||||
if (Array.isArray(raw.variables)) {
|
||||
variables = raw.variables
|
||||
.filter((item) => item && typeof item === 'object' && item.slot)
|
||||
.map((item) => ({
|
||||
slot: String(item.slot).trim(),
|
||||
name: String(item.name || '').trim()
|
||||
}));
|
||||
} else if (raw.variables && typeof raw.variables === 'object') {
|
||||
variables = Object.entries(raw.variables).map(([slot, name]) => ({
|
||||
slot: String(slot).trim(),
|
||||
name: String(name || '').trim()
|
||||
}));
|
||||
} else if (def) {
|
||||
variables = defaultVariablesList(def);
|
||||
}
|
||||
const variables = Object.prototype.hasOwnProperty.call(raw, 'variables')
|
||||
? normalizeVariablesInput(raw.variables, def)
|
||||
: (def ? defaultVariablesList(def) : []);
|
||||
|
||||
return {
|
||||
templateId: raw.templateId != null ? String(raw.templateId) : '',
|
||||
@@ -129,19 +152,58 @@ const normalizeStoredEntry = (raw, def) => {
|
||||
};
|
||||
};
|
||||
|
||||
const parseIncomingVariables = (rawVariables, def) => {
|
||||
if (rawVariables === undefined) return undefined;
|
||||
return normalizeVariablesInput(rawVariables, def);
|
||||
};
|
||||
|
||||
const mergeTemplateEntry = (def, storedRaw, incomingRaw = null) => {
|
||||
const storedEntry = normalizeStoredEntry(storedRaw, def);
|
||||
let templateId = storedEntry.templateId || envFallbackFor(def);
|
||||
let variables = storedEntry.variables;
|
||||
|
||||
if (incomingRaw) {
|
||||
if (incomingRaw.templateId !== undefined) {
|
||||
const sanitized = sanitizeTemplateId(incomingRaw.templateId);
|
||||
if (sanitized === null) {
|
||||
const error = new Error(`Invalid SMS template ID for ${def?.key || 'template'}`);
|
||||
error.code = 'INVALID_TEMPLATE_ID';
|
||||
error.field = def?.key;
|
||||
throw error;
|
||||
}
|
||||
templateId = sanitized;
|
||||
}
|
||||
|
||||
if (incomingRaw.variables !== undefined) {
|
||||
const parsed = parseIncomingVariables(incomingRaw.variables, def) || [];
|
||||
for (const item of parsed) {
|
||||
const sanitized = sanitizeVariableName(item.name);
|
||||
if (item.name && sanitized === null) {
|
||||
const error = new Error(`Invalid SMS variable name "${item.name}"`);
|
||||
error.code = 'INVALID_VARIABLE_NAME';
|
||||
error.field = `${def?.key}.${item.slot}`;
|
||||
error.rawName = item.name;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
variables = parsed;
|
||||
}
|
||||
} else if (!storedEntry.templateId) {
|
||||
templateId = envFallbackFor(def);
|
||||
}
|
||||
|
||||
return {
|
||||
templateId,
|
||||
variables: resolveVariablesList(def, variables)
|
||||
};
|
||||
};
|
||||
|
||||
const resolveVariablesList = (def, storedVariables) => {
|
||||
if (!storedVariables) {
|
||||
return defaultVariablesList(def);
|
||||
}
|
||||
|
||||
let list = [];
|
||||
if (Array.isArray(storedVariables)) {
|
||||
list = storedVariables;
|
||||
} else if (typeof storedVariables === 'object') {
|
||||
list = Object.entries(storedVariables).map(([slot, name]) => ({ slot, name }));
|
||||
}
|
||||
|
||||
return list
|
||||
return normalizeVariablesInput(storedVariables, def)
|
||||
.map((item) => {
|
||||
const slot = String(item.slot || '').trim();
|
||||
if (!slot) return null;
|
||||
@@ -159,14 +221,7 @@ const resolveVariablesList = (def, storedVariables) => {
|
||||
};
|
||||
|
||||
const buildSmsParameters = (variables, valuesBySlot = {}) => {
|
||||
let list = [];
|
||||
if (Array.isArray(variables)) {
|
||||
list = variables;
|
||||
} else if (variables && typeof variables === 'object') {
|
||||
list = Object.entries(variables).map(([slot, name]) => ({ slot, name }));
|
||||
}
|
||||
|
||||
return list
|
||||
return normalizeVariablesInput(variables, null)
|
||||
.map((item) => {
|
||||
const name = sanitizeVariableName(item?.name);
|
||||
if (!name) return null;
|
||||
@@ -189,6 +244,8 @@ module.exports = {
|
||||
defaultVariablesList,
|
||||
emptyTemplateEntry,
|
||||
normalizeStoredEntry,
|
||||
parseIncomingVariables,
|
||||
mergeTemplateEntry,
|
||||
resolveVariablesList,
|
||||
buildSmsParameters
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user