Files
aiform_dev/docs/SQL_CLAIMSAVE_DOCUMENT_SKIP.sql
AI Assistant 080e7ec105 feat: Получение cf_2624 из MySQL и блокировка полей при подтверждении данных
- Добавлен сервис CrmMySQLService для прямого подключения к MySQL CRM
- Обновлён метод get_draft() для получения cf_2624 напрямую из БД
- Реализована блокировка полей (readonly) при contact_data_confirmed = true
- Добавлен выбор банка для СБП выплат с динамической загрузкой из API
- Обновлена документация по работе с cf_2624 и MySQL
- Добавлен network_mode: host в docker-compose для доступа к MySQL
- Обновлены компоненты формы для поддержки блокировки полей
2025-12-04 12:22:23 +03:00

380 lines
16 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ============================================================================
-- 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;