From 67e0ff400ee619cce6d92ebf87af154cf1c0104f Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Mon, 24 Nov 2025 15:58:17 +0300 Subject: [PATCH] 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 --- .../form/generateConfirmationFormHTML.ts | 391 +++++++++++++++++- 1 file changed, 379 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/form/generateConfirmationFormHTML.ts b/frontend/src/components/form/generateConfirmationFormHTML.ts index fe99be4..5c384cb 100644 --- a/frontend/src/components/form/generateConfirmationFormHTML.ts +++ b/frontend/src/components/form/generateConfirmationFormHTML.ts @@ -99,23 +99,175 @@ export function generateConfirmationFormHTML(data: any): string { ogrn: null, }; - // Данные уже нормализованы, используем их напрямую - // Убеждаемся, что есть базовые структуры - if (!caseObj.offenders || !caseObj.offenders.length) { - caseObj.offenders = [Object.assign({}, baseOffender)]; + // Функция нормализации данных из формата propertyName в формат case + function normalizeData(data: any): any { + console.log('=== НОРМАЛИЗАЦИЯ ДАННЫХ ==='); + 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 = {}; - if (!caseObj.attachments) caseObj.attachments = []; + + // Нормализуем данные + let normalizedCaseObj: any; + + // Если данные приходят в формате 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') { - const normalized = normalizeMoney(caseObj.project.agrprice); + if (normalizedCaseObj.project && normalizedCaseObj.project.agrprice && typeof normalizedCaseObj.project.agrprice === 'string') { + const normalized = normalizeMoney(normalizedCaseObj.project.agrprice); 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, '')); @@ -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){ if (s === null || s === undefined) return ''; return String(s).replace(/&/g,'&').replace(/