Files
aiform_dev/docs/CLAIMSAVE_FINAL_SQL.md
AI Assistant d6b17baa7d feat: Add PostgreSQL fields and workflow for form without files
Database changes:
- Add unified_id, contact_id, phone columns to clpr_claims table
- Create indexes for fast lookup by these fields
- Migrate existing data from payload to new columns
- SQL migration: docs/SQL_ALTER_CLPR_CLAIMS_ADD_FIELDS.sql

SQL improvements:
- Simplify claimsave query: remove complex claim_lookup logic
- Use UPSERT (INSERT ON CONFLICT) with known claim_id
- Always return claim (fix NULL issue)
- Store unified_id, contact_id, phone directly in table columns
- SQL: docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql

Workflow enhancements:
- Add branch for form submissions WITHOUT files
- Create 6 new nodes: extract, prepare, save, redis, respond
- Separate flow for has_files=false in IF node
- Instructions: docs/N8N_FORM_GET_NO_FILES_INSTRUCTIONS.md
- Node config: docs/N8N_FORM_GET_NO_FILES_BRANCH.json

Migration stats:
- Total claims: 81
- With unified_id: 77
- Migrated from payload: 2

Next steps:
1. Add 6 nodes to n8n workflow form_get (ID: 8ZVMTsuH7Cmw7snw)
2. Connect TRUE branch of IF node to extract_webhook_data_no_files
3. Test form submission without files
4. Verify PostgreSQL save and Redis event
2025-11-21 15:57:18 +03:00

