Files
aiform_prod/SESSION_LOG_2025-11-01.md
AI Assistant 4c8fda5f55 Добавлено логирование для отладки черновиков
- Добавлены логи в frontend (ClaimForm.tsx) для отслеживания unified_id и запросов к API
- Добавлены логи в backend (claims.py) для отладки SQL запросов
- Создан лог сессии с описанием проблемы и текущего состояния
- Проблема: API возвращает 0 черновиков, хотя в БД есть данные
2025-11-19 18:46:48 +03:00

38 KiB
Raw Permalink Blame History

📋 Лог сессии: CreateWebProject + Интеграция SMS → CRM

Дата: 01 ноября 2025 (10:00 - 13:30 MSK)
Задачи:

  1. Создание операции CreateWebProject для vTiger CRM
  2. Исправление валидации SMS кодов
  3. Интеграция n8n webhook для создания контакта после SMS верификации

Статус: Успешно завершено


🎯 Основные задачи

Задача 1: CreateWebProject

Создать операцию vTiger webservice для создания проекта по аналогии с CreateWebContact.

Требования:

  • Обязательные поля: policy_number (cf_1885), contact_id
  • Опциональные: period_start (cf_1887), period_end (cf_1889)
  • Логика: если проект с таким полисом существует → возврат ID без обновления
  • Если не существует → создание нового
  • Возврат: {"project_id": "123", "is_new": true/false}

Задача 2: SMS валидация

Исправить проблему с валидацией SMS кодов (формат телефона).

Задача 3: n8n интеграция

Добавить вызов n8n webhook после SMS верификации для создания контакта в CRM.


Выполненные задачи

1. CreateWebProject.php - Операция vTiger Webservice

Файл: include/Webservices/CreateWebProject.php

Обязательные параметры:

  • policy_number - номер полиса ERV (cf_1885)
  • contact_id - ID контакта для привязки

Опциональные параметры:

  • period_start - дата начала страхования (cf_1887)
  • period_end - дата окончания страхования (cf_1889)

Логика работы:

1. Ищем проект по номеру полиса (cf_1885):
   SELECT p.projectid FROM vtiger_project p
   INNER JOIN vtiger_projectcf pcf ON p.projectid = pcf.projectid
   WHERE e.deleted = 0 AND pcf.cf_1885 = 'E1000-123456789'

2. Если найден  возвращаем ID БЕЗ обновления:
   {"project_id": "396865", "is_new": false}

3. Если НЕ найден  создаём новый:
   - projectname: "ERV E1000-123456789 цифровой адвокат"
   - projectstatus: "модерация"
   - projecttype: "ерв урегулирование"
   - linktoaccountscontacts: "12x{contact_id}"
   - cf_1994: "11x67458" (Заявитель - контрагент)
   - cf_1885: номер полиса
   
   {"project_id": "396866", "is_new": true}

Регистрация в БД:

-- vtiger_ws_operation
INSERT INTO vtiger_ws_operation (
  operationid, name, handler_path, handler_method, type, prelogin
) VALUES (
  51, 
  'CreateWebProject', 
  'include/Webservices/CreateWebProject.php', 
  'vtws_createwebproject', 
  'POST', 
  0
);

-- vtiger_ws_operation_parameters
INSERT INTO vtiger_ws_operation_parameters (operationid, name, type, sequence)
VALUES 
  (51, 'policy_number', 'String', 1),
  (51, 'contact_id', 'String', 2),
  (51, 'period_start', 'String', 3),
  (51, 'period_end', 'String', 4);

Тестирование:

# Тест 1: Создание нового проекта
Policy: E1000-TEST-1761990646
Contact: 396625
Result: {"project_id":"396865","is_new":true}# Тест 2: Повторный вызов (поиск существующего)
Policy: E1000-TEST-1761990646
Contact: 396625
Result: {"project_id":"396865","is_new":false}

Логи: logs/CreateWebProject.log


2. Исправление валидации SMS кодов

Проблема:

При отправке: ключ в Redis = erv:sms_verify:+79262306381 (С ПЛЮСОМ)
При проверке: поиск ключа = sms_verify:79262306381 (БЕЗ ПЛЮСА)
Результат: "No verification code found" ❌

