Files
aiform_dev/fix_draft_bddb6815_with_contract.py
AI Assistant 02689e65db fix: Исправление загрузки документов и SQL запросов
- Исправлена потеря документов при обновлении черновика (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
2025-11-26 19:54:51 +03:00

262 lines
12 KiB
Python
Raw Permalink 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.

#!/usr/bin/env python3
"""
Скрипт для исправления черновика bddb6815-8e17-4d54-a721-5e94382942c7
Добавляет documents_required и обновляет статус с учётом уже загруженного договора
"""
import asyncio
import asyncpg
import json
from datetime import datetime
# Параметры подключения к БД
POSTGRES_HOST = "147.45.189.234"
POSTGRES_PORT = 5432
POSTGRES_DB = "default_db"
POSTGRES_USER = "gen_user"
POSTGRES_PASSWORD = "2~~9_^kVsU?2\\S"
CLAIM_ID = "bddb6815-8e17-4d54-a721-5e94382942c7"
DOCUMENTS_REQUIRED = [
{
"id": "contract",
"name": "Договор или заказ",
"hints": "Фото или скан подписанного договора или квитанции",
"accept": ["pdf", "jpg", "png"],
"priority": 1,
"required": True
},
{
"id": "payment",
"name": "Чек или подтверждение оплаты",
"hints": "Копия кассового чека, онлайн-платежа или квитанции",
"accept": ["pdf", "jpg", "png"],
"priority": 1,
"required": True
},
{
"id": "correspondence",
"name": "Переписка",
"hints": "Скриншоты сообщений, писем, жалоб",
"accept": ["pdf", "jpg", "png"],
"priority": 2,
"required": False
},
{
"id": "evidence_photo",
"name": "Фото доказательства",
"hints": "Фото дефектов товара, видео процесса ремонта или передачи",
"accept": ["jpg", "png", "pdf"],
"priority": 2,
"required": False
}
]
async def fix_draft():
"""Исправляет черновик: добавляет documents_required и обновляет статус"""
conn = await asyncpg.connect(
host=POSTGRES_HOST,
port=POSTGRES_PORT,
database=POSTGRES_DB,
user=POSTGRES_USER,
password=POSTGRES_PASSWORD
)
try:
# Получаем текущее состояние черновика
row = await conn.fetchrow("""
SELECT id, status_code, payload
FROM clpr_claims
WHERE id::text = $1 OR payload->>'claim_id' = $1
ORDER BY updated_at DESC
LIMIT 1
""", CLAIM_ID)
if not row:
print(f"❌ Черновик {CLAIM_ID} не найден!")
return
payload = row['payload'] if isinstance(row['payload'], dict) else json.loads(row['payload'])
current_status = row['status_code']
print(f"📋 Текущее состояние черновика:")
print(f" - status_code: {current_status}")
print(f" - documents_required: {len(payload.get('documents_required', []))} шт.")
print(f" - documents_uploaded: {len(payload.get('documents_uploaded', []))} шт.")
print(f" - documents_meta: {len(payload.get('documents_meta', []))} шт.")
# Проверяем documents_meta на наличие загруженных документов
documents_meta = payload.get('documents_meta', [])
existing_documents_uploaded = payload.get('documents_uploaded', [])
# Функция для определения типа документа (сначала по field_label, потом по field_name)
def get_document_type(field_label: str, field_name: str) -> str:
field_label_lower = field_label.lower()
# ✅ СНАЧАЛА проверяем field_label (более точный способ)
if 'договор' in field_label_lower or 'заказ' in field_label_lower:
return 'contract'
elif 'чек' in field_label_lower or 'оплат' in field_label_lower:
return 'payment'
elif 'переписк' in field_label_lower:
return 'correspondence'
elif 'доказательств' in field_label_lower or 'фото' in field_label_lower:
return 'evidence_photo'
# ✅ ПОТОМ проверяем field_name (fallback)
elif 'uploads[0]' in field_name:
return 'contract'
elif 'uploads[1]' in field_name:
return 'payment'
elif 'uploads[2]' in field_name:
return 'correspondence'
elif 'uploads[3]' in field_name:
return 'evidence_photo'
else:
return 'unknown'
# ✅ Объединяем существующие documents_uploaded с documents_meta
# Создаём мапу file_id -> doc_meta для быстрого поиска
meta_by_file_id = {}
if documents_meta:
print(f"\n🔍 Найдено {len(documents_meta)} документов в documents_meta")
for doc_meta in documents_meta:
file_id = doc_meta.get('file_id', '')
if file_id:
meta_by_file_id[file_id] = doc_meta
# ✅ Пересоздаём documents_uploaded: объединяем существующие с данными из documents_meta
documents_uploaded = []
seen_file_ids = set()
# Сначала обрабатываем documents_meta (приоритет)
for doc_meta in documents_meta:
file_id = doc_meta.get('file_id', '')
if not file_id or file_id in seen_file_ids:
continue
field_label = doc_meta.get('field_label', '')
field_name = doc_meta.get('field_name', '')
doc_type = get_document_type(field_label, field_name)
if doc_type != 'unknown':
seen_file_ids.add(file_id)
documents_uploaded.append({
"id": doc_type,
"type": doc_type,
"file_id": file_id,
"file_name": doc_meta.get('file_name', ''),
"original_file_name": doc_meta.get('original_file_name', ''),
"uploaded_at": doc_meta.get('uploaded_at', datetime.utcnow().isoformat()),
"ocr_status": "completed",
"files_count": doc_meta.get('files_count', 1),
"pages": doc_meta.get('pages', None)
})
print(f" ✅ Из documents_meta: {doc_type} ({field_label}) - {doc_meta.get('original_file_name', 'N/A')}")
# Затем добавляем существующие documents_uploaded, которых нет в documents_meta
for existing_doc in existing_documents_uploaded:
file_id = existing_doc.get('file_id', '')
if not file_id or file_id in seen_file_ids:
continue
# Если есть в documents_meta - пропускаем (уже обработали)
if file_id in meta_by_file_id:
continue
# Используем существующий тип или пытаемся определить по file_name
doc_type = existing_doc.get('type') or existing_doc.get('id') or 'unknown'
# Если тип неправильный (например, contract вместо payment), пытаемся определить по file_name
if doc_type == 'contract' and 'chek' in file_id.lower():
doc_type = 'payment'
elif doc_type == 'contract' and 'dogovor' in file_id.lower():
doc_type = 'contract'
seen_file_ids.add(file_id)
documents_uploaded.append({
"id": doc_type,
"type": doc_type,
"file_id": file_id,
"file_name": existing_doc.get('file_name', ''),
"original_file_name": existing_doc.get('original_file_name', ''),
"uploaded_at": existing_doc.get('uploaded_at', datetime.utcnow().isoformat()),
"ocr_status": existing_doc.get('ocr_status', 'completed'),
"files_count": existing_doc.get('files_count', 1),
"pages": existing_doc.get('pages', None)
})
print(f" ✅ Из существующих: {doc_type} - {existing_doc.get('original_file_name', 'N/A')}")
# Определяем current_doc_index (индекс следующего документа для загрузки)
# Убираем дубликаты по типу документа
uploaded_types = list(set([doc.get('id') or doc.get('type') for doc in documents_uploaded]))
current_doc_index = 0
# Находим первый незагруженный документ
for idx, doc_req in enumerate(DOCUMENTS_REQUIRED):
if doc_req['id'] not in uploaded_types:
current_doc_index = idx
break
else:
# Все документы загружены
current_doc_index = len(DOCUMENTS_REQUIRED)
# Определяем новый статус (учитываем уникальные типы документов)
uploaded_unique_types = len(uploaded_types)
if uploaded_unique_types >= len(DOCUMENTS_REQUIRED):
new_status = 'draft_docs_complete'
elif uploaded_unique_types > 0:
new_status = 'draft_docs_progress'
else:
new_status = 'draft_new'
# Обновляем payload
payload['documents_required'] = DOCUMENTS_REQUIRED
payload['documents_uploaded'] = documents_uploaded
payload['current_doc_index'] = current_doc_index
print(f"\n📝 Обновление черновика:")
print(f" - documents_required: {len(DOCUMENTS_REQUIRED)} документов")
print(f" - documents_uploaded: {len(documents_uploaded)} документов")
print(f" - current_doc_index: {current_doc_index} (следующий документ: {DOCUMENTS_REQUIRED[current_doc_index]['name'] if current_doc_index < len(DOCUMENTS_REQUIRED) else 'все загружены'})")
print(f" - status_code: {current_status}{new_status}")
# Обновляем черновик
await conn.execute("""
UPDATE clpr_claims
SET
status_code = $1,
payload = $2::jsonb,
updated_at = now()
WHERE id::text = $3 OR payload->>'claim_id' = $3
""", new_status, json.dumps(payload, ensure_ascii=False), CLAIM_ID)
print(f"\n✅ Черновик исправлен!")
# Проверяем результат
row_after = await conn.fetchrow("""
SELECT
id::text,
status_code,
jsonb_array_length(COALESCE(payload->'documents_required', '[]'::jsonb)) as docs_required_count,
jsonb_array_length(COALESCE(payload->'documents_uploaded', '[]'::jsonb)) as docs_uploaded_count,
(payload->>'current_doc_index')::int as current_doc_index
FROM clpr_claims
WHERE id::text = $1 OR payload->>'claim_id' = $1
ORDER BY updated_at DESC
LIMIT 1
""", CLAIM_ID)
print(f"\n📊 Результат:")
print(f" - status_code: {row_after['status_code']}")
print(f" - documents_required count: {row_after['docs_required_count']}")
print(f" - documents_uploaded count: {row_after['docs_uploaded_count']}")
print(f" - current_doc_index: {row_after['current_doc_index']}")
finally:
await conn.close()
if __name__ == "__main__":
asyncio.run(fix_draft())