212 lines
6.9 KiB
Markdown
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 для ноды `claimsave_final`
## Текущая проблема
Нода `claimsave_final` использует `$2::uuid`, но получает строку `"CLM-2025-11-18-GEQ3KL"`, что вызывает ошибку.
## Особенности `claimsave_final`
1. Используется **после конвертации файлов в PDF** и загрузки в S3
2. Работает с `file_url` (URL файла в S3)
3. Обновляет только `documents_meta` в payload (не трогает `answers`)
4. Использует динамический префикс таблицы (для разных схем)
## Исправленный SQL запрос
```sql
-- $1 = payload_partial_json (jsonb)
-- $2 = claim_id (text, например "CLM-2025-11-18-GEQ3KL")
WITH partial AS (
SELECT $1::jsonb AS p, $2::text AS claim_id_str
),
-- Находим UUID по строковому claim_id
claim_lookup AS (
SELECT
COALESCE(
(SELECT id FROM clpr_claims WHERE payload->>'claim_id' = partial.claim_id_str LIMIT 1),
gen_random_uuid()
) AS claim_uuid
FROM partial
),
-- Если записи нет, создаем её (на всякий случай)
claim_created AS (
INSERT INTO clpr_claims (
id,
session_token,
channel,
type_code,
status_code,
payload,
created_at,
updated_at,
expires_at
)
SELECT
claim_lookup.claim_uuid,
COALESCE(partial.p->>'session_id', 'sess-' || gen_random_uuid()::text),
'web_form',
COALESCE(partial.p->>'type_code', 'consumer'),
'draft',
jsonb_build_object(
'claim_id', partial.claim_id_str,
'documents_meta', COALESCE(partial.p->'documents_meta', '[]'::jsonb)
),
now(),
now(),
now() + interval '14 days'
FROM partial, claim_lookup
WHERE NOT EXISTS (
SELECT 1 FROM clpr_claims WHERE id = claim_lookup.claim_uuid
)
ON CONFLICT (id) DO NOTHING
RETURNING id
),
-- Получаем финальный UUID
claim_final AS (
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM claim_created)
THEN (SELECT id FROM claim_created LIMIT 1)
ELSE claim_lookup.claim_uuid
END AS claim_uuid
FROM claim_lookup
),
-- Извлекаем документы из payload
docs AS (
SELECT
claim_final.claim_uuid::text AS claim_id, -- преобразуем UUID в строку для clpr_claim_documents
doc.field_name::text,
doc.file_id::text,
doc.file_name::text,
doc.original_file_name::text,
(doc.uploaded_at)::timestamptz AS uploaded_at,
doc.file_url::text
FROM partial, claim_final
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,
file_url text
)
),
-- Сохраняем/обновляем документы
upsert_docs AS (
INSERT INTO clpr_claim_documents
(claim_id, field_name, file_id, uploaded_at, file_name, original_file_name)
SELECT
claim_id,
field_name,
file_id,
uploaded_at,
file_name,
original_file_name
FROM docs
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
),
-- Обновляем payload (только documents_meta, не трогаем answers)
upd_claim AS (
UPDATE clpr_claims c
SET
payload = jsonb_set(
COALESCE(c.payload, '{}'::jsonb),
'{documents_meta}',
COALESCE((SELECT p->'documents_meta' FROM partial), '[]'::jsonb),
true
),
updated_at = now(),
expires_at = now() + interval '14 days'
FROM partial, claim_final
WHERE c.id = claim_final.claim_uuid
RETURNING c.id, c.payload
)
SELECT
(SELECT jsonb_build_object(
'claim_id', u.id::text,
'claim_id_str', (u.payload->>'claim_id'),
'payload', u.payload
) FROM upd_claim u LIMIT 1) AS claim,
(
SELECT jsonb_agg(
jsonb_build_object(
'id', u.id,
'field_name', u.field_name,
'file_id', u.file_id,
'file_url', d.file_url,
'file_name', d.file_name,
'original_file_name', d.original_file_name,
'uploaded_at', d.uploaded_at,
-- имя, которое безопасно отдавать во внешний API
'filename_for_upload',
COALESCE(
NULLIF(d.original_file_name, ''),
NULLIF(d.file_name, ''),
regexp_replace(d.file_id, '^.*/', '') -- хвост пути как запасной
)
)
)
FROM upsert_docs u
JOIN docs d
ON d.claim_id = u.claim_id
AND d.field_name = u.field_name
WHERE d.file_url IS NOT NULL AND d.file_url <> '' -- не показываем без URL
) AS documents;
```
## Изменения
1. **`$2::text` вместо `$2::uuid`**: Принимает строковый `claim_id`
2. **`claim_lookup` CTE**: Находит UUID по строковому `claim_id` из `payload->>'claim_id'`
3. **`claim_created` CTE**: Создает запись, если её нет (на всякий случай)
4. **`claim_final` CTE**: Получает финальный UUID (из созданной или существующей записи)
5. **`docs` CTE**: Преобразует UUID в строку для `clpr_claim_documents` (т.к. там `claim_id` имеет тип `character varying`)
6. **Убраны динамические префиксы**: Используется `clpr_claims` и `clpr_claim_documents` напрямую
## Параметры запроса
В n8n PostgreSQL Node:
```
Parameters:
$1 = {{ $json.payload_partial_json }} (JSONB)
$2 = {{ $json.claim_id }} (TEXT, строка "CLM-2025-11-18-GEQ3KL")
```
## Если нужен динамический префикс
Если всё-таки нужен динамический префикс таблицы (как в оригинале), можно использовать:
```sql
-- Вместо clpr_claims использовать:
{{ $('Edit Fields').item.json.propertyName.prefix }}claims
-- Вместо clpr_claim_documents использовать:
{{ $('Edit Fields').item.json.propertyName.prefix }}claim_documents
```
Но для `ticket_form` это не нужно, т.к. мы всегда работаем с `clpr_*` таблицами.
## Отличия от `claimsave`
1. **`claimsave`**: Сохраняет данные визарда (answers, wizard_plan, wizard_answers)
2. **`claimsave_final`**: Обновляет только `documents_meta` после обработки файлов, добавляет `file_url`
Оба запроса теперь используют строковый `claim_id` и правильно находят UUID.