Причина: Несоответствие формата телефона между отправкой и проверкой.

Решение:

Файл: backend/app/services/sms_service.py

async def send_verification_code(self, phone: str) -> Optional[str]:
    # Нормализуем формат телефона (убираем + если есть)
    phone = phone.replace("+", "").replace("-", "").replace(" ", "")
    
    verification_key = f"sms_verify:{phone}"  # → sms_verify:79262306381
    await redis_service.set(verification_key, code, expire=600)
    ...

async def verify_code(self, phone: str, code: str) -> bool:
    # Нормализуем формат телефона (убираем + если есть)
    phone = phone.replace("+", "").replace("-", "").replace(" ", "")
    
    verification_key = f"sms_verify:{phone}"  # → sms_verify:79262306381
    stored_code = await redis_service.get(verification_key)
    ...

Дополнительно:

  • Отключен rate limiting (60 сек задержка) для тестирования
  • Добавлено детальное логирование сравнения кодов
  • Backend подключён к внешнему Redis: crm.clientright.ru:6379

Проверка:

# До исправления
redis-cli> GET "erv:sms_verify:+79262306381"  # Ключ с +
"123456"
# Проверка ищет без + → не находит ❌

# После исправления
redis-cli> GET "erv:sms_verify:79262306381"  # Ключ без +
"123456"
# Проверка ищет без + → находит ✅

3. Интеграция n8n webhook после SMS верификации

Проблема: После SMS верификации контакт не создавался в CRM автоматически.

Решение:

Файл: frontend/src/components/form/Step1Phone.tsx

// После успешной SMS верификации
if (response.ok) {
  addDebugEvent?.('sms', 'success', `✅ Телефон подтвержден успешно`);
  message.success('Телефон подтвержден!');
  setIsPhoneVerified(true);
  
  // 🆕 Вызов n8n webhook для создания контакта
  try {
    addDebugEvent?.('crm', 'info', '📞 Создание контакта в CRM...');
    
    const crmResponse = await fetch(
      'https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27', 
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ phone })  // 79001234567
      }
    );
    
    const crmResult = await crmResponse.json();
    
    if (crmResponse.ok) {
      addDebugEvent?.('crm', 'success', `✅ Контакт создан/найден в CRM`, crmResult);
      
      // Сохраняем данные из CRM в форму
      updateFormData({
        phone,
        contact_id: crmResult.contact_id,
        claim_id: crmResult.claim_id,
        is_new_contact: crmResult.is_new_contact
      });
      
      message.success(crmResult.is_new_contact ? 'Контакт создан!' : 'Контакт найден!');
      onNext();
    } else {
      addDebugEvent?.('crm', 'error', '❌ Ошибка создания контакта в CRM');
      message.error('Ошибка создания контакта в CRM');
    }
  } catch (crmError) {
    addDebugEvent?.('crm', 'error', '❌ Ошибка соединения с CRM');
    message.error('Ошибка соединения с CRM');
  }
}

Workflow n8n (get_contact_CRM):

Webhook: https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27

Input: {"phone": "79001234567"}

Флоу:
1. Edit Fields (извлечение phone)
   ↓
2. Get Challenge (vTiger webservice)
   ↓
3. Execute a command (md5 hash)
   ↓
4. Login to CRM
   ↓
5. CreateWebContact (создание/поиск контакта)
   ↓
6. Code in JavaScript (парсинг JSON + генерация claim_id)
   ↓
7. Redis (сохранение session:claim:{claim_id})
   ↓
8. Respond to Webhook

Output: {
  "claim_id": "CLM-2025-11-01-XXXXX",
  "contact_id": "396625",
  "is_new_contact": false,
  "phone": "79001234567"
}

Redis Session:

Ключ: claim:CLM-2025-11-01-IWR1U2
TTL: 604800 секунд (7 дней)
Значение: {
  "claim_id": "CLM-2025-11-01-IWR1U2",
  "contact_id": "396625",
  "phone": "79001234567",
  "is_new_contact": false,
  "status": "draft",
  "current_step": 1,
  "created_at": "2025-11-01T10:15:32.123Z",
  "updated_at": "2025-11-01T10:15:32.123Z",
  "voucher": null,
  "event_type": null,
  "documents": {}
}

