feat: Add normalizeData function matching n8n implementation
Problem:
- Form didn't handle data in propertyName format from n8n
- Missing normalization logic from n8n Code node
Solution:
1. Added normalizeData function in TypeScript:
- Handles propertyName format (new format from n8n)
- Handles old format (applicant, case, contract_or_service)
- Handles case format (current structure)
- Converts field names (first_name -> firstname, etc.)
- Normalizes money values
- Extracts attachments_names
2. Added normalizeData function in JavaScript:
- Same logic as TypeScript version
- Handles data extraction from multiple sources:
* propertyName (object or string)
* value
* data
* output
* case
- Supports array format (takes first element)
3. Enhanced data extraction logic:
- Checks multiple data sources
- Handles both array and object formats
- Merges SMS meta data from injected
- Ensures base structures exist
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Added normalization logic
This commit is contained in:
@@ -99,23 +99,175 @@ export function generateConfirmationFormHTML(data: any): string {
|
|||||||
ogrn: null,
|
ogrn: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Данные уже нормализованы, используем их напрямую
|
// Функция нормализации данных из формата propertyName в формат case
|
||||||
// Убеждаемся, что есть базовые структуры
|
function normalizeData(data: any): any {
|
||||||
if (!caseObj.offenders || !caseObj.offenders.length) {
|
console.log('=== НОРМАЛИЗАЦИЯ ДАННЫХ ===');
|
||||||
caseObj.offenders = [Object.assign({}, baseOffender)];
|
console.log('Input data:', data);
|
||||||
|
console.log('Has propertyName:', !!data.propertyName);
|
||||||
|
console.log('Has applicant in propertyName:', !!(data.propertyName && data.propertyName.applicant));
|
||||||
|
|
||||||
|
// Если данные приходят в новом формате (с propertyName)
|
||||||
|
if (data.propertyName && data.propertyName.applicant) {
|
||||||
|
console.log('Using NEW format with propertyName');
|
||||||
|
const props = data.propertyName;
|
||||||
|
const applicant = props.applicant || {};
|
||||||
|
const caseData = props.case || {};
|
||||||
|
const contract = props.contract_or_service || {};
|
||||||
|
const offenders = props.offenders || [];
|
||||||
|
const claim = props.claim || {};
|
||||||
|
const meta = props.meta || {};
|
||||||
|
|
||||||
|
console.log('=== ОТЛАДКА КОНТРАКТА ===');
|
||||||
|
console.log('contract_or_service:', contract);
|
||||||
|
console.log('subject:', contract.subject);
|
||||||
|
console.log('agreement_date_fmt:', contract.agreement_date_fmt);
|
||||||
|
console.log('agreement_date:', contract.agreement_date);
|
||||||
|
console.log('period_start_fmt:', contract.period_start_fmt);
|
||||||
|
console.log('period_end_fmt:', contract.period_end_fmt);
|
||||||
|
|
||||||
|
// Получаем список приложенных документов
|
||||||
|
const attachments = props.attachments_names || [];
|
||||||
|
console.log('=== ОТЛАДКА ПРИЛОЖЕНИЙ ===');
|
||||||
|
console.log('attachments_names:', attachments);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
firstname: applicant.first_name || null,
|
||||||
|
secondname: applicant.middle_name || null,
|
||||||
|
lastname: applicant.last_name || null,
|
||||||
|
mobile: applicant.phone || null,
|
||||||
|
email: applicant.email || null,
|
||||||
|
birthday: applicant.birth_date_fmt || applicant.birth_date || null,
|
||||||
|
birthplace: applicant.birth_place || null,
|
||||||
|
mailingstreet: applicant.address || null,
|
||||||
|
inn: applicant.inn || null,
|
||||||
|
tgid: null,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
category: caseData.category || null,
|
||||||
|
direction: caseData.direction || null,
|
||||||
|
agrprice: normalizeMoney(contract.amount_paid_fmt || contract.amount_paid) || null,
|
||||||
|
subject: contract.subject || null,
|
||||||
|
agrdate: contract.agreement_date_fmt || contract.agreement_date || null,
|
||||||
|
startdate: contract.period_start_fmt || contract.period_start || null,
|
||||||
|
finishdate: contract.period_end_fmt || contract.period_end || null,
|
||||||
|
period_text: contract.period_text || null,
|
||||||
|
description: claim.description || null,
|
||||||
|
reason: claim.reason || caseData.category || null,
|
||||||
|
},
|
||||||
|
attachments: attachments,
|
||||||
|
offenders: offenders.map((o: any) => ({
|
||||||
|
accountname: o.name || null,
|
||||||
|
address: o.address || null,
|
||||||
|
email: o.email || null,
|
||||||
|
website: o.website || null,
|
||||||
|
phone: o.phone || null,
|
||||||
|
inn: o.inn || null,
|
||||||
|
ogrn: o.ogrn || null,
|
||||||
|
})),
|
||||||
|
meta: Object.assign({}, meta, {
|
||||||
|
session_token: data.session_token || meta.claim_id || null,
|
||||||
|
prefix: data.prefix || null,
|
||||||
|
telegram_id: data.telegram_id || null,
|
||||||
|
claim_id: data.claim_id || meta.claim_id || null,
|
||||||
|
unified_id: meta.unified_id || null,
|
||||||
|
user_id: meta.user_id || null,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если данные приходят в старом формате (прямо applicant, case, etc)
|
||||||
|
if (data.applicant || data.case || data.contract_or_service) {
|
||||||
|
const applicant = data.applicant || {};
|
||||||
|
const caseData = data.case || {};
|
||||||
|
const contract = data.contract_or_service || {};
|
||||||
|
const offenders = data.offenders || [];
|
||||||
|
const claim = data.claim || {};
|
||||||
|
|
||||||
|
console.log('=== ОТЛАДКА КОНТРАКТА (старый формат) ===');
|
||||||
|
console.log('contract_or_service:', contract);
|
||||||
|
console.log('subject:', contract.subject);
|
||||||
|
console.log('agreement_date_fmt:', contract.agreement_date_fmt);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
firstname: applicant.first_name || null,
|
||||||
|
secondname: applicant.middle_name || null,
|
||||||
|
lastname: applicant.last_name || null,
|
||||||
|
mobile: applicant.phone || null,
|
||||||
|
email: applicant.email || null,
|
||||||
|
birthday: applicant.birth_date_fmt || applicant.birth_date || null,
|
||||||
|
birthplace: applicant.birth_place || null,
|
||||||
|
mailingstreet: applicant.address || null,
|
||||||
|
inn: applicant.inn || null,
|
||||||
|
tgid: null,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
category: caseData.category || null,
|
||||||
|
direction: caseData.direction || null,
|
||||||
|
agrprice: normalizeMoney(contract.amount_paid_fmt || contract.amount_paid) || null,
|
||||||
|
subject: contract.subject || null,
|
||||||
|
agrdate: contract.agreement_date_fmt || contract.agreement_date || null,
|
||||||
|
startdate: contract.period_start_fmt || contract.period_start || null,
|
||||||
|
finishdate: contract.period_end_fmt || contract.period_end || null,
|
||||||
|
period_text: contract.period_text || null,
|
||||||
|
description: claim.description || null,
|
||||||
|
reason: claim.reason || caseData.category || null,
|
||||||
|
},
|
||||||
|
attachments: data.attachments_names || [],
|
||||||
|
offenders: offenders.map((o: any) => Object.assign({}, baseOffender, o || {})),
|
||||||
|
meta: data.meta || {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если данные уже в формате case (наша текущая структура)
|
||||||
|
if (data.case) {
|
||||||
|
return data.case;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Старый формат (обратная совместимость)
|
||||||
|
return {
|
||||||
|
user: Object.assign({}, baseUser, tryParseJSON(data.user) || data.user || {}),
|
||||||
|
project: Object.assign({}, baseProject, tryParseJSON(data.project) || data.project || {}),
|
||||||
|
offenders: Array.isArray(data.offenders) ? data.offenders.map((o: any) => Object.assign({}, baseOffender, o || {})) : [],
|
||||||
|
meta: Object.assign({}, data.meta || {}),
|
||||||
|
attachments: data.attachments || [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (!caseObj.user) caseObj.user = Object.assign({}, baseUser, caseObj.user || {});
|
|
||||||
if (!caseObj.project) caseObj.project = Object.assign({}, baseProject, caseObj.project || {});
|
// Нормализуем данные
|
||||||
if (!caseObj.meta) caseObj.meta = {};
|
let normalizedCaseObj: any;
|
||||||
if (!caseObj.attachments) caseObj.attachments = [];
|
|
||||||
|
// Если данные приходят в формате propertyName (как из n8n)
|
||||||
|
if (data.propertyName) {
|
||||||
|
normalizedCaseObj = normalizeData(data);
|
||||||
|
} else if (data.case) {
|
||||||
|
// Данные уже в формате case
|
||||||
|
normalizedCaseObj = data.case;
|
||||||
|
} else {
|
||||||
|
// Пытаемся нормализовать из любого формата
|
||||||
|
normalizedCaseObj = normalizeData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Убеждаемся, что есть базовые структуры
|
||||||
|
if (!normalizedCaseObj.offenders || !normalizedCaseObj.offenders.length) {
|
||||||
|
normalizedCaseObj.offenders = [Object.assign({}, baseOffender)];
|
||||||
|
}
|
||||||
|
if (!normalizedCaseObj.user) normalizedCaseObj.user = Object.assign({}, baseUser, normalizedCaseObj.user || {});
|
||||||
|
if (!normalizedCaseObj.project) normalizedCaseObj.project = Object.assign({}, baseProject, normalizedCaseObj.project || {});
|
||||||
|
if (!normalizedCaseObj.meta) normalizedCaseObj.meta = {};
|
||||||
|
if (!normalizedCaseObj.attachments) normalizedCaseObj.attachments = [];
|
||||||
|
|
||||||
// Нормализуем сумму, если она пришла в виде строки
|
// Нормализуем сумму, если она пришла в виде строки
|
||||||
if (caseObj.project && caseObj.project.agrprice && typeof caseObj.project.agrprice === 'string') {
|
if (normalizedCaseObj.project && normalizedCaseObj.project.agrprice && typeof normalizedCaseObj.project.agrprice === 'string') {
|
||||||
const normalized = normalizeMoney(caseObj.project.agrprice);
|
const normalized = normalizeMoney(normalizedCaseObj.project.agrprice);
|
||||||
if (normalized !== null) {
|
if (normalized !== null) {
|
||||||
caseObj.project.agrprice = normalized;
|
normalizedCaseObj.project.agrprice = normalized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Используем нормализованные данные
|
||||||
|
const caseObj = normalizedCaseObj;
|
||||||
|
|
||||||
// Сервисные поля
|
// Сервисные поля
|
||||||
const sessionToken = String(safeGet(caseObj.meta?.session_token, data.session_token, ''));
|
const sessionToken = String(safeGet(caseObj.meta?.session_token, data.session_token, ''));
|
||||||
@@ -374,6 +526,163 @@ export function generateConfirmationFormHTML(data: any): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryParseJSON(x) {
|
||||||
|
try {
|
||||||
|
return typeof x === 'string' ? JSON.parse(x) : x;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMoney(rawValue) {
|
||||||
|
if (!rawValue) return null;
|
||||||
|
console.log('normalizeMoney: входящее значение:', rawValue, 'тип:', typeof rawValue);
|
||||||
|
var s = String(rawValue);
|
||||||
|
s = s.replace(/\\s+/g, '');
|
||||||
|
s = s.replace(/руб(лей|ль|\\\\.)?/gi, '');
|
||||||
|
s = s.replace(/₽|р\\\\.|р$/gi, '');
|
||||||
|
s = s.replace(/[^0-9,.-]/g, '');
|
||||||
|
s = s.replace(',', '.');
|
||||||
|
console.log('normalizeMoney: после очистки:', s);
|
||||||
|
if (!/^-?[0-9]+(\\.[0-9]{1,2})?$/.test(s)) {
|
||||||
|
console.log('normalizeMoney: невалидный формат после очистки');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var result = parseFloat(s);
|
||||||
|
console.log('normalizeMoney: результат:', result);
|
||||||
|
return result > 0 ? result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция нормализации данных из формата propertyName в формат case
|
||||||
|
function normalizeData(data) {
|
||||||
|
console.log('=== НОРМАЛИЗАЦИЯ ДАННЫХ ===');
|
||||||
|
console.log('Input data:', data);
|
||||||
|
|
||||||
|
// Если данные приходят в новом формате (с propertyName)
|
||||||
|
if (data.propertyName && data.propertyName.applicant) {
|
||||||
|
console.log('Using NEW format with propertyName');
|
||||||
|
var props = data.propertyName;
|
||||||
|
var applicant = props.applicant || {};
|
||||||
|
var caseData = props.case || {};
|
||||||
|
var contract = props.contract_or_service || {};
|
||||||
|
var offenders = props.offenders || [];
|
||||||
|
var claim = props.claim || {};
|
||||||
|
var meta = props.meta || {};
|
||||||
|
|
||||||
|
var attachments = props.attachments_names || [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
firstname: applicant.first_name || null,
|
||||||
|
secondname: applicant.middle_name || null,
|
||||||
|
lastname: applicant.last_name || null,
|
||||||
|
mobile: applicant.phone || null,
|
||||||
|
email: applicant.email || null,
|
||||||
|
birthday: applicant.birth_date_fmt || applicant.birth_date || null,
|
||||||
|
birthplace: applicant.birth_place || null,
|
||||||
|
mailingstreet: applicant.address || null,
|
||||||
|
inn: applicant.inn || null,
|
||||||
|
tgid: null,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
category: caseData.category || null,
|
||||||
|
direction: caseData.direction || null,
|
||||||
|
agrprice: normalizeMoney(contract.amount_paid_fmt || contract.amount_paid) || null,
|
||||||
|
subject: contract.subject || null,
|
||||||
|
agrdate: contract.agreement_date_fmt || contract.agreement_date || null,
|
||||||
|
startdate: contract.period_start_fmt || contract.period_start || null,
|
||||||
|
finishdate: contract.period_end_fmt || contract.period_end || null,
|
||||||
|
period_text: contract.period_text || null,
|
||||||
|
description: claim.description || null,
|
||||||
|
reason: claim.reason || caseData.category || null,
|
||||||
|
},
|
||||||
|
attachments: attachments,
|
||||||
|
offenders: offenders.map(function(o) {
|
||||||
|
return {
|
||||||
|
accountname: o.name || null,
|
||||||
|
address: o.address || null,
|
||||||
|
email: o.email || null,
|
||||||
|
website: o.website || null,
|
||||||
|
phone: o.phone || null,
|
||||||
|
inn: o.inn || null,
|
||||||
|
ogrn: o.ogrn || null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
meta: Object.assign({}, meta, {
|
||||||
|
session_token: data.session_token || meta.claim_id || null,
|
||||||
|
prefix: data.prefix || null,
|
||||||
|
telegram_id: data.telegram_id || null,
|
||||||
|
claim_id: data.claim_id || meta.claim_id || null,
|
||||||
|
unified_id: meta.unified_id || null,
|
||||||
|
user_id: meta.user_id || null,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если данные приходят в старом формате (прямо applicant, case, etc)
|
||||||
|
if (data.applicant || data.case || data.contract_or_service) {
|
||||||
|
var applicant = data.applicant || {};
|
||||||
|
var caseData = data.case || {};
|
||||||
|
var contract = data.contract_or_service || {};
|
||||||
|
var offenders = data.offenders || [];
|
||||||
|
var claim = data.claim || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
firstname: applicant.first_name || null,
|
||||||
|
secondname: applicant.middle_name || null,
|
||||||
|
lastname: applicant.last_name || null,
|
||||||
|
mobile: applicant.phone || null,
|
||||||
|
email: applicant.email || null,
|
||||||
|
birthday: applicant.birth_date_fmt || applicant.birth_date || null,
|
||||||
|
birthplace: applicant.birth_place || null,
|
||||||
|
mailingstreet: applicant.address || null,
|
||||||
|
inn: applicant.inn || null,
|
||||||
|
tgid: null,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
category: caseData.category || null,
|
||||||
|
direction: caseData.direction || null,
|
||||||
|
agrprice: normalizeMoney(contract.amount_paid_fmt || contract.amount_paid) || null,
|
||||||
|
subject: contract.subject || null,
|
||||||
|
agrdate: contract.agreement_date_fmt || contract.agreement_date || null,
|
||||||
|
startdate: contract.period_start_fmt || contract.period_start || null,
|
||||||
|
finishdate: contract.period_end_fmt || contract.period_end || null,
|
||||||
|
period_text: contract.period_text || null,
|
||||||
|
description: claim.description || null,
|
||||||
|
reason: claim.reason || caseData.category || null,
|
||||||
|
},
|
||||||
|
attachments: data.attachments_names || [],
|
||||||
|
offenders: offenders.map(function(o) {
|
||||||
|
return {
|
||||||
|
accountname: o.name || o.accountname || null,
|
||||||
|
address: o.address || null,
|
||||||
|
email: o.email || null,
|
||||||
|
website: o.website || null,
|
||||||
|
phone: o.phone || null,
|
||||||
|
inn: o.inn || null,
|
||||||
|
ogrn: o.ogrn || null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
meta: data.meta || {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если данные уже в формате case (наша текущая структура)
|
||||||
|
if (data.case) {
|
||||||
|
return data.case;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Старый формат (обратная совместимость)
|
||||||
|
return {
|
||||||
|
user: tryParseJSON(data.user) || data.user || {},
|
||||||
|
project: tryParseJSON(data.project) || data.project || {},
|
||||||
|
offenders: Array.isArray(data.offenders) ? data.offenders : [],
|
||||||
|
meta: data.meta || {},
|
||||||
|
attachments: data.attachments || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function esc(s){
|
function esc(s){
|
||||||
if (s === null || s === undefined) return '';
|
if (s === null || s === undefined) return '';
|
||||||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"');
|
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"');
|
||||||
@@ -447,8 +756,66 @@ export function generateConfirmationFormHTML(data: any): string {
|
|||||||
return checkboxHtml;
|
return checkboxHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Получаем данные
|
||||||
var injected = getData();
|
var injected = getData();
|
||||||
var state = (injected && injected.case) ? injected.case : { user: {}, project: {}, offenders: [{}], meta: {} };
|
console.log('=== ПОЛУЧЕНЫ ДАННЫЕ ===');
|
||||||
|
console.log('injected:', injected);
|
||||||
|
console.log('injected.case:', injected.case);
|
||||||
|
console.log('injected.propertyName:', injected.propertyName);
|
||||||
|
|
||||||
|
// Достаём объект кейса из «типичных» мест
|
||||||
|
var dataCandidate = null;
|
||||||
|
if (!dataCandidate && injected.propertyName !== undefined) {
|
||||||
|
// Если propertyName - это объект (как в вашем случае), берем его напрямую
|
||||||
|
if (typeof injected.propertyName === 'object' && injected.propertyName !== null) {
|
||||||
|
dataCandidate = injected.propertyName;
|
||||||
|
} else if (typeof injected.propertyName === 'string') {
|
||||||
|
dataCandidate = tryParseJSON(injected.propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dataCandidate && injected.value !== undefined) dataCandidate = tryParseJSON(injected.value);
|
||||||
|
if (!dataCandidate && (injected.user || injected.project || injected.offenders || injected.meta)) dataCandidate = injected;
|
||||||
|
if (!dataCandidate && injected.data) dataCandidate = injected.data;
|
||||||
|
if (!dataCandidate && injected.output) dataCandidate = tryParseJSON(injected.output) || injected.output;
|
||||||
|
if (!dataCandidate && injected.case) dataCandidate = { case: injected.case };
|
||||||
|
dataCandidate = dataCandidate || injected;
|
||||||
|
|
||||||
|
console.log('dataCandidate:', dataCandidate);
|
||||||
|
console.log('Type of dataCandidate:', typeof dataCandidate);
|
||||||
|
console.log('Keys of dataCandidate:', Object.keys(dataCandidate || {}));
|
||||||
|
|
||||||
|
// Если dataCandidate - массив, берем первый элемент
|
||||||
|
var dataToNormalize = Array.isArray(dataCandidate) ? dataCandidate[0] : dataCandidate;
|
||||||
|
console.log('Data to normalize:', dataToNormalize);
|
||||||
|
|
||||||
|
// Нормализуем данные
|
||||||
|
var normalizedCase = normalizeData(dataToNormalize);
|
||||||
|
console.log('Normalized case:', normalizedCase);
|
||||||
|
|
||||||
|
// Формируем state из нормализованных данных
|
||||||
|
var state = normalizedCase || { user: {}, project: {}, offenders: [{}], meta: {}, attachments: [] };
|
||||||
|
|
||||||
|
// Убеждаемся, что есть базовые структуры
|
||||||
|
if (!state.offenders || !state.offenders.length) {
|
||||||
|
state.offenders = [{}];
|
||||||
|
}
|
||||||
|
if (!state.user) state.user = {};
|
||||||
|
if (!state.project) state.project = {};
|
||||||
|
if (!state.meta) state.meta = {};
|
||||||
|
if (!state.attachments) state.attachments = [];
|
||||||
|
|
||||||
|
// Добавляем SMS данные из injected
|
||||||
|
if (injected.sms_meta) {
|
||||||
|
state.meta = Object.assign({}, state.meta, injected.sms_meta);
|
||||||
|
}
|
||||||
|
if (injected.session_token) {
|
||||||
|
state.meta.session_token = injected.session_token;
|
||||||
|
}
|
||||||
|
if (injected.token) {
|
||||||
|
state.meta.token = injected.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Final state:', state);
|
||||||
|
|
||||||
function renderStatement() {
|
function renderStatement() {
|
||||||
var u = state.user || {};
|
var u = state.user || {};
|
||||||
|
|||||||
Reference in New Issue
Block a user