-- Упрощённый UPSERT для сохранения claim с известным claim_id -- Используется в n8n workflow: form_get (нода claimsave) -- Дата: 2025-11-21 -- Описание: Простой INSERT/UPDATE для claim, т.к. claim_id уже известен -- Входные параметры: -- $1: payload_partial_json (jsonb) - данные формы с wizard_answers, wizard_plan, documents_meta -- $2: claim_id (text) - UUID заявки WITH partial AS ( SELECT $1::jsonb AS p, $2::text AS claim_id_str ), -- Парсим wizard_answers wizard_answers_parsed AS ( SELECT CASE WHEN partial.p->>'wizard_answers' IS NOT NULL THEN (partial.p->>'wizard_answers')::jsonb WHEN partial.p->'wizard_answers' IS NOT NULL AND jsonb_typeof(partial.p->'wizard_answers') = 'object' THEN partial.p->'wizard_answers' ELSE '{}'::jsonb END AS answers FROM partial ), -- Парсим wizard_plan wizard_plan_parsed AS ( SELECT CASE WHEN partial.p->>'wizard_plan' IS NOT NULL THEN (partial.p->>'wizard_plan')::jsonb WHEN partial.p->'wizard_plan' IS NOT NULL AND jsonb_typeof(partial.p->'wizard_plan') = 'object' THEN partial.p->'wizard_plan' ELSE NULL END AS wizard_plan 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 partial.claim_id_str::uuid, COALESCE(partial.p->>'session_id', 'sess-unknown'), partial.p->>'unified_id', partial.p->>'contact_id', partial.p->>'phone', 'web_form', COALESCE(partial.p->>'type_code', 'consumer'), CASE WHEN (SELECT answers->>'docs_exist' FROM wizard_answers_parsed) = 'true' THEN 'in_work' ELSE 'draft' END, jsonb_build_object( 'claim_id', partial.claim_id_str, 'answers', (SELECT answers FROM wizard_answers_parsed), 'documents_meta', COALESCE(partial.p->'documents_meta', '[]'::jsonb), 'wizard_plan', (SELECT wizard_plan FROM wizard_plan_parsed) ), COALESCE( (SELECT created_at FROM clpr_claims WHERE id = partial.claim_id_str::uuid), now() ), now(), now() + interval '14 days' FROM partial ON CONFLICT (id) DO UPDATE SET session_token = EXCLUDED.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 = EXCLUDED.status_code, payload = ( -- Сохраняем старые поля, которых нет в новом payload clpr_claims.payload - 'answers' - 'documents_meta' - 'wizard_plan' - 'claim_id' ) || EXCLUDED.payload, updated_at = now(), expires_at = now() + interval '14 days' RETURNING id, status_code, payload, unified_id, contact_id, phone, session_token ), -- UPSERT documents (если есть) docs_upsert AS ( INSERT INTO clpr_claim_documents ( claim_id, field_name, file_id, uploaded_at, file_name, original_file_name ) SELECT partial.claim_id_str AS claim_id, doc.field_name, doc.file_id, COALESCE((doc.uploaded_at)::timestamptz, now()), doc.file_name, doc.original_file_name FROM partial CROSS JOIN LATERAL jsonb_to_recordset( COALESCE(partial.p->'documents_meta', '[]'::jsonb) ) AS doc( field_name text, file_id text, file_name text, original_file_name text, uploaded_at text ) WHERE partial.p->'documents_meta' IS NOT NULL AND jsonb_array_length(partial.p->'documents_meta') > 0 ON CONFLICT (claim_id, field_name) DO UPDATE SET file_id = EXCLUDED.file_id, uploaded_at = EXCLUDED.uploaded_at, file_name = EXCLUDED.file_name, original_file_name = EXCLUDED.original_file_name RETURNING id, claim_id, field_name, file_id, file_name, original_file_name ) -- Возвращаем результат SELECT (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 ) FROM claim_upsert cu) AS claim, (SELECT jsonb_agg(jsonb_build_object( 'id', id, 'field_name', field_name, 'file_id', file_id, 'file_name', file_name, 'original_file_name', original_file_name )) FROM docs_upsert) AS documents; /* ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ: 1. Вызов с wizard_answers и wizard_plan: SELECT * FROM ... WHERE ... = ( '{ "session_id": "sess_xxx", "unified_id": "usr_xxx", "contact_id": "12345", "phone": "79262306381", "wizard_answers": "{\\"q1\\": \\"answer1\\"}", "wizard_plan": "{\\"questions\\": [...]}", "documents_meta": [ { "field_name": "uploads[0][0]", "file_id": "clientright/0/file.pdf", "file_name": "file.pdf", "original_file_name": "original.pdf", "uploaded_at": "2025-11-21T12:00:00Z" } ] }'::jsonb, 'uuid-here'::text ); 2. Вызов БЕЗ файлов (только answers): SELECT * FROM ... WHERE ... = ( '{ "session_id": "sess_xxx", "unified_id": "usr_xxx", "contact_id": "12345", "phone": "79262306381", "wizard_answers": "{\\"q1\\": \\"answer1\\"}", "wizard_plan": null, "documents_meta": [] }'::jsonb, 'uuid-here'::text ); РЕЗУЛЬТАТ: { "claim": { "claim_id": "uuid", "claim_id_str": "uuid", "status_code": "draft" or "in_work", "unified_id": "usr_xxx", "contact_id": "12345", "phone": "79262306381", "session_token": "sess_xxx", "payload": {...} }, "documents": [ { "id": "uuid", "field_name": "uploads[0][0]", "file_id": "clientright/0/file.pdf", "file_name": "file.pdf", "original_file_name": "original.pdf" } ] } */