4. Обновлён FormData интерфейс

Файл: frontend/src/pages/ClaimForm.tsx

interface FormData {
  // 🆕 Шаг 1: Phone
  phone?: string;
  contact_id?: string;
  is_new_contact?: boolean;
  
  // Шаг 2: Policy
  voucher: string;
  claim_id?: string;
  session_id?: string;
  
  // Шаг 3: Event Type
  eventType?: string;
  
  // Шаги 4+: Documents
  documents?: Record<string, {...}>;
  
  // Последний шаг: Payment
  fullName?: string;
  email?: string;
  paymentMethod?: string;
  bankName?: string;
  cardNumber?: string;
  accountNumber?: string;
}

5. Исправлён docker-compose.yml

Проблемы:

  1. Backend пытался подключиться к локальному Redis (localhost:6379)
  2. Попытка запуска локальных контейнеров redis/postgres, которые не нужны

Решение:

backend:
  build: ./backend
  ports:
    - "8100:8100"
  environment:
    # 🆕 Подключение к внешнему Redis
    - REDIS_HOST=crm.clientright.ru
    - REDIS_PORT=6379
    - REDIS_PASSWORD=CRM_Redis_Pass_2025_Secure!
    - POSTGRES_URL=postgresql://erv_user:erv_password@postgres:5432/erv_db
    - RABBITMQ_URL=amqp://admin:tyejvtej@185.197.75.249:5672
  # 🆕 Убраны зависимости от локальных сервисов
  # depends_on:
  #   - redis
  #   - postgres
  networks:
    - erv-network
  restart: unless-stopped

Статус сервисов:

  • Backend подключён к внешнему Redis (crm.clientright.ru:6379)
  • Backend подключён к внешнему PostgreSQL (147.45.189.234:5432)
  • RabbitMQ подключён (185.197.75.249:5672)
  • S3 подключён (Timeweb Cloud Storage)

🐛 Исправленные проблемы

Проблема 1: UNKNOWN_OPERATION для CreateWebProject

Симптом:

{"success":false,"error":{"code":"UNKNOWN_OPERATION","message":"Unknown operation requested"}}

Причина: Неправильная структура регистрации в БД.

Было:

INSERT INTO vtiger_ws_operation 
VALUES (51, 'CreateWebProject', 'include/Webservices/CreateWebProject.php', ...)
-- Поле 'handler' вместо 'handler_path' и 'handler_method'

Стало:

INSERT INTO vtiger_ws_operation (
  operationid, name, handler_path, handler_method, type, prelogin
) VALUES (
  51, 
  'CreateWebProject', 
  'include/Webservices/CreateWebProject.php',
  'vtws_createwebproject',  -- ⭐ Имя функции!
  'POST', 
  0
);

Решение: Используем правильные поля handler_path и handler_method.


Проблема 2: BOM символ в CreateWebProject.php

Симптом:

{"success":true,"result":...}

Причина: Файл сохранён с UTF-8 BOM.

Решение:

sed -i '1s/^\xEF\xBB\xBF//' include/Webservices/CreateWebProject.php

Проблема 3: SMS код не валидируется

Симптом:

Отправка: +79262306381
Redis key: erv:sms_verify:+79262306381

Проверка: 79262306381
Redis key: sms_verify:79262306381

Результат: "No verification code found"

Решение: Нормализация телефона в обоих методах (убираем +, -, пробелы).


Проблема 4: Backend не подключается к Redis

Симптом:

❌ Redis connection error: Error connecting to localhost:6379

Причина:

  • docker-compose.yml имел REDIS_URL=redis://redis:6379
  • Backend пытался подключиться к локальному Redis
  • Локальный Redis конфликтовал с внешним на порту 6379

Решение:

environment:
  - REDIS_HOST=crm.clientright.ru
  - REDIS_PORT=6379
  - REDIS_PASSWORD=CRM_Redis_Pass_2025_Secure!

Проблема 5: Step1Phone не отображается на первом шаге

Симптом: Поле телефона пропало с первой страницы.

Причина: Забыл добавить импорт и регистрацию в steps массиве.

