- Исправлена потеря документов при обновлении черновика (SQL объединяет вместо перезаписи) - Исправлено определение типа документа (приоритет field_label над field_name) - Исправлены дубликаты в documents_meta и documents_uploaded - Добавлена передача group_index с фронтенда для правильного field_name - Исправлены все документы в таблице clpr_claim_documents с правильными field_name - Обновлены SQL запросы: claimsave и claimsave_final для нового флоу - Добавлена поддержка multi-file upload для одного документа - Исправлены дубликаты в списке загруженных документов на фронтенде Файлы: - SQL: SQL_CLAIMSAVE_FIXED_NEW_FLOW.sql, SQL_CLAIMSAVE_FINAL_FIXED_NEW_FLOW_WITH_UPLOADED.sql - n8n: N8N_CODE_PROCESS_UPLOADED_FILES_FIXED.js (поддержка group_index) - Backend: documents.py (передача group_index в n8n) - Frontend: StepWizardPlan.tsx (передача group_index, исправление дубликатов) - Скрипты: fix_claim_documents_field_names.py, fix_documents_meta_duplicates.py Результат: документы больше не теряются, имеют правильные типы и field_name
158 lines
6.2 KiB
JavaScript
158 lines
6.2 KiB
JavaScript
// ============================================================================
|
||
// n8n Code Node: Обработка загруженных файлов (ИСПРАВЛЕННАЯ ВЕРСИЯ)
|
||
// ============================================================================
|
||
// OCR возвращает объединённые документы: один файл на группу (group_index)
|
||
// Структура: { data: [{ group_index_num: 0, files_count: 2, newfile: "...", ... }] }
|
||
// Решение: обрабатываем каждый элемент из data как объединённый документ
|
||
// ============================================================================
|
||
|
||
// ==== INPUT SHAPE SUPPORT ====
|
||
// OCR возвращает: { data: [ ...объединённые документы... ] }
|
||
const raw = $json;
|
||
const items = Array.isArray(raw?.data) ? raw.data : (Array.isArray(raw) ? raw : []);
|
||
|
||
if (!items.length) {
|
||
return [{
|
||
json: {
|
||
claim_id: null,
|
||
payload_partial_json: { documents_meta: [], edit_fields_raw: null, edit_fields_parsed: null },
|
||
filesRows: []
|
||
}
|
||
}];
|
||
}
|
||
|
||
// ==== CLAIM_ID DISCOVERY ====
|
||
let claim_id = $json.claim_id
|
||
|| $items('Edit Fields6')?.[0]?.json?.propertyName?.case_id
|
||
|| $('Edit Fields6').first().json.body.claim_id
|
||
|| null;
|
||
|
||
// ==== UTILS ====
|
||
const safeStr = (v) => (v == null ? '' : String(v));
|
||
const nowIso = new Date().toISOString();
|
||
const tryParseJSON = (x) => {
|
||
if (x == null) return null;
|
||
if (typeof x === 'object') return x;
|
||
if (typeof x === 'string') { try { return JSON.parse(x); } catch { return null; } }
|
||
return null;
|
||
};
|
||
|
||
// ==== ПРЕДВАРИТЕЛЬНО СОБИРАЕМ uploads_field_labels ИЗ BODY ====
|
||
const editRaw = $items('Edit Fields6')?.[0]?.json || null;
|
||
const body = editRaw?.body || null;
|
||
|
||
let uploads_descriptions = [];
|
||
let uploads_field_names = [];
|
||
let uploads_field_labels = [];
|
||
|
||
if (body && typeof body === 'object') {
|
||
const d = [];
|
||
const f = [];
|
||
const l = [];
|
||
for (const k of Object.keys(body)) {
|
||
const mD = k.match(/^uploads_descriptions\[(\d+)\]$/);
|
||
const mF = k.match(/^uploads_field_names\[(\d+)\]$/);
|
||
const mL = k.match(/^uploads_field_labels\[(\d+)\]$/);
|
||
if (mD) d[Number(mD[1])] = safeStr(body[k]);
|
||
if (mF) f[Number(mF[1])] = safeStr(body[k]);
|
||
if (mL) l[Number(mL[1])] = safeStr(body[k]);
|
||
}
|
||
uploads_descriptions = d.filter(v => v !== undefined);
|
||
uploads_field_names = f.filter(v => v !== undefined);
|
||
uploads_field_labels = l.filter(v => v !== undefined);
|
||
}
|
||
|
||
// ==== BUILD documents_meta + filesRows ====
|
||
// OCR возвращает объединённые документы: один файл на group_index
|
||
// Каждый элемент из data - это уже объединённый PDF (может содержать несколько страниц)
|
||
const documents_meta = [];
|
||
const filesRows = [];
|
||
|
||
for (const it of items) {
|
||
// ✅ ПРИОРИТЕТ: Используем group_index из body (переданный с фронтенда)
|
||
// Если его нет - используем group_index_num из OCR
|
||
// Если и его нет - пытаемся определить по document_type из uploads_field_names
|
||
let grp = null;
|
||
|
||
if (body && body.group_index !== undefined && body.group_index !== null) {
|
||
grp = Number(body.group_index);
|
||
} else if (it.group_index_num !== undefined && it.group_index_num !== null) {
|
||
grp = Number(it.group_index_num);
|
||
} else {
|
||
// Fallback: пытаемся определить по document_type
|
||
const doc_type = uploads_field_names[0] || uploads_field_labels[0] || '';
|
||
// Ищем индекс в documents_required по типу документа
|
||
// Это не идеально, но лучше чем всегда 0
|
||
grp = 0; // По умолчанию 0, если не можем определить
|
||
}
|
||
|
||
grp = grp || 0;
|
||
const file_index = 0; // После объединения всегда один файл на группу
|
||
|
||
const field_name = `uploads[${grp}][${file_index}]`;
|
||
const field_label = uploads_field_labels[grp] || uploads_field_names[grp] || uploads_descriptions[grp] || `group-${grp}`;
|
||
|
||
// OCR уже объединил файлы, используем newfile (путь к объединённому файлу)
|
||
const draft_key = safeStr(it.newfile || (it.folder && it.file_name ? `${it.folder}/${it.file_name}` : ''));
|
||
const original_name = safeStr(it.file_name || `group_${grp}.pdf`);
|
||
const description = safeStr(it.description || uploads_descriptions[grp] || '');
|
||
const prefix = safeStr(it.prefix || '');
|
||
|
||
// files_count показывает, сколько исходных файлов было объединено
|
||
const files_count = Number(it.files_count) || 1;
|
||
const pages = Number(it.pages) || null;
|
||
|
||
documents_meta.push({
|
||
field_name,
|
||
field_label,
|
||
file_id: draft_key,
|
||
file_name: original_name,
|
||
original_file_name: original_name,
|
||
uploaded_at: nowIso,
|
||
files_count, // Информация: сколько файлов было объединено
|
||
pages, // Информация: сколько страниц в объединённом PDF
|
||
});
|
||
|
||
filesRows.push({
|
||
claim_id,
|
||
group_index: grp,
|
||
file_index, // Всегда 0 для объединённого документа
|
||
original_name,
|
||
draft_key,
|
||
mime: 'application/pdf',
|
||
size_bytes: null,
|
||
description,
|
||
prefix,
|
||
field_name,
|
||
field_label,
|
||
files_count, // Информация для отладки
|
||
pages, // Информация для отладки
|
||
});
|
||
}
|
||
|
||
// ==== ПОДТЯГИВАЕМ ВСЁ ИЗ "Edit Fields" ====
|
||
const propertyName = editRaw?.propertyName || null;
|
||
const answers_parsed = body ? (tryParseJSON(body.answers) || null) : null;
|
||
const wizard_plan_parsed = body ? (tryParseJSON(body.wizard_plan) || null) : null;
|
||
|
||
// ==== OUTPUT ====
|
||
return [{
|
||
json: {
|
||
claim_id,
|
||
payload_partial_json: {
|
||
documents_meta,
|
||
edit_fields_raw: editRaw || null,
|
||
edit_fields_parsed: {
|
||
propertyName,
|
||
body,
|
||
uploads_descriptions,
|
||
uploads_field_names,
|
||
uploads_field_labels,
|
||
answers_parsed,
|
||
wizard_plan_parsed,
|
||
}
|
||
},
|
||
filesRows
|
||
}
|
||
}];
|