-- ============================================================================ -- SQL для сохранения claim при пропуске документа (document skip) - НОВЫЙ ФЛОУ -- ============================================================================ -- Проблема: При пропуске документа нужно обновить documents_skipped и current_doc_index -- Решение: Добавляем документ в documents_skipped, обновляем current_doc_index и status_code -- ============================================================================ WITH partial AS ( SELECT $1::jsonb AS p, $2::text AS claim_id_str ), existing_claim AS ( SELECT id, session_token, unified_id, contact_id, phone, payload, status_code, created_at FROM clpr_claims WHERE id = (SELECT claim_id_str::uuid FROM partial) OR payload->>'claim_id' = (SELECT claim_id_str FROM partial) ORDER BY CASE WHEN id = (SELECT claim_id_str::uuid FROM partial) THEN 1 ELSE 2 END, updated_at DESC LIMIT 1 ), -- Парсим информацию о пропущенном документе skipped_document_info AS ( SELECT COALESCE( partial.p->>'document_type', partial.p->'body'->>'document_type', partial.p->'edit_fields_raw'->'body'->>'document_type', partial.p->'edit_fields_parsed'->'body'->>'document_type' ) AS document_type, COALESCE( partial.p->>'document_name', partial.p->'body'->>'document_name', partial.p->'edit_fields_raw'->'body'->>'document_name', partial.p->'edit_fields_parsed'->'body'->>'document_name' ) AS document_name, COALESCE( (partial.p->>'group_index')::int, (partial.p->'body'->>'group_index')::int, (partial.p->'edit_fields_raw'->'body'->>'group_index')::int, (partial.p->'edit_fields_parsed'->'body'->>'group_index')::int, NULL ) AS group_index FROM partial ), -- Парсим documents_required (или берём из БД) documents_required_parsed AS ( SELECT CASE WHEN partial.p->'documents_required' IS NOT NULL AND jsonb_typeof(partial.p->'documents_required') = 'array' THEN partial.p->'documents_required' WHEN EXISTS (SELECT 1 FROM existing_claim WHERE payload->'documents_required' IS NOT NULL) THEN (SELECT payload->'documents_required' FROM existing_claim) ELSE '[]'::jsonb END AS documents_required FROM partial ), -- Парсим documents_uploaded (или берём из БД) documents_uploaded_parsed AS ( SELECT CASE WHEN partial.p->'documents_uploaded' IS NOT NULL AND jsonb_typeof(partial.p->'documents_uploaded') = 'array' THEN partial.p->'documents_uploaded' WHEN EXISTS (SELECT 1 FROM existing_claim WHERE payload->'documents_uploaded' IS NOT NULL) THEN (SELECT payload->'documents_uploaded' FROM existing_claim) ELSE '[]'::jsonb END AS documents_uploaded FROM partial ), -- Парсим documents_skipped (или берём из БД) и ДОБАВЛЯЕМ/ОБНОВЛЯЕМ новый пропущенный документ documents_skipped_parsed AS ( SELECT CASE -- Если документ уже есть в списке пропущенных - ОБНОВЛЯЕМ его (добавляем group_index, если его нет) WHEN EXISTS ( SELECT 1 FROM existing_claim WHERE payload->'documents_skipped' @> jsonb_build_array( jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info) ) ) ) THEN ( -- Удаляем старую запись и добавляем новую с group_index SELECT jsonb_agg( CASE WHEN doc->>'id' = (SELECT document_type FROM skipped_document_info) THEN jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info), 'name', COALESCE((SELECT document_name FROM skipped_document_info), (SELECT document_type FROM skipped_document_info)), 'group_index', (SELECT group_index FROM skipped_document_info), 'skipped_at', COALESCE(doc->>'skipped_at', now()::text) ) ELSE doc END ) FROM existing_claim, jsonb_array_elements(payload->'documents_skipped') AS doc ) -- Добавляем новый пропущенный документ WHEN EXISTS (SELECT 1 FROM existing_claim WHERE payload->'documents_skipped' IS NOT NULL) THEN ( SELECT payload->'documents_skipped' || jsonb_build_array( jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info), 'name', COALESCE((SELECT document_name FROM skipped_document_info), (SELECT document_type FROM skipped_document_info)), 'group_index', (SELECT group_index FROM skipped_document_info), 'skipped_at', now()::text ) ) FROM existing_claim ) -- Создаём новый массив с пропущенным документом ELSE jsonb_build_array( jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info), 'name', COALESCE((SELECT document_name FROM skipped_document_info), (SELECT document_type FROM skipped_document_info)), 'group_index', (SELECT group_index FROM skipped_document_info), 'skipped_at', now()::text ) ) END AS documents_skipped FROM partial ), -- Парсим current_doc_index и ОБНОВЛЯЕМ его (увеличиваем на 1 или находим следующий непропущенный) current_doc_index_parsed AS ( SELECT CASE -- Если передан group_index - используем его + 1 WHEN (SELECT group_index FROM skipped_document_info) IS NOT NULL THEN (SELECT group_index FROM skipped_document_info) + 1 -- Иначе берём из payload и увеличиваем на 1 WHEN partial.p->'current_doc_index' IS NOT NULL THEN (partial.p->'current_doc_index')::int + 1 WHEN EXISTS (SELECT 1 FROM existing_claim WHERE payload->'current_doc_index' IS NOT NULL) THEN (SELECT (payload->'current_doc_index')::int FROM existing_claim) + 1 ELSE 1 END AS current_doc_index FROM partial ), -- Определяем правильный статус на основе прогресса документов status_code_resolved AS ( SELECT CASE -- Если есть documents_required - новый флоу WHEN (SELECT jsonb_array_length(documents_required) FROM documents_required_parsed) > 0 THEN CASE -- Все документы загружены или пропущены WHEN (SELECT jsonb_array_length(documents_uploaded) FROM documents_uploaded_parsed) + (SELECT jsonb_array_length(documents_skipped) FROM documents_skipped_parsed) >= (SELECT jsonb_array_length(documents_required) FROM documents_required_parsed) THEN 'draft_docs_complete' -- Документы обрабатываются (есть загруженные или пропущенные) WHEN (SELECT jsonb_array_length(documents_uploaded) FROM documents_uploaded_parsed) > 0 OR (SELECT jsonb_array_length(documents_skipped) FROM documents_skipped_parsed) > 0 THEN 'draft_docs_progress' -- Только описание ELSE 'draft_new' END -- Сохраняем существующий статус, если он новый WHEN EXISTS (SELECT 1 FROM existing_claim WHERE status_code IN ('draft_new', 'draft_docs_progress', 'draft_docs_complete', 'draft_claim_ready')) THEN (SELECT status_code FROM existing_claim) -- По умолчанию ELSE 'draft' END AS status_code FROM partial ), -- UPSERT claim claim_upsert AS ( INSERT INTO clpr_claims ( id, session_token, unified_id, contact_id, phone, channel, type_code, status_code, payload, created_at, updated_at, expires_at ) SELECT COALESCE((SELECT id FROM existing_claim), partial.claim_id_str::uuid), COALESCE( partial.p->>'session_id', partial.p->'body'->>'session_id', partial.p->'edit_fields_parsed'->'body'->>'session_id', partial.p->'edit_fields_raw'->'body'->>'session_id', (SELECT payload->>'session_id' FROM existing_claim), 'sess-unknown' ), COALESCE( partial.p->>'unified_id', partial.p->'body'->>'unified_id', partial.p->'edit_fields_parsed'->'body'->>'unified_id', partial.p->'edit_fields_raw'->'body'->>'unified_id', (SELECT unified_id FROM existing_claim) ), COALESCE( partial.p->>'contact_id', partial.p->'body'->>'contact_id', partial.p->'edit_fields_parsed'->'body'->>'contact_id', partial.p->'edit_fields_raw'->'body'->>'contact_id', (SELECT contact_id FROM existing_claim) ), COALESCE( partial.p->>'phone', partial.p->'body'->>'phone', partial.p->'edit_fields_parsed'->'body'->>'phone', partial.p->'edit_fields_raw'->'body'->>'phone', (SELECT phone FROM existing_claim) ), 'web_form', COALESCE(partial.p->>'type_code', 'consumer'), (SELECT status_code FROM status_code_resolved), jsonb_build_object( 'claim_id', partial.claim_id_str, -- Сохраняем существующие поля из payload 'problem_description', COALESCE( partial.p->>'problem_description', (SELECT payload->>'problem_description' FROM existing_claim) ), 'answers', COALESCE( partial.p->'answers', (SELECT payload->'answers' FROM existing_claim), '{}'::jsonb ), 'wizard_plan', COALESCE( partial.p->'wizard_plan', (SELECT payload->'wizard_plan' FROM existing_claim) ), -- ✅ Сохраняем documents_meta (если есть) 'documents_meta', COALESCE( partial.p->'documents_meta', (SELECT payload->'documents_meta' FROM existing_claim), '[]'::jsonb ), -- ✅ НОВЫЙ ФЛОУ: Обновляем documents_required, documents_uploaded, documents_skipped, current_doc_index 'documents_required', (SELECT documents_required FROM documents_required_parsed), 'documents_uploaded', (SELECT documents_uploaded FROM documents_uploaded_parsed), 'documents_skipped', (SELECT documents_skipped FROM documents_skipped_parsed), 'current_doc_index', (SELECT current_doc_index FROM current_doc_index_parsed), 'phone', COALESCE( partial.p->>'phone', (SELECT payload->>'phone' FROM existing_claim) ), 'email', COALESCE( partial.p->>'email', (SELECT payload->>'email' FROM existing_claim) ) ), COALESCE((SELECT created_at FROM existing_claim), now()), now(), now() + interval '14 days' FROM partial ON CONFLICT (id) DO UPDATE SET session_token = COALESCE(EXCLUDED.session_token, clpr_claims.session_token), unified_id = COALESCE(EXCLUDED.unified_id, clpr_claims.unified_id), contact_id = COALESCE(EXCLUDED.contact_id, clpr_claims.contact_id), phone = COALESCE(EXCLUDED.phone, clpr_claims.phone), -- ✅ Обновляем status_code на основе прогресса документов status_code = (SELECT status_code FROM status_code_resolved), -- ✅ Объединяем payload правильно: аккуратно обновляем критичные поля payload = jsonb_set( jsonb_set( jsonb_set( jsonb_set( -- Сначала берём существующий payload и объединяем с новым (без критичных полей) COALESCE(clpr_claims.payload, '{}'::jsonb) || (EXCLUDED.payload - 'documents_required' - 'documents_uploaded' - 'documents_skipped' - 'current_doc_index'), '{documents_required}', COALESCE( EXCLUDED.payload->'documents_required', clpr_claims.payload->'documents_required', '[]'::jsonb ), true ), '{documents_uploaded}', COALESCE( EXCLUDED.payload->'documents_uploaded', clpr_claims.payload->'documents_uploaded', '[]'::jsonb ), true ), '{documents_skipped}', -- ✅ ОБЪЕДИНЯЕМ documents_skipped (добавляем новый пропущенный документ или ОБНОВЛЯЕМ существующий) CASE -- Если новый документ уже есть в существующем списке - ОБНОВЛЯЕМ его (добавляем group_index) WHEN clpr_claims.payload->'documents_skipped' @> jsonb_build_array( jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info) ) ) THEN ( -- Обновляем существующую запись, добавляя group_index SELECT jsonb_agg( CASE WHEN doc->>'id' = (SELECT document_type FROM skipped_document_info) THEN jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info), 'name', COALESCE((SELECT document_name FROM skipped_document_info), (SELECT document_type FROM skipped_document_info)), 'group_index', (SELECT group_index FROM skipped_document_info), 'skipped_at', COALESCE(doc->>'skipped_at', now()::text) ) ELSE doc END ) FROM jsonb_array_elements(clpr_claims.payload->'documents_skipped') AS doc ) -- Добавляем новый пропущенный документ ELSE COALESCE(clpr_claims.payload->'documents_skipped', '[]'::jsonb) || jsonb_build_array( jsonb_build_object( 'id', (SELECT document_type FROM skipped_document_info), 'name', COALESCE((SELECT document_name FROM skipped_document_info), (SELECT document_type FROM skipped_document_info)), 'group_index', (SELECT group_index FROM skipped_document_info), 'skipped_at', now()::text ) ) END, true ), '{current_doc_index}', COALESCE( EXCLUDED.payload->'current_doc_index', -- Если не передан, увеличиваем существующий на 1 CASE WHEN clpr_claims.payload->'current_doc_index' IS NOT NULL THEN to_jsonb((clpr_claims.payload->'current_doc_index')::int + 1) ELSE to_jsonb(1) END ), true ), updated_at = now(), expires_at = now() + interval '14 days' RETURNING id, status_code, payload, unified_id, contact_id, phone, session_token ) -- Возвращаем результат SELECT jsonb_build_object( 'claim_id', cu.id::text, 'claim_id_str', (cu.payload->>'claim_id'), 'status_code', cu.status_code, 'unified_id', cu.unified_id, 'contact_id', cu.contact_id, 'phone', cu.phone, 'session_token', cu.session_token, 'payload', cu.payload, 'documents_skipped', cu.payload->'documents_skipped', 'current_doc_index', cu.payload->'current_doc_index' ) AS claim FROM claim_upsert cu;