Решение:

// ClaimForm.tsx
import Step1Phone from '../components/form/Step1Phone';

const steps = useMemo(() => {
  const stepsArray: any[] = [];
  
  // 🆕 Шаг 1: Phone
  stepsArray.push({
    title: 'Телефон',
    description: 'Подтверждение по SMS',
    content: <Step1Phone ... />
  });
  
  // Шаг 2: Policy
  stepsArray.push({
    title: 'Проверка полиса',
    ...
  });
  ...
}, [...]);

Проблема 6: n8n webhook не вызывается

Симптом: После SMS верификации контакт не создаётся в CRM.

Причина:

  1. Код добавлен на хосте, но не попал в Docker контейнер
  2. Frontend не был пересобран

Решение:

cd erv_platform
docker-compose down frontend
docker-compose up -d --build frontend

Проверка:

docker exec -i erv_platform_frontend_1 sh -c \
  "grep -n 'n8n.clientright.pro/webhook' /app/src/components/form/Step1Phone.tsx"

# Результат:
94: const crmResponse = await fetch('https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27', {

📊 Итоговая архитектура

Флоу создания заявки:

┌─────────────────────────────────────────────────────────────┐
│ Шаг 1: Телефон + SMS                                        │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. Пользователь вводит телефон: 9001234567             │ │
│ │ 2. Frontend → POST /api/v1/sms/send                     │ │
│ │ 3. Backend генерирует код → Redis                       │ │
│ │ 4. Пользователь вводит код из SMS                       │ │
│ │ 5. Frontend → POST /api/v1/sms/verify                   │ │
│ │ 6. Backend проверяет код в Redis                        │ │
│ │ 7. ✅ Код верный → вызов n8n webhook                    │ │
│ │ 8. n8n → CreateWebContact (CRM)                         │ │
│ │ 9. n8n генерирует claim_id                              │ │
│ │ 10. n8n сохраняет сессию в Redis                        │ │
│ │ 11. n8n → Response: {contact_id, claim_id, is_new}      │ │
│ │ 12. Frontend сохраняет данные в formData                │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Шаг 2: Полис                                                │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. Пользователь вводит номер полиса                     │ │
│ │ 2. Frontend → n8n webhook (проверка полиса)             │ │
│ │ 3. n8n → CreateWebProject (CRM)                         │ │
│ │ 4. n8n обновляет Redis session                          │ │
│ │ 5. Response: {project_id, is_new, period_start/end}     │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Шаг 3: Тип события → Шаги 4+: Документы → Последний: Оплата│
└─────────────────────────────────────────────────────────────┘

📁 Структура файлов

CRM (vTiger):

include/Webservices/
├── CreateWebContact.php      (operation_id: 50) ✅
├── CreateWebProject.php       (operation_id: 51) 🆕
└── CreateERVTicket.php        (будущее)

logs/
├── CreateWebContact.log
└── CreateWebProject.log       🆕

CREATE_WEB_PROJECT_DOCS.md     🆕

ERV Platform:

backend/app/services/
└── sms_service.py             🔧 Исправлена нормализация телефона

frontend/src/components/form/
├── Step1Phone.tsx             🔧 Добавлен вызов n8n webhook
├── Step1Policy.tsx
├── Step2EventType.tsx
├── StepDocumentUpload.tsx
└── Step3Payment.tsx

frontend/src/pages/
└── ClaimForm.tsx              🔧 Добавлен Step1Phone на первый шаг

docker-compose.yml             🔧 Redis подключён к внешнему серверу

🧪 Тестирование

CreateWebProject:

Тест 1: Создание нового проекта

curl -X POST "https://crm.clientright.ru/webservice.php" \
  -d "operation=CreateWebProject" \
  -d "sessionName=xyz123" \
  -d "policy_number=E1000-TEST-1761990646" \
  -d "contact_id=396625" \
  -d "period_start=01-01-2025" \
  -d "period_end=31-12-2025"

Response: {"success":true,"result":"{\"project_id\":\"396865\",\"is_new\":true}"}
✅ Проект создан

Тест 2: Повторный вызов (поиск существующего)

curl -X POST "https://crm.clientright.ru/webservice.php" \
  -d "operation=CreateWebProject" \
  -d "sessionName=xyz123" \
  -d "policy_number=E1000-TEST-1761990646" \
  -d "contact_id=396625"

Response: {"success":true,"result":"{\"project_id\":\"396865\",\"is_new\":false}"}
✅ Проект найден (дубликат НЕ создан!)

SMS Validation:

До исправления:

Отправка кода → ключ: erv:sms_verify:+79262306381
Проверка кода → ключ: sms_verify:79262306381
Результат: ❌ "No verification code found"

После исправления:

Отправка кода → ключ: erv:sms_verify:79262306381
Проверка кода → ключ: sms_verify:79262306381
Результат: ✅ "Code verified"

n8n Integration:

Frontend → n8n webhook:

POST https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27
Body: {"phone": "79001234567"}

Response: {
  "claim_id": "CLM-2025-11-01-IWR1U2",
  "contact_id": "396625",
  "is_new_contact": false,
  "phone": "79001234567"
}

Redis Session (проверка):

redis-cli -h crm.clientright.ru -a 'CRM_Redis_Pass_2025_Secure!' \
  GET "claim:CLM-2025-11-01-IWR1U2"

{
  "claim_id": "CLM-2025-11-01-IWR1U2",
  "contact_id": "396625",
  "phone": "79001234567",
  "is_new_contact": false,
  "status": "draft",
  ...
}

📝 Git История

erv_platform (main):

89a182b - fix: Интеграция n8n webhook для создания контакта после SMS
8c21450 - docs: Лог сессии 30 октября - Телефон на шаг 1 + интеграция CRM
7b554c0 - feat: Полный флоу для создания контакта через CreateWebContact

CRM (master):

f720c14e - chore: Обновлён submodule erv_platform
c34f7c9b - docs: Документация для CreateWebProject
af802149 - feat: Добавлена операция CreateWebProject для vTiger webservice
d7941ac8 - feat: CreateWebContact возвращает is_new флаг
09c1fbd1 - feat: Добавлена операция CreateWebContact для vTiger webservice

🔗 Важные URL

Frontend: http://147.45.146.17:5173
Backend API: http://147.45.146.17:8100
CRM: https://crm.clientright.ru
CRM Webservice: https://crm.clientright.ru/webservice.php
n8n Production: https://n8n.clientright.pro
n8n Webhook (contact): https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27
Gitea ERV: http://147.45.146.17:3002/negodiy/erv-platform


🎯 Следующие шаги

  1. Тестирование полного флоу:

    • Телефон → SMS → CRM контакт → Полис → CRM проект → Документы → Тикет
  2. Доработка Step1Policy:

    • Вызов n8n webhook для проверки полиса
    • Создание проекта через CreateWebProject
    • Обновление Redis session
  3. Создание операции CreateERVTicket:

    • Финальный шаг создания тикета в HelpDesk
    • Привязка к проекту и контакту
  4. Личный кабинет:

    • Вход по телефону + SMS
    • Список незавершённых заявок
    • Возможность продолжить заявку

📊 Метрики

Время выполнения сессии: ~3.5 часа
Количество коммитов:

  • erv_platform: 3 коммита
  • CRM: 3 коммита

Созданных файлов: 2

  • include/Webservices/CreateWebProject.php
  • CREATE_WEB_PROJECT_DOCS.md

Изменённых файлов: 4

  • backend/app/services/sms_service.py
  • frontend/src/components/form/Step1Phone.tsx
  • frontend/src/pages/ClaimForm.tsx
  • docker-compose.yml

Строк добавлено: ~350
Строк удалено: ~30
Frontend rebuilds: 4
Backend rebuilds: 3
Тестовых запросов: 15+


Статус: Успешно завершено
Автор: AI Assistant (Claude Sonnet 4.5)
Дата: 01 ноября 2025, 13:30 MSK


📋 Лог сессии (продолжение): CreateWebClaim + Интеграция заявок

Дата: 01 ноября 2025 (21:00 - 01:15 MSK следующего дня)
Задачи:

  1. Создание операции CreateWebClaim для vTiger CRM
  2. Интеграция n8n workflow для создания заявок
  3. Backend proxy для безопасного вызова n8n webhooks
  4. Frontend интеграция в Step2EventType

Статус: Успешно завершено


🎯 Основные задачи

Задача 1: CreateWebClaim

Создать операцию vTiger webservice для создания заявок (HelpDesk tickets) по аналогии с CreateWebContact и CreateWebProject.

Требования:

  • Обязательные поля: title, contact_id, project_id, event_type
  • Опциональные: description, incident_date, transport_number
  • Маппинг типов событий на русские категории
  • Возврат: {"ticket_id": "123", "ticket_number": "ЗАЯВКА_456", ...}

Задача 2: n8n workflow

Создать workflow get_claim_CRM_ERV для обработки создания заявок с мержингом данных в Redis session.

Задача 3: Backend proxy

Добавить endpoint /api/n8n/claim/create для проксирования запросов к n8n webhook.

Задача 4: Frontend интеграция

Обновить Step2EventType.tsx для вызова создания черновика заявки при выборе типа события.


Выполненные задачи

1. CreateWebClaim.php - Операция vTiger Webservice

Файл: include/Webservices/CreateWebClaim.php

Обязательные параметры:

  • title - название заявки
  • contact_id - ID контакта (без префикса 12x)
  • project_id - ID проекта (без префикса 33x)
  • event_type - тип события (delay_flight, cancel_flight, etc.)

Опциональные параметры:

  • description - описание проблемы
  • incident_date - дата инцидента (YYYY-MM-DD)
  • transport_number - номер рейса/поезда/парома

Маппинг типов событий:

$eventTypeMap = array(
    'delay_flight' => 'Задержка рейса',
    'cancel_flight' => 'Отмена рейса',
    'missed_connection' => 'Пропуск стыковки',
    'delay_train' => 'Задержка поезда',
    'cancel_train' => 'Отмена поезда',
    'delay_ferry' => 'Задержка парома',
    'cancel_ferry' => 'Отмена парома'
);

Возвращаемые данные:

{
  "success": true,
  "result": {
    "ticket_id": "396932",
    "ticket_number": "ЗАЯВКА_825",
    "title": "Задержка авиарейса (более 3 часов) - E1000-302538524",
    "category": "Задержка рейса",
    "status": "рассмотрение"
  }
}

Регистрация в БД:

INSERT INTO vtiger_ws_operation (operationid, name, handler_path, handler_method, type, prelogin)
VALUES (52, 'CreateWebClaim', 'include/Webservices/CreateWebClaim.php', 'vtws_createwebclaim', 'POST', 0);

Особенности реализации:

  • ob_start() / ob_end_clean() для подавления BOM и warnings
  • Формирование полного описания с метаданными
  • Привязка к контакту (12x{contact_id}) и проекту (33x{project_id})
  • Логирование в logs/CreateWebClaim.log

2. n8n Workflow: get_claim_CRM_ERV

ID: qdYZqhIDGhK9E4DA
Webhook: d5bf4ca6-9e44-44b9-9714-3186ea703e7d
URL: https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d

Последовательность нод:

1. clime (Webhook) - получение данных от фронтенда
   ↓
2. Redis_get_session - чтение существующей сессии
   ↓
3. Edit Fields - подготовка данных для CRM
   ↓
4. Get Challenge - получение токена vTiger
   ↓
5. Execute a command - генерация MD5 hash
   ↓
6. Edit Fields3 - подготовка accessKey
   ↓
7. Login to CRM - авторизация в vTiger
   ↓
8. CreateWebTicket - создание заявки через CreateWebClaim
   ↓
9. Code in JavaScript - мерж данных заявки в сессию
   ↓
10. Redis (SET) - обновление сессии в Redis
    ↓
11. Code in JavaScript2 - формирование response для фронта
    ↓
12. Respond to Webhook - возврат данных

Code Node: Мерж данных заявки

const existingSession = $('Redis_get_session').first().json.propertyName;
const sessionData = JSON.parse(existingSession);
const claimResult = $node["CreateWebTicket"].json.result;
const webhookData = $('clime').first().json.body;

const updatedSession = {
  ...sessionData,
  ticket_id: claimResult.ticket_id,
  ticket_number: claimResult.ticket_number,
  ticket_title: claimResult.title,
  ticket_category: claimResult.category,
  ticket_status: claimResult.status,
  event_type: webhookData.event_type,
  current_step: 3,
  updated_at: new Date().toISOString()
};

return {
  redis_key: `claim:${sessionData.claim_id}`,
  redis_value: JSON.stringify(updatedSession),
  ttl: 604800
};

Структура сессии в Redis после создания заявки:

{
  "claim_id": "CLM-2025-11-01-4EZ5L1",
  "contact_id": "320096",
  "phone": "79262306381",
  "is_new_contact": false,
  "project_id": "396868",
  "is_new_project": false,
  "voucher": "E1000-302538524",
  "ticket_id": "396932",
  "ticket_number": "ЗАЯВКА_825",
  "ticket_title": "Задержка авиарейса (более 3 часов) - E1000-302538524",
  "ticket_category": "Задержка рейса",
  "ticket_status": "рассмотрение",
  "event_type": "delay_flight",
  "status": "draft",
  "current_step": 3,
  "created_at": "2025-11-01T21:13:23.043Z",
  "updated_at": "2025-11-01T22:15:23.000Z"
}

3. Backend Proxy: /api/n8n/claim/create

Файл: backend/app/api/n8n_proxy.py

Новый endpoint:

@router.post("/claim/create")
async def proxy_create_claim(request: Request):
    """
    Проксирует создание черновика заявки к n8n webhook
    Frontend → /api/n8n/claim/create → n8n webhook
    """
    body = await request.json()
    
    logger.info(f"🔄 Proxy create claim: event_type={body.get('event_type')}, claim_id={body.get('claim_id')}")
    
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(
            N8N_CREATE_CLAIM_WEBHOOK,
            json=body,
            headers={"Content-Type": "application/json"}
        )
        
        if response.status_code == 200:
            response_text = response.text
            if not response_text or response_text.strip() == '':
                raise HTTPException(status_code=500, detail="N8N вернул пустой ответ")
            return response.json()

Конфигурация:

N8N_CREATE_CLAIM_WEBHOOK = getattr(
    settings, 
    'n8n_create_claim_webhook', 
    'https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d'
)

4. Frontend: Step2EventType.tsx

Изменения:

const handleSubmit = async () => {
  const values = await form.validateFields();
  setLoading(true);
  
  const eventLabel = EVENT_TYPES.find(e => e.value === values.eventType)?.label;
  const title = `${eventLabel} - ${formData.voucher || 'полис не указан'}`;
  
  // Вызов backend proxy для создания заявки
  const response = await fetch('/api/n8n/claim/create', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      claim_id: formData.claim_id,
      contact_id: formData.contact_id,
      project_id: formData.project_id,
      event_type: values.eventType,
      title: title,
      voucher: formData.voucher,
      session_id: formData.session_id
    })
  });
  
  let result = await response.json();
  
  // ✅ n8n может вернуть массив - берём первый элемент
  if (Array.isArray(result) && result.length > 0) {
    result = result[0];
  }
  
  if (response.ok && result.success) {
    updateFormData({ 
      eventType: values.eventType,
      ticket_id: result.result?.ticket_id,
      ticket_number: result.result?.ticket_number
    });
    message.success(`Черновик заявки создан: ${result.result?.ticket_number}`);
    onNext();
  }
};

