Frontend: - Changed main title to 'Подать обращение о защите прав потребителя' - Changed browser title to 'Clientright — защита прав потребителей' - Enhanced draft cards: show problem_description (250 chars), category tag, document progress bar - Fixed 'Назад' button to always return to draft selection - Added SSE connection for OCR status updates - Renamed steps: Вход, Обращение, Документы, Заявление - Skip 'Проверка полиса' and 'Тип события' steps for new claim flow Backend: - Fixed client IP extraction (X-Forwarded-For, X-Real-IP) - Added problem_title, category, documents_required_list to draft list API - Fixed documents_uploaded count to count unique field_labels CRM Webservices: - Added UpsertContact.php - create/update contacts with tgid support - Added UpsertAccounts.php - batch upsert offenders by INN - Added UpsertProject.php - create/update projects with offender mapping Database: - Fixed documents_meta duplicates in existing claims - SQL query for deduplication by field_name provided
235 lines
10 KiB
PHP
235 lines
10 KiB
PHP
<?php
|
||
/*********************************************************************************
|
||
* API-интерфейс для создания/обновления Контакта (Upsert)
|
||
* Гибкий метод: обновляет если найден, создаёт если нет
|
||
*
|
||
* Приоритет поиска:
|
||
* 1. contact_id (если передан - сразу обновляем)
|
||
* 2. mobile (ищем по мобильному)
|
||
* 3. tgid (ищем по полю phone, где хранится telegram_id)
|
||
*
|
||
* Все поля опциональны, кроме хотя бы одного идентификатора
|
||
*
|
||
* Автор: Фёдор, 2025-12-01
|
||
********************************************************************************/
|
||
|
||
include_once 'include/Webservices/Query.php';
|
||
include_once 'modules/Users/Users.php';
|
||
require_once('include/Webservices/Utils.php');
|
||
require_once 'include/Webservices/Create.php';
|
||
require_once 'include/Webservices/Revise.php';
|
||
require_once 'includes/Loader.php';
|
||
vimport('includes.runtime.Globals');
|
||
vimport('includes.runtime.BaseModel');
|
||
vimport('includes.runtime.LanguageHandler');
|
||
|
||
/**
|
||
* Upsert контакта - создание или обновление
|
||
*
|
||
* @param string $contact_id - ID контакта в CRM (если известен)
|
||
* @param string $mobile - мобильный телефон
|
||
* @param string $tgid - telegram ID
|
||
* @param string $firstname - имя
|
||
* @param string $secondname - отчество
|
||
* @param string $lastname - фамилия
|
||
* @param string $email - email
|
||
* @param string $birthday - дата рождения
|
||
* @param string $birthplace - место рождения
|
||
* @param string $mailingstreet - адрес
|
||
* @param string $inn - ИНН
|
||
* @param string $requisites - реквизиты
|
||
* @param string $code - SMS код верификации
|
||
* @param mixed $user - пользователь CRM
|
||
* @return string JSON с результатом
|
||
*/
|
||
function vtws_upsertcontact(
|
||
$contact_id = '',
|
||
$mobile = '',
|
||
$tgid = '',
|
||
$firstname = '',
|
||
$secondname = '',
|
||
$lastname = '',
|
||
$email = '',
|
||
$birthday = '',
|
||
$birthplace = '',
|
||
$mailingstreet = '',
|
||
$inn = '',
|
||
$requisites = '',
|
||
$code = '',
|
||
$user = false
|
||
) {
|
||
$logFile = 'logs/UpsertContact.log';
|
||
$logstring = date("Y-m-d H:i:s") . ' REQUEST: ' . json_encode($_REQUEST);
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
global $adb, $current_user;
|
||
|
||
// Результат
|
||
$result = array(
|
||
'success' => false,
|
||
'contact_id' => null,
|
||
'action' => null, // 'created', 'updated', 'found'
|
||
'message' => ''
|
||
);
|
||
|
||
// ========================================
|
||
// 1. ФОРМАТИРОВАНИЕ ТЕЛЕФОНА
|
||
// ========================================
|
||
if (!empty($mobile)) {
|
||
$mobile = preg_replace('/[^0-9]/', '', $mobile);
|
||
if (strlen($mobile) == 11 && $mobile[0] == '8') {
|
||
$mobile = "7" . substr($mobile, 1);
|
||
} else if (strlen($mobile) == 10) {
|
||
$mobile = "7" . $mobile;
|
||
} else if (strlen($mobile) != 11) {
|
||
// Некорректный номер - логируем, но не падаем
|
||
$logstring = date("Y-m-d H:i:s") . ' ⚠️ Некорректный номер телефона: ' . $mobile . ' (игнорируем)';
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
$mobile = ''; // Обнуляем некорректный номер
|
||
}
|
||
}
|
||
|
||
// ========================================
|
||
// 2. ПОИСК СУЩЕСТВУЮЩЕГО КОНТАКТА
|
||
// ========================================
|
||
$existingContactId = null;
|
||
$searchMethod = '';
|
||
|
||
// 2.1 По contact_id (приоритет 1)
|
||
if (!empty($contact_id)) {
|
||
$contact_id = preg_replace('/[^0-9]/', '', $contact_id); // Очищаем от 12x префикса
|
||
$query = "SELECT c.contactid FROM vtiger_contactdetails c
|
||
LEFT JOIN vtiger_crmentity e ON e.crmid = c.contactid
|
||
WHERE e.deleted = 0 AND c.contactid = ? LIMIT 1";
|
||
$res = $adb->pquery($query, array($contact_id));
|
||
if ($adb->num_rows($res) > 0) {
|
||
$existingContactId = $adb->query_result($res, 0, 'contactid');
|
||
$searchMethod = 'by_contact_id';
|
||
}
|
||
}
|
||
|
||
// 2.2 По mobile (приоритет 2)
|
||
if (empty($existingContactId) && !empty($mobile)) {
|
||
$query = "SELECT c.contactid FROM vtiger_contactdetails c
|
||
LEFT JOIN vtiger_crmentity e ON e.crmid = c.contactid
|
||
WHERE e.deleted = 0 AND c.mobile = ? LIMIT 1";
|
||
$res = $adb->pquery($query, array($mobile));
|
||
if ($adb->num_rows($res) > 0) {
|
||
$existingContactId = $adb->query_result($res, 0, 'contactid');
|
||
$searchMethod = 'by_mobile';
|
||
}
|
||
}
|
||
|
||
// 2.3 По tgid (приоритет 3) - tgid хранится в поле phone
|
||
if (empty($existingContactId) && !empty($tgid)) {
|
||
$query = "SELECT c.contactid FROM vtiger_contactdetails c
|
||
LEFT JOIN vtiger_crmentity e ON e.crmid = c.contactid
|
||
WHERE e.deleted = 0 AND c.phone = ? LIMIT 1";
|
||
$res = $adb->pquery($query, array($tgid));
|
||
if ($adb->num_rows($res) > 0) {
|
||
$existingContactId = $adb->query_result($res, 0, 'contactid');
|
||
$searchMethod = 'by_tgid';
|
||
}
|
||
}
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' Поиск: contact_id=' . $contact_id . ', mobile=' . $mobile . ', tgid=' . $tgid;
|
||
$logstring .= ' → Найден: ' . ($existingContactId ? $existingContactId . ' (' . $searchMethod . ')' : 'НЕТ');
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
// ========================================
|
||
// 3. ФОРМИРУЕМ ПАРАМЕТРЫ
|
||
// ========================================
|
||
$params = array();
|
||
|
||
// Только непустые поля добавляем в params
|
||
if (!empty($firstname)) $params['firstname'] = $firstname;
|
||
if (!empty($secondname)) $params['cf_1157'] = $secondname; // Отчество
|
||
if (!empty($lastname)) $params['lastname'] = $lastname;
|
||
if (!empty($mobile)) $params['mobile'] = $mobile;
|
||
if (!empty($email)) $params['email'] = $email;
|
||
if (!empty($tgid)) $params['phone'] = $tgid; // TG ID в поле phone
|
||
if (!empty($birthday)) $params['birthday'] = $birthday;
|
||
if (!empty($birthplace)) $params['cf_1263'] = $birthplace; // Место рождения
|
||
if (!empty($mailingstreet)) $params['mailingstreet'] = $mailingstreet;
|
||
if (!empty($inn)) $params['cf_1257'] = $inn; // ИНН
|
||
if (!empty($requisites)) $params['cf_1849'] = $requisites; // Реквизиты
|
||
if (!empty($code)) $params['cf_1580'] = $code; // SMS код
|
||
|
||
// ========================================
|
||
// 4. СОЗДАНИЕ ИЛИ ОБНОВЛЕНИЕ
|
||
// ========================================
|
||
try {
|
||
if (!empty($existingContactId)) {
|
||
// === ОБНОВЛЕНИЕ ===
|
||
$params['id'] = '12x' . $existingContactId;
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' 📝 Обновляем контакт ' . $existingContactId . ': ' . json_encode($params);
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
$contact = vtws_revise($params, $current_user);
|
||
|
||
$result['success'] = true;
|
||
$result['contact_id'] = $existingContactId;
|
||
$result['action'] = 'updated';
|
||
$result['search_method'] = $searchMethod;
|
||
$result['message'] = 'Контакт обновлён';
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' ✅ Контакт ' . $existingContactId . ' обновлён';
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
} else {
|
||
// === СОЗДАНИЕ ===
|
||
|
||
// Проверяем минимальные данные для создания
|
||
if (empty($mobile) && empty($tgid)) {
|
||
throw new WebServiceException(
|
||
WebServiceErrorCode::$INVALIDID,
|
||
"Для создания контакта нужен хотя бы mobile или tgid"
|
||
);
|
||
}
|
||
|
||
// Дефолтные значения для обязательных полей CRM
|
||
if (empty($params['firstname'])) {
|
||
$params['firstname'] = 'Клиент';
|
||
}
|
||
if (empty($params['lastname'])) {
|
||
$suffix = !empty($mobile) ? substr($mobile, -4) : substr($tgid, -4);
|
||
$params['lastname'] = 'Web_' . $suffix;
|
||
}
|
||
if (empty($params['birthday'])) {
|
||
$params['birthday'] = '01-01-1990';
|
||
}
|
||
|
||
// Назначаем ответственного
|
||
$params['assigned_user_id'] = vtws_getWebserviceEntityId('Users', $current_user->id);
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' 🆕 Создаём контакт: ' . json_encode($params);
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
$contact = vtws_create('Contacts', $params, $current_user);
|
||
$newContactId = substr($contact['id'], 3); // Убираем 12x
|
||
|
||
$result['success'] = true;
|
||
$result['contact_id'] = $newContactId;
|
||
$result['action'] = 'created';
|
||
$result['message'] = 'Контакт создан';
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' ✅ Создан контакт ' . $newContactId;
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
}
|
||
|
||
} catch (WebServiceException $ex) {
|
||
$result['success'] = false;
|
||
$result['message'] = $ex->getMessage();
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' ❌ Ошибка: ' . $ex->getMessage();
|
||
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
|
||
|
||
throw $ex;
|
||
}
|
||
|
||
$logstring = date('Y-m-d H:i:s') . ' RESULT: ' . json_encode($result, JSON_UNESCAPED_UNICODE) . PHP_EOL;
|
||
file_put_contents($logFile, $logstring, FILE_APPEND);
|
||
|
||
return json_encode($result, JSON_UNESCAPED_UNICODE);
|
||
} |