diff --git a/SESSION_LOG_2025-10-28.md b/SESSION_LOG_2025-10-28.md
index 77c2e14..1979614 100644
--- a/SESSION_LOG_2025-10-28.md
+++ b/SESSION_LOG_2025-10-28.md
@@ -354,3 +354,710 @@ Channels: ocr_events:{claim_id}
**Автор:** AI Assistant (Claude Sonnet 4.5)
**Дата:** 28 октября 2025, 01:00 MSK
+---
+---
+
+# 📋 Лог сессии: Умная форма Step 2 с AI-обработкой документов
+
+**Дата:** 28 октября 2025 (13:00 - 17:00 MSK)
+**Задача:** Рефакторинг Step 2 в интеллектуальную форму с пошаговой загрузкой и AI-обработкой документов
+**Статус:** ✅ Успешно завершено
+
+---
+
+## 🎯 Основные задачи
+
+### 1. ✅ Улучшение UX на Step 1 (Policy)
+- Добавлены динамические кнопки в модалке OCR:
+ - **"Продолжить →"** при успешном распознавании → переход на Step 2
+ - **"Загрузить другой файл"** при ошибке → очистка и повтор
+- Добавлен **DEV MODE** панель с кнопкой быстрого перехода на Step 2 без валидации
+
+### 2. ✅ Рефакторинг Step 2 (Details)
+**Было:**
+- Ручной ввод всех полей (тип события, дата, номер рейса/поезда/парома)
+- Загрузка документов как дополнение к ручному вводу
+
+**Стало:**
+- **"Интеллектуальная форма"** — AI извлекает данные из документов
+- **Пошаговая загрузка** каждого документа с индивидуальной обработкой
+- **Модалка обработки** для каждого документа с результатами извлечения
+- Ручной ввод только при необходимости (fallback)
+
+### 3. ✅ Определение требований к документам
+
+#### Задержка рейса (`delay_flight`)
+1. **Посадочный талон ИЛИ Билет** (обязательно)
+ - `file_type: flight_delay_boarding_or_ticket`
+ - `event_type: flight_delay_boarding_or_ticket_processed`
+ - AI извлекает: номер рейса, дату, маршрут, ФИО, время вылета
+
+2. **Подтверждение задержки** (обязательно, до 3 файлов)
+ - `file_type: flight_delay_confirmation`
+ - `event_type: flight_delay_confirmation_processed`
+ - Справка от АК, email/SMS, ИЛИ фото табло
+ - AI извлекает: время задержки, причину, фактическое время вылета
+
+#### Отмена рейса (`cancel_flight`)
+1. **Билет** (обязательно)
+ - `file_type: flight_cancel_ticket`
+ - `event_type: flight_cancel_ticket_processed`
+
+2. **Уведомление об отмене** (обязательно, до 3 файлов)
+ - `file_type: flight_cancel_notice`
+ - `event_type: flight_cancel_notice_processed`
+ - Письмо/SMS от АК, фото табло
+
+#### Пропуск стыковки (`missed_connection`)
+1. **Рейс отправления: Посадочный талон ИЛИ Билет** (обязательно)
+ - `file_type: missed_connection_first_boarding_or_ticket`
+ - `event_type: missed_connection_first_boarding_or_ticket_processed`
+
+2. **Рейс прибытия: Билет на пропущенный рейс** (обязательно)
+ - `file_type: missed_connection_second_ticket`
+ - `event_type: missed_connection_second_ticket_processed`
+
+3. **Подтверждение задержки первого рейса** (опционально, до 3 файлов)
+ - `file_type: missed_connection_delay_proof`
+ - `event_type: missed_connection_delay_proof_processed`
+
+#### Задержка/отмена поезда (`delay_train`, `cancel_train`)
+1. **Билет** (обязательно)
+ - `file_type: train_delay_ticket` / `train_cancel_ticket`
+
+2. **Справка о задержке/отмене** (обязательно, до 3 файлов)
+ - `file_type: train_delay_certificate` / `train_cancel_certificate`
+ - Справка от ЖД, фото табло
+
+#### Задержка/отмена парома/круиза (`delay_ferry`, `cancel_ferry`)
+1. **Билет/Бронь** (обязательно)
+ - `file_type: ferry_delay_ticket` / `ferry_cancel_ticket`
+
+2. **Подтверждение задержки/отмены** (обязательно, до 3 файлов)
+ - `file_type: ferry_delay_confirmation` / `ferry_cancel_confirmation`
+ - Справка, email, фото расписания
+
+### 4. ✅ Уникальные `file_type` для каждого документа
+
+**Принцип:** Каждый тип документа → уникальный `file_type` → уникальный `event_type` в Redis
+
+```typescript
+// Пример для отмены рейса
+{
+ file_type: "flight_cancel_ticket" // → S3, n8n, DB
+ event_type: "flight_cancel_ticket_processed" // → Redis pub/sub
+}
+
+{
+ file_type: "flight_cancel_notice"
+ event_type: "flight_cancel_notice_processed"
+}
+```
+
+**Почему это важно:**
+- n8n разделяет потоки обработки по `file_type`
+- Разные AI промпты для каждого типа документа
+- Frontend слушает уникальный `event_type` для каждого документа
+
+### 5. ✅ Добавлены DEV MODE кнопки во все 3 шага
+
+**Step 1 (Policy):**
+- "Далее → (Step 2) [пропустить]" — авто-заполнение voucher и claim_id
+
+**Step 2 (Details):**
+- "← Назад (Step 1)" — возврат назад
+- "Далее → (Step 3) [пропустить]" — авто-заполнение eventType, incidentDate, transportNumber
+
+**Step 3 (Payment):**
+- "← Назад (Step 2)" — возврат назад
+- "✅ Автоподтверждение телефона [dev]" — автозаполнение всех полей + setIsPhoneVerified(true)
+- "🚀 Отправить [пропустить]" — автозаполнение + submit
+
+---
+
+## 🛠️ Технические изменения
+
+### Файл: `frontend/src/components/form/Step1Policy.tsx`
+
+#### Изменение 1: Динамические кнопки в модалке OCR
+
+**Было:**
+```typescript
+footer={[
+
+]}
+```
+
+**Стало:**
+```typescript
+footer={ocrModalContent === 'loading' ? null :
+ ocrModalContent?.success ? [
+
+ ] : [
+
+ ]
+}
+```
+
+#### Изменение 2: DEV MODE панель
+
+```typescript
+
+
+ 🔧 DEV MODE - Быстрая навигация (без валидации)
+
+
+
+```
+
+### Файл: `frontend/src/components/form/Step2Details.tsx`
+
+#### Полный рефакторинг!
+
+**Бэкап старой версии:** `Step2Details.OLD_MANUAL_INPUT.tsx`
+
+**Новая структура:**
+
+1. **`DOCUMENT_CONFIGS`** — конфигурация документов для каждого типа события:
+```typescript
+const DOCUMENT_CONFIGS = {
+ delay_flight: [
+ {
+ name: "Посадочный талон ИЛИ Билет",
+ field: "boarding_or_ticket",
+ file_type: "flight_delay_boarding_or_ticket",
+ required: true,
+ maxFiles: 1,
+ description: "Посадочный талон (boarding pass) или билет (ticket/booking)",
+ aiPromptFocus: "Извлеки: номер рейса, дату, маршрут, ФИО пассажира, время вылета"
+ },
+ // ... остальные документы
+ ],
+ cancel_flight: [...],
+ // ... остальные типы событий
+};
+```
+
+2. **Пошаговая загрузка документов:**
+```typescript
+const [currentDocIndex, setCurrentDocIndex] = useState(0);
+const currentDoc = requiredDocs[currentDocIndex];
+
+// После успешной загрузки
+if (currentDocIndex < requiredDocs.length - 1) {
+ setCurrentDocIndex(prev => prev + 1);
+} else {
+ // Все документы загружены
+ onNext();
+}
+```
+
+3. **Модалка обработки для каждого документа:**
+```typescript
+
+ {currentDocIndex < requiredDocs.length - 1
+ ? 'Продолжить к следующему документу →'
+ : 'Далее (Step 3) →'
+ }
+
+ ]}
+>
+ {processingModalContent === 'loading' ? (
+
+
+
Обрабатываем документ...
+
+ ) : (
+
+
+
{JSON.stringify(processingModalContent, null, 2)}
+
+ )}
+
+```
+
+4. **SSE для каждого документа с уникальным `event_type`:**
+```typescript
+const eventSource = new EventSource(
+ `${API_BASE_URL}/events/${claimId}?event_type=${currentDoc.file_type}_processed`
+);
+
+eventSource.onmessage = (event) => {
+ const result = JSON.parse(event.data);
+ if (result.event_type === `${currentDoc.file_type}_processed`) {
+ setProcessingModalContent(result.data);
+ }
+};
+```
+
+#### DEV MODE панель:
+```typescript
+
+
+
+
+```
+
+### Файл: `frontend/src/components/form/Step3Payment.tsx`
+
+#### DEV MODE панель (3 кнопки):
+```typescript
+
+
+
+
+
+```
+
+---
+
+## 🐛 Проблемы и их решения
+
+### Проблема 1: Синтаксические ошибки на фронте
+
+**Симптом:**
+```
+чета шляпа у нас на фронте
+```
+
+**Диагностика:**
+- Пользователь сообщил "что то не того"
+- Проверка файлов показала **дублирующийся код** после закрывающих тегов компонентов
+
+**Найденные проблемы:**
+
+1. **`Step1Policy.tsx`** (строки 659-820):
+ - Дублирован весь DEV MODE блок после `` компонента
+ - Код был просто скопирован повторно
+
+2. **`Step3Payment.tsx`** (после строки 381):
+ - Дублирован обрезанный фрагмент DEV панели
+ - Неполный JSX
+
+**Решение:**
+```bash
+# Удалены дублирующиеся блоки
+# Step1Policy.tsx: строки 659-820 удалены
+# Step3Payment.tsx: строки после 381 удалены
+
+# Rebuild frontend
+docker-compose build frontend
+docker-compose up -d frontend
+```
+
+**Коммиты:**
+- `2999951` - fix: Удалён дублирующийся код в Step1Policy.tsx
+- `1207222` - fix: Удалён дублирующийся код в Step3Payment.tsx
+
+### Проблема 2: PostgreSQL INSERT не возвращает данные в n8n
+
+**Симптом:**
+```json
+{
+ "s3_url": null,
+ "file_id": null,
+ "error": {
+ "message": "422 - \"{\\\"detail\\\":[{\\\"type\\\":\\\"string_type\\\",\\\"loc\\\":[\\\"body\\\",\\\"file_url\\\"],\\\"msg\\\":\\\"Input should be a valid string\\\",\\\"input\\\":null}]}\""
+ }
+}
+```
+
+**Причина:**
+1. `INSERT INTO claim_files` не вернул `file_id` и `s3_url`
+2. Выяснилось: запись в `claims` с данным `claim_number` не существует
+3. Foreign key `claim_id` не может быть установлен → INSERT падает
+4. `file_size` передан как `"4.47 MB"` вместо числа в байтах
+
+**Решение:**
+Создан UPSERT запрос с CTE (Common Table Expression):
+
+```sql
+WITH upserted_claim AS (
+ INSERT INTO claims (
+ claim_number, voucher, session_id, status, created_at, updated_at
+ ) VALUES (
+ $1, $2, $3, 'draft', NOW(), NOW()
+ )
+ ON CONFLICT (claim_number)
+ DO UPDATE SET
+ updated_at = NOW(),
+ voucher = COALESCE(EXCLUDED.voucher, claims.voucher)
+ RETURNING id as claim_id
+)
+INSERT INTO claim_files (
+ claim_id, file_type, original_name, s3_key, s3_url,
+ file_size, mime_type, ocr_status, uploaded_at
+)
+SELECT
+ upserted_claim.claim_id,
+ $4, $5, $6, $7, $8, $9, 'pending', NOW()
+FROM upserted_claim
+RETURNING id as file_id, s3_url, ocr_status;
+```
+
+**Параметры:**
+```javascript
+[
+ claim_number, // $1
+ voucher, // $2
+ session_id, // $3
+ file_type, // $4
+ original_name, // $5
+ s3_key, // $6
+ s3_url, // $7
+ file_size, // $8 (число в байтах!)
+ mime_type // $9
+]
+```
+
+**Преимущества:**
+- ✅ Атомарная операция
+- ✅ Идемпотентность (можно запускать повторно)
+- ✅ Всегда создаёт `claims` если его нет
+- ✅ Обновляет `updated_at` если уже есть
+- ✅ Возвращает `file_id` и `s3_url` для следующих шагов
+
+---
+
+## ✅ Тестирование
+
+### Тест 1: Загрузка билета на отмену рейса
+
+**Файл:** "Билет Романова.pdf"
+**Claim ID:** CLM-2025-10-28-33ID32
+**file_type:** `flight_cancel_ticket`
+
+**Результат Redis:**
+```json
+{
+ "claim_id": "CLM-2025-10-28-33ID32",
+ "event": {
+ "event_type": "flight_cancel_ticket_processed",
+ "status": "completed",
+ "message": "✅ Документ обработан: flight_cancel_ticket",
+ "data": {
+ "output": {
+ "is_flight_doc": "yes",
+ "document_type": "e-ticket",
+ "ticket_number": "2222411714956",
+ "passengers": [{
+ "full_name": "ROMANOVA ANASTASIIA",
+ "doc_number": "774099576"
+ }],
+ "itinerary": [{
+ "flight_number": "A4-6025",
+ "departure": {
+ "airport_iata": "MRV",
+ "date_local": "2025-09-30",
+ "time_local": "16:25"
+ },
+ "arrival": {
+ "airport_iata": "TLV",
+ "time_local": "20:00"
+ }
+ }]
+ }
+ }
+ }
+}
+```
+
+**Backend лог:**
+```
+16:46:29 - 📥 Received message type: message
+16:46:29 - 📦 Raw event data: {"claim_id":"CLM-2025-10-28-33ID32",...}
+16:46:29 - 📦 Unwrapped n8n Redis format for CLM-2025-10-28-33ID32
+16:46:29 - 📤 Sending event to client: completed
+16:46:29 - ✅ Task CLM-2025-10-28-33ID32 finished, closing SSE
+```
+
+**Результат:** ✅ Полный успех!
+- S3 upload ✅
+- PostgreSQL UPSERT ✅
+- OCR/AI обработка ✅
+- Redis publish ✅
+- Backend SSE ✅
+- Frontend получил данные ✅
+
+---
+
+## 📊 Архитектура Step 2 (новая)
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Step 2: Details (NEW) │
+│ │
+│ 1. Выбор типа события (eventType) │
+│ ↓ │
+│ 2. DOCUMENT_CONFIGS определяет список документов │
+│ ↓ │
+│ 3. Пошаговая загрузка каждого документа: │
+│ │
+│ ┌────────────────────────────────────────────────────┐ │
+│ │ Документ 1: Посадочный талон │ │
+│ │ - Upload компонент │ │
+│ │ - POST /upload → n8n webhook │ │
+│ │ - file_type: "flight_delay_boarding_or_ticket" │ │
+│ │ - SSE: event_type = "..._processed" │ │
+│ │ - Модалка: "Обрабатываем..." │ │
+│ │ - Результат: extracted data │ │
+│ │ - Кнопка: "Продолжить к следующему" │ │
+│ └────────────────────────────────────────────────────┘ │
+│ ↓ │
+│ ┌────────────────────────────────────────────────────┐ │
+│ │ Документ 2: Подтверждение задержки │ │
+│ │ - (аналогично) │ │
+│ │ - file_type: "flight_delay_confirmation" │ │
+│ │ - Может быть до 3 файлов │ │
+│ │ - Кнопка: "Далее (Step 3)" │ │
+│ └────────────────────────────────────────────────────┘ │
+│ │
+│ 4. После загрузки всех документов → Step 3 │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Data Flow для одного документа:
+
+```
+Frontend n8n Backend Redis
+ │ │ │ │
+ │ POST /upload │ │ │
+ ├────────────────────>│ │ │
+ │ {claim_id, │ │ │
+ │ file_type, │ │ │
+ │ file} │ │ │
+ │ │ │ │
+ │ SSE connect │ │ │
+ ├────────────────────────────────────────────>│ │
+ │ /events/CLM-XXX? │ │ │
+ │ event_type= │ │ │
+ │ flight_..._processed│ │ │
+ │ │ │ │
+ │ │ 1. Upload to S3 │ │
+ │ │ 2. UPSERT claims │ │
+ │ │ 3. INSERT claim_files │ │
+ │ │ 4. OCR Service │ │
+ │ │ 5. AI Vision │ │
+ │ │ 6. PUBLISH │ │
+ │ ├────────────────────────────────────────────>│
+ │ │ {event_type: │ │
+ │ │ "..._processed", │ │
+ │ │ data: {...}} │ │
+ │ │ │ │
+ │ │ │<──────────────────┤
+ │ │ │ SUBSCRIBE │
+ │ │ │ ocr_events:CLM-XXX │
+ │<────────────────────────────────────────────┤ │
+ │ SSE: data: {event_type, data} │ │
+ │ │ │ │
+ │ Show modal: │ │ │
+ │ "✅ Обработано" │ │ │
+ │ Display data │ │ │
+ │ │ │ │
+ │ User clicks │ │ │
+ │ "Continue" → │ │ │
+ │ next document │ │ │
+ │ (or Step 3) │ │ │
+```
+
+---
+
+## 🎯 Логика обработки результатов AI (спроектирована)
+
+### Предложенная структура валидации:
+
+```typescript
+const handleOcrResult = (event) => {
+ const { output } = event.data;
+
+ // Проверка 1: Это правильный тип документа?
+ if (output.is_flight_doc !== "yes") {
+ return { success: false, message: "❌ Это не авиадокумент" };
+ }
+
+ // Проверка 2: Извлечены ли критичные данные?
+ const firstFlight = output.itinerary?.[0];
+ const criticalFields = {
+ flightNumber: firstFlight?.flight_number,
+ departureDate: firstFlight?.departure?.date_local,
+ departureAirport: firstFlight?.departure?.airport_iata,
+ arrivalAirport: firstFlight?.arrival?.airport_iata,
+ passengerName: output.passengers?.[0]?.full_name
+ };
+
+ const missing = Object.entries(criticalFields)
+ .filter(([_, value]) => !value)
+ .map(([key]) => key);
+
+ if (missing.length === 0) {
+ return { success: true, message: "✅ Билет распознан успешно!" };
+ } else {
+ return {
+ success: "partial",
+ message: "⚠️ Билет распознан, но не хватает данных",
+ missingFields: missing
+ };
+ }
+};
+```
+
+### 3 сценария UI:
+
+**SUCCESS:** Все данные извлечены
+- ✅ Показать извлечённые данные
+- Кнопка: "Продолжить к следующему документу →"
+
+**PARTIAL:** Документ валидный, но данные неполные
+- ⚠️ Показать что извлечено + что отсутствует
+- 3 кнопки:
+ 1. "📸 Загрузить документ лучшего качества"
+ 2. "✍️ Ввести недостающие данные вручную"
+ 3. "Продолжить с доступными данными"
+
+**FAIL:** Неправильный тип документа
+- ❌ Ошибка
+- 2 кнопки:
+ 1. "Загрузить другой файл"
+ 2. "Ввести данные вручную"
+
+---
+
+## 📝 Git Commits
+
+```bash
+# Commit history (от старого к новому)
+6fe1459 - backup: Сохранён старый Step2Details с ручным вводом полей
+122af07 - feat: Умная форма Step2 с автоматическим распознаванием документов
+9084d75 - feat: Пошаговая загрузка документов с модалкой на Step 2
+2999951 - fix: Удалён дублирующийся код в Step1Policy.tsx
+1207222 - fix: Удалён дублирующийся код в Step3Payment.tsx
+```
+
+**Push:** ✅ `origin/main` (все коммиты)
+
+---
+
+## 📈 Метрики
+
+**Время выполнения сессии:** ~4 часа
+**Количество коммитов:** 5
+**Изменённых файлов:** 4 (Step1Policy, Step2Details, Step2Details.OLD, Step3Payment)
+**Строк добавлено:** ~800
+**Строк удалено:** ~200 (дубликаты) + ~400 (рефакторинг Step2)
+
+**Frontend rebuilds:** 3
+**Тестовых загрузок:** 3
+**Redis событий обработано:** 3
+
+---
+
+## 🔗 Ссылки
+
+- Frontend: http://147.45.146.17:5173
+- Backend API: http://localhost:8100
+- Gitea: http://147.45.146.17:3002/negodiy/erv-platform
+- n8n: http://147.45.146.17:5678
+- N8N SQL Queries: `/erv_platform/N8N_SQL_QUERIES.md`
+
+---
+
+## 📝 Важные заметки
+
+### Redis Password (обновлено)
+```
+Host: crm.clientright.ru
+Port: 6379
+Password: CRM_Redis_Pass_2025_Secure!
+(из /etc/redis/redis.conf)
+```
+
+### PostgreSQL UPSERT для n8n
+Сохранён в `N8N_SQL_QUERIES.md` для использования в webhook nodes.
+
+### Структура `file_type` → `event_type`
+```
+file_type: "flight_cancel_ticket"
+event_type: "flight_cancel_ticket_processed"
+
+Формула: event_type = file_type + "_processed"
+```
+
+### DEV MODE
+Все три шага имеют панель для быстрой навигации без заполнения форм — ускоряет разработку и тестирование.
+
+---
+
+**Статус:** ✅ Успешно завершено
+**Автор:** AI Assistant (Claude Sonnet 4.5)
+**Дата:** 28 октября 2025, 17:00 MSK
+