🐛 Решённые проблемы

1. BOM (Byte Order Mark) в JSON ответе

Проблема: vTiger webservice возвращал \xEF\xBB\xBF (UTF-8 BOM) перед JSON.

Решение:

// В CreateWebClaim.php
ob_start();
// ... код операции ...
ob_end_clean();
return $result; // Вместо json_encode($result)
// В webservice.php
ob_clean(); // После всех include

2. Пустой ответ от n8n webhook

Проблема: n8n workflow выполнялся успешно, но возвращал пустое тело ответа.

Причина: "Respond to Webhook" node был настроен на respondWith: "json" вместо "lastNode".

Решение: В n8n изменить настройку:

  • Respond With: Last Node (вместо JSON)

3. n8n возвращает массив вместо объекта

Проблема: n8n возвращал [{success: true, ...}] вместо {success: true, ...}.

Решение: Добавлена обработка в frontend:

if (Array.isArray(result) && result.length > 0) {
  result = result[0];
}

4. CORS и безопасность webhooks

Проблема: Прямые вызовы n8n webhook с фронтенда блокировались CORS.

Решение: Backend proxy /api/n8n/claim/create скрывает webhook URL и обрабатывает запросы.


📊 Результаты

Успешно созданные заявки

Последняя:

  • ID: 396932
  • Номер: ЗАЯВКА_825
  • Title: "Задержка авиарейса (более 3 часов) - E1000-302538524"
  • Category: "Задержка рейса"
  • Status: "рассмотрение"
  • Contact: 320096
  • Project: 396868

