Files
crm.clientright.ru/include/Webservices/CreateWebClaimV2.php
Fedor 01c4fe80b5 chore: snapshot current working tree changes
Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
2026-03-26 14:19:01 +03:00

329 lines
14 KiB
PHP
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.

<?php
/*********************************************************************************
* API-интерфейс для создания Заявки (HelpDesk) из Web-формы (V2 - JSON версия)
* Принимает JSON строку с данными заявки
* Автор: Фёдор, 2025-12-29
********************************************************************************/
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 'includes/Loader.php';
vimport ('includes.runtime.Globals');
vimport ('includes.runtime.BaseModel');
vimport ('includes.runtime.LanguageHandler');
/**
* Создание заявки из web-формы ERV Platform (V2 - JSON версия)
*
* @param string $claim_json - JSON строка с данными заявки (обязательно)
* @param object $user - пользователь (опционально)
* @return array - {"ticket_id": "123", "ticket_number": "TT12345", "title": "...", "category": "...", "status": "..."}
*/
function vtws_createwebclaimv2($claim_json, $user = false) {
$logstring = date("Y-m-d H:i:s").' REQUEST: '.json_encode($_REQUEST);
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
// Проверка обязательного параметра
if(empty($claim_json)){
$logstring = date("Y-m-d H:i:s").' Не передан параметр claim_json';
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не передан параметр claim_json");
}
// Парсим JSON
$claimData = json_decode($claim_json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// Пробуем очистить от возможных лишних символов
$cleanedJson = trim($claim_json);
$cleanedJson = preg_replace('/^[^{]*/', '', $cleanedJson); // Убираем всё до первой {
$cleanedJson = preg_replace('/[^}]*$/', '', $cleanedJson); // Убираем всё после последней }
$claimData = json_decode($cleanedJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$logstring = date("Y-m-d H:i:s").' Ошибка парсинга JSON: '.json_last_error_msg().', JSON: '.substr($claim_json, 0, 200);
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Ошибка парсинга JSON: ".json_last_error_msg());
}
}
$logstring = date("Y-m-d H:i:s").' CLEANED JSON: '.json_encode($claimData);
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
// Извлекаем обязательные поля
$project_id = isset($claimData['project_id']) ? $claimData['project_id'] : '';
$contact_id = isset($claimData['contact_id']) ? $claimData['contact_id'] : '';
$event_type = isset($claimData['cf_1726']) ? $claimData['cf_1726'] : '';
$description = isset($claimData['description']) ? $claimData['description'] : '';
// Проверка обязательных полей
if(empty($project_id)){
$logstring = date("Y-m-d H:i:s").' Не указано обязательное поле: project_id';
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не указан ID проекта");
}
if(empty($contact_id)){
$logstring = date("Y-m-d H:i:s").' Не указано обязательное поле: contact_id';
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не указан ID контакта");
}
if(empty($event_type)){
$logstring = date("Y-m-d H:i:s").' Не указано обязательное поле: cf_1726 (event_type)';
file_put_contents('logs/CreateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не указан тип страхового случая");
}
global $adb, $current_user;
// Нормализуем ID контакта и проекта
$contactIdNumeric = preg_replace('/[^0-9]/', '', $contact_id);
$projectIdNumeric = preg_replace('/[^0-9]/', '', $project_id);
$contactWsId = '12x' . $contactIdNumeric;
$projectWsId = '33x' . $projectIdNumeric;
$logstring = date('Y-m-d H:i:s').' Нормализовали ID: contact='.$contactIdNumeric.' (raw='.$contact_id.'), project='.$projectIdNumeric.' (raw='.$project_id.')'.PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
// Маппинг типов событий на русские названия для cf_2650
$eventTypeMap = array(
'delay_flight' => 'Задержка рейса',
'cancel_flight' => 'Отмена рейса',
'miss_connection' => 'Пропуск стыковки',
'missed_connection' => 'Пропуск стыковки',
'delay_train' => 'Задержка поезда',
'cancel_train' => 'Отмена поезда',
'delay_ferry' => 'Задержка парома',
'cancel_ferry' => 'Отмена парома'
);
// ticketcategories всегда "Цифровой адвокат ЕРВ"
$ticketCategory = 'Цифровой адвокат ЕРВ';
// Нормализуем event_type для cf_2650
$normalizedEventType = isset($eventTypeMap[$event_type]) ? $eventTypeMap[$event_type] : 'Цифровой адвокат ЕРВ';
// Извлекаем дополнительные поля
$incident_date = isset($claimData['cf_2566']) ? $claimData['cf_2566'] : '';
$transport_number = isset($claimData['cf_2568']) ? $claimData['cf_2568'] : '';
$cf_1885 = isset($claimData['cf_1885']) ? $claimData['cf_1885'] : '';
$lastname = isset($claimData['lastname']) ? $claimData['lastname'] : '';
$firstname = isset($claimData['firstname']) ? $claimData['firstname'] : '';
// Формируем ticket_title: event_type_cf_1885_lastname_firstname
$ticket_title = $event_type;
if (!empty($cf_1885)) {
$ticket_title .= '_' . $cf_1885;
}
if (!empty($lastname)) {
$ticket_title .= '_' . $lastname;
}
if (!empty($firstname)) {
$ticket_title .= '_' . $firstname;
}
// Формируем описание
$fullDescription = '';
if (!empty($description)) {
$fullDescription .= $description . "\n\n";
}
$fullDescription .= "Тип события: " . $normalizedEventType . "\n";
if (!empty($incident_date)) {
$fullDescription .= "Дата инцидента: " . $incident_date . "\n";
}
if (!empty($transport_number)) {
$fullDescription .= "Номер рейса: " . $transport_number . "\n";
}
// Добавляем cf_departure_flight и cf_departure_date, если есть
$cf_departure_flight = isset($claimData['cf_departure_flight']) ? $claimData['cf_departure_flight'] : '';
$cf_departure_date = isset($claimData['cf_departure_date']) ? $claimData['cf_departure_date'] : '';
if (!empty($cf_departure_flight)) {
$fullDescription .= "Рейс стыковки: " . $cf_departure_flight . "\n";
}
if (!empty($cf_departure_date)) {
$fullDescription .= "Дата стыковки: " . $cf_departure_date . "\n";
}
// Формируем массив параметров для создания заявки
$params = array(
'ticket_title' => $ticket_title,
'parent_id' => '11x67458', // Заявитель - контрагент
'ticketcategories' => $ticketCategory,
'ticketstatus' => 'рассмотрение',
'contact_id' => $contactWsId,
'cf_2066' => $projectWsId, // Связь с проектом
'ticketpriorities' => 'High',
'assigned_user_id' => vtws_getWebserviceEntityId('Users', $current_user->id),
'description' => $fullDescription,
'cf_1726' => $event_type, // Сырой тип события
'cf_2650' => $normalizedEventType // Нормализованный тип события
);
// Маппинг дополнительных полей
if (!empty($incident_date)) {
$params['cf_2566'] = $incident_date;
}
if (!empty($transport_number)) {
$params['cf_2568'] = $transport_number;
}
if (!empty($cf_departure_flight)) {
$params['cf_2630'] = $cf_departure_flight;
}
if (!empty($cf_departure_date)) {
$params['cf_2632'] = $cf_departure_date;
}
// Страна (cf_1909 → cf_2636)
if (isset($claimData['cf_1909']) && !empty($claimData['cf_1909'])) {
$params['cf_2636'] = $claimData['cf_1909'];
}
// cf_2502 → cf_2572
if (isset($claimData['cf_2502']) && !empty($claimData['cf_2502'])) {
$params['cf_2572'] = $claimData['cf_2502'];
}
// code → cf_2574
if (isset($claimData['code']) && !empty($claimData['code'])) {
$params['cf_2574'] = $claimData['code'];
}
// cf_1885 → cf_2642
if (!empty($cf_1885)) {
$params['cf_2642'] = $cf_1885;
}
// IP → cf_2634
if (isset($claimData['ip']) && !empty($claimData['ip'])) {
$params['cf_2634'] = $claimData['ip'];
}
// region → cf_2640
if (isset($claimData['region']) && !empty($claimData['region'])) {
$params['cf_2640'] = $claimData['region'];
}
// source → cf_2638
if (isset($claimData['source']) && !empty($claimData['source'])) {
$params['cf_2638'] = $claimData['source'];
}
// cf_2508 → cf_2508 (прямое маппирование)
if (isset($claimData['cf_2508']) && !empty($claimData['cf_2508'])) {
$params['cf_2508'] = $claimData['cf_2508'];
}
// cf_2648 → cf_2648 (прямое маппирование)
if (isset($claimData['cf_2648']) && !empty($claimData['cf_2648'])) {
$params['cf_2648'] = $claimData['cf_2648'];
}
// cf_2658, cf_2664 (прямое маппирование: ФИО, дата рождения и др.)
if (isset($claimData['cf_2658']) && $claimData['cf_2658'] !== '') {
$params['cf_2658'] = $claimData['cf_2658'];
}
if (isset($claimData['cf_2664']) && $claimData['cf_2664'] !== '') {
$params['cf_2664'] = $claimData['cf_2664'];
}
// currency_code → cf_2652
if (isset($claimData['currency_code']) && !empty($claimData['currency_code'])) {
$params['cf_2652'] = $claimData['currency_code'];
}
// course → cf_2654 (сохраняем с 4 знаками после запятой)
// Важно: сохраняем как строку, чтобы vTiger не округлял через CurrencyField::convertToUserFormat
if (isset($claimData['course']) && !empty($claimData['course'])) {
// Преобразуем в число и форматируем с 4 знаками после запятой
$courseValue = floatval($claimData['course']);
// Форматируем с 4 знаками и сохраняем как строку, чтобы сохранить точность
$params['cf_2654'] = sprintf('%.4f', $courseValue);
}
$logstring = date('Y-m-d H:i:s').' Массив для создания Заявки: '.json_encode($params).PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
try {
$result = vtws_create('HelpDesk', $params, $current_user);
$ticketId = substr($result['id'], 3); // Убираем префикс "17x"
$ticketNumber = isset($result['ticket_no']) ? $result['ticket_no'] : 'N/A';
$logstring = date('Y-m-d H:i:s').' ✅ Создана Заявка id='.$ticketId.' ticket_no='.$ticketNumber.PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
// Если курс был передан, сохраняем его напрямую в БД с 4 знаками после запятой
// Это нужно, чтобы обойти форматирование vTiger через CurrencyField::convertToUserFormat
if (isset($claimData['course']) && !empty($claimData['course'])) {
$courseValue = floatval($claimData['course']);
$courseFormatted = sprintf('%.4f', $courseValue);
// Сохраняем напрямую в БД, чтобы сохранить 4 знака после запятой
try {
$adb->pquery(
"UPDATE vtiger_ticketcf SET cf_2654 = ? WHERE ticketid = ?",
array($courseFormatted, $ticketId)
);
$logstring = date('Y-m-d H:i:s').' 💰 Курс сохранен напрямую в БД: cf_2654='.$courseFormatted.' для заявки '.$ticketId.PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
} catch (Exception $courseEx) {
$logstring = date('Y-m-d H:i:s').' ⚠️ Ошибка сохранения курса: '.$courseEx->getMessage().PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
}
}
// Создаём двустороннюю связь между Проектом и Заявкой
try {
$relationCheck = $adb->pquery(
"SELECT 1 FROM vtiger_crmentityrel
WHERE (crmid = ? AND relcrmid = ?)
OR (crmid = ? AND relcrmid = ?)
LIMIT 1",
array($projectIdNumeric, $ticketId, $ticketId, $projectIdNumeric)
);
if (!$relationCheck || $adb->num_rows($relationCheck) === 0) {
$adb->pquery(
"INSERT INTO vtiger_crmentityrel (crmid, module, relcrmid, relmodule) VALUES (?, ?, ?, ?)",
array($projectIdNumeric, 'Project', $ticketId, 'HelpDesk')
);
$logstring = date('Y-m-d H:i:s').' 🔗 Добавлена связь Project('.$projectIdNumeric.') ⇄ HelpDesk('.$ticketId.')'.PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
} else {
$logstring = date('Y-m-d H:i:s').' 🔗 Связь Project('.$projectIdNumeric.') ⇄ HelpDesk('.$ticketId.') уже существует'.PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
}
} catch (Exception $relEx) {
$logstring = date('Y-m-d H:i:s').' ⚠️ Ошибка связывания Project('.$projectIdNumeric.') ⇄ HelpDesk('.$ticketId.'): '.$relEx->getMessage().PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
}
// Возвращаем массив
$output = array(
'ticket_id' => $ticketId,
'ticket_number' => $ticketNumber,
'title' => $ticket_title,
'category' => $ticketCategory,
'status' => 'рассмотрение'
);
} catch (WebServiceException $ex) {
$logstring = date('Y-m-d H:i:s').' ❌ Ошибка создания: '.$ex->getMessage().PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
throw $ex;
}
$logstring = date('Y-m-d H:i:s').' Return: '.json_encode($output).PHP_EOL;
file_put_contents('logs/CreateWebClaimV2.log', $logstring, FILE_APPEND);
return $output;
}