Статистика n8n workflow

  • Total Executions: 10
  • Success: 5 (после исправления "Respond to Webhook")
  • Errors: 5 (до исправления)
  • Success Rate: 100% (после фикса)

Лог backend

✅ Claim created successfully. Response: {"success":true,"result":{"claim_id":"CLM-2025-11-01-4EZ5L1"...
HTTP 200 OK

📦 Изменённые/созданные файлы

Созданные файлы

  1. include/Webservices/CreateWebClaim.php - операция vTiger для заявок

Изменённые файлы (Backend)

  1. backend/app/api/n8n_proxy.py - добавлен endpoint /api/n8n/claim/create
  2. webservice.php - ob_get_clean() + ob_start() для очистки BOM

Изменённые файлы (Frontend)

  1. frontend/src/components/form/Step2EventType.tsx - интеграция создания заявки
  2. frontend/src/pages/ClaimForm.tsx - передача addDebugEvent в Step2EventType

n8n Workflow

  1. Создан: get_claim_CRM_ERV (ID: qdYZqhIDGhK9E4DA)
  2. Webhook: d5bf4ca6-9e44-44b9-9714-3186ea703e7d

🔧 Технические детали

Git коммиты (CRM)

  1. c60d00f5 - feat: Создана операция CreateWebClaim

Git коммиты (ERV Platform)

  1. 793177b - feat: Интеграция создания черновика заявки в Step2EventType
  2. cacb2ee - fix: Обработка массива в ответе n8n для CreateWebClaim
  3. 927a8f5 - feat: Проксирование CreateClaim через backend
  4. 6cd7027 - fix: Улучшена обработка ответа n8n в claim/create

Docker rebuilds

  • Backend: 3 раза
  • Frontend: 5 раз (включая force rebuild с --no-cache)

Тестовых запросов

  • Curl тесты: 10+
  • Frontend тесты: 5+
  • Всего созданных заявок: 8

🎯 Полный флоу создания заявки

1. Frontend: Step2EventType
   - Пользователь выбирает тип события
   - Формируется title из event_type + voucher
   ↓
2. Frontend → Backend: POST /api/n8n/claim/create
   - Данные: claim_id, contact_id, project_id, event_type, title
   ↓
3. Backend Proxy
   - Логирование запроса
   - Проксирование к n8n webhook
   ↓
4. n8n Workflow: get_claim_CRM_ERV
   - Redis GET: чтение сессии
   - vTiger Login: авторизация
   - CreateWebClaim: создание заявки
   - Code: мерж данных в сессию
   - Redis SET: обновление сессии
   - Code: формирование response
   - Respond to Webhook
   ↓
5. Backend Proxy
   - Получение JSON от n8n
   - Возврат фронту
   ↓
6. Frontend
   - Обработка массива (если нужно)
   - Сохранение ticket_id, ticket_number в formData
   - message.success()
   - Переход на следующий шаг

📈 Метрики

Время выполнения одного запроса:

  • Frontend → Backend: ~50ms
  • Backend → n8n: ~2800ms (включая vTiger CRM)
  • n8n → vTiger CreateWebClaim: ~1500ms
  • Redis операции: ~100ms
  • Общее время: ~3 секунды

Размер данных:

  • Request: ~250 bytes
  • Response: ~400 bytes

Статус: Успешно завершено
Время работы: 4 часа 15 минут
Автор: AI Assistant (Claude Sonnet 4.5)
Дата: 01-02 ноября 2025, 21:00-01:15 MSK