232 lines
11 KiB
PHP
232 lines
11 KiB
PHP
|
|
<?php
|
|||
|
|
/*********************************************************************************
|
|||
|
|
* API для обновления существующей Заявки (HelpDesk) из Web-формы (V2 - JSON)
|
|||
|
|
* Принимает ticket_id + claim_json с полями для обновления (те же ключи, что в CreateWebClaimV2)
|
|||
|
|
* Автор: Фёдор, 2026-01-23
|
|||
|
|
********************************************************************************/
|
|||
|
|
|
|||
|
|
include_once 'include/Webservices/Query.php';
|
|||
|
|
include_once 'modules/Users/Users.php';
|
|||
|
|
require_once('include/Webservices/Utils.php');
|
|||
|
|
require_once 'include/Webservices/Revise.php';
|
|||
|
|
require_once 'includes/Loader.php';
|
|||
|
|
vimport ('includes.runtime.Globals');
|
|||
|
|
vimport ('includes.runtime.BaseModel');
|
|||
|
|
vimport ('includes.runtime.LanguageHandler');
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Обновление заявки (тикета) по ID
|
|||
|
|
*
|
|||
|
|
* @param string $ticket_id - ID заявки (число или 17x123)
|
|||
|
|
* @param string $claim_json - JSON с полями для обновления (только переданные поля будут обновлены)
|
|||
|
|
* @param object $user - пользователь (опционально)
|
|||
|
|
* @return array - {"success": true, "ticket_id": "...", "ticket_number": "..."}
|
|||
|
|
*/
|
|||
|
|
function vtws_updatewebclaimv2($ticket_id, $claim_json, $user = false) {
|
|||
|
|
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' REQUEST: ticket_id='.$ticket_id.' '.json_encode($_REQUEST);
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
|
|||
|
|
if (empty($ticket_id)) {
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' Не передан параметр ticket_id';
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не передан параметр ticket_id");
|
|||
|
|
}
|
|||
|
|
if ($claim_json === '' || $claim_json === null) {
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' Не передан параметр claim_json';
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Не передан параметр claim_json");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Параметр может прийти строкой (JSON) или уже массивом (тип encoded в БД)
|
|||
|
|
if (is_array($claim_json)) {
|
|||
|
|
$claimData = $claim_json;
|
|||
|
|
} else {
|
|||
|
|
$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();
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Ошибка парсинга JSON: ".json_last_error_msg());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' claimData keys: '.implode(', ', array_keys($claimData)).PHP_EOL;
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring, FILE_APPEND);
|
|||
|
|
|
|||
|
|
global $adb, $current_user;
|
|||
|
|
|
|||
|
|
$ticketIdNumeric = preg_replace('/[^0-9]/', '', $ticket_id);
|
|||
|
|
$ticketWsId = '17x' . $ticketIdNumeric;
|
|||
|
|
|
|||
|
|
// Проверяем, что запись существует и это HelpDesk
|
|||
|
|
$check = $adb->pquery(
|
|||
|
|
"SELECT e.crmid, e.setype FROM vtiger_crmentity e WHERE e.deleted = 0 AND e.crmid = ?",
|
|||
|
|
array($ticketIdNumeric)
|
|||
|
|
);
|
|||
|
|
if (!$check || $adb->num_rows($check) === 0) {
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' Заявка с id='.$ticketIdNumeric.' не найдена';
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
throw new WebServiceException(WebServiceErrorCode::$RECORDNOTFOUND, "Заявка не найдена");
|
|||
|
|
}
|
|||
|
|
$setype = $adb->query_result($check, 0, 'setype');
|
|||
|
|
if ($setype !== 'HelpDesk') {
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' Запись '.$ticketIdNumeric.' не является заявкой (HelpDesk): '.$setype;
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, "Указанная запись не является заявкой");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$eventTypeMap = array(
|
|||
|
|
'delay_flight' => 'Задержка рейса',
|
|||
|
|
'cancel_flight' => 'Отмена рейса',
|
|||
|
|
'miss_connection' => 'Пропуск стыковки',
|
|||
|
|
'missed_connection' => 'Пропуск стыковки',
|
|||
|
|
'delay_train' => 'Задержка поезда',
|
|||
|
|
'cancel_train' => 'Отмена поезда',
|
|||
|
|
'delay_ferry' => 'Задержка парома',
|
|||
|
|
'cancel_ferry' => 'Отмена парома'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$element = array('id' => $ticketWsId);
|
|||
|
|
|
|||
|
|
// Маппинг полей из claim_json в поля CRM (только если ключ передан)
|
|||
|
|
if (array_key_exists('ticket_title', $claimData)) {
|
|||
|
|
$element['ticket_title'] = $claimData['ticket_title'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('description', $claimData)) {
|
|||
|
|
$element['description'] = $claimData['description'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('ticketstatus', $claimData)) {
|
|||
|
|
$element['ticketstatus'] = $claimData['ticketstatus'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('project_id', $claimData) && $claimData['project_id'] !== '') {
|
|||
|
|
$pid = preg_replace('/[^0-9]/', '', $claimData['project_id']);
|
|||
|
|
$element['cf_2066'] = '33x' . $pid;
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('contact_id', $claimData) && $claimData['contact_id'] !== '') {
|
|||
|
|
$cid = preg_replace('/[^0-9]/', '', $claimData['contact_id']);
|
|||
|
|
$element['contact_id'] = '12x' . $cid;
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_1726', $claimData)) {
|
|||
|
|
$element['cf_1726'] = $claimData['cf_1726'];
|
|||
|
|
$element['cf_2650'] = isset($eventTypeMap[$claimData['cf_1726']]) ? $eventTypeMap[$claimData['cf_1726']] : 'Цифровой адвокат ЕРВ';
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_2566', $claimData)) {
|
|||
|
|
$element['cf_2566'] = $claimData['cf_2566'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_2568', $claimData)) {
|
|||
|
|
$element['cf_2568'] = $claimData['cf_2568'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_departure_flight', $claimData)) {
|
|||
|
|
$element['cf_2630'] = $claimData['cf_departure_flight'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_departure_date', $claimData)) {
|
|||
|
|
$element['cf_2632'] = $claimData['cf_departure_date'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_1909', $claimData)) {
|
|||
|
|
$element['cf_2636'] = $claimData['cf_1909'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_2502', $claimData)) {
|
|||
|
|
$element['cf_2572'] = $claimData['cf_2502'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('code', $claimData)) {
|
|||
|
|
$element['cf_2574'] = $claimData['code'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_1885', $claimData)) {
|
|||
|
|
$element['cf_2642'] = $claimData['cf_1885'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('ip', $claimData)) {
|
|||
|
|
$element['cf_2634'] = $claimData['ip'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('region', $claimData)) {
|
|||
|
|
$element['cf_2640'] = $claimData['region'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('source', $claimData)) {
|
|||
|
|
$element['cf_2638'] = $claimData['source'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_2508', $claimData)) {
|
|||
|
|
$element['cf_2508'] = $claimData['cf_2508'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('cf_2648', $claimData)) {
|
|||
|
|
$element['cf_2648'] = $claimData['cf_2648'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('currency_code', $claimData)) {
|
|||
|
|
$element['cf_2652'] = $claimData['currency_code'];
|
|||
|
|
}
|
|||
|
|
if (array_key_exists('course', $claimData) && $claimData['course'] !== '' && $claimData['course'] !== null) {
|
|||
|
|
$element['cf_2654'] = sprintf('%.4f', floatval($claimData['course']));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Любое поле cf_XXXX из claim_json прокидываем в CRM как есть (в т.ч. cf_2662 и др.)
|
|||
|
|
foreach ($claimData as $k => $v) {
|
|||
|
|
if (preg_match('/^cf_\d+$/', $k) && !array_key_exists($k, $element)) {
|
|||
|
|
$element[$k] = $v;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если передан только id — нет полей для обновления
|
|||
|
|
if (count($element) === 1) {
|
|||
|
|
$ticketNoRes = $adb->pquery("SELECT ticket_no FROM vtiger_troubletickets WHERE ticketid = ?", array($ticketIdNumeric));
|
|||
|
|
$ticketNo = $ticketNoRes && $adb->num_rows($ticketNoRes) ? $adb->query_result($ticketNoRes, 0, 'ticket_no') : 'N/A';
|
|||
|
|
$logstring = date("Y-m-d H:i:s").' В claim_json нет известных полей для обновления. Получены ключи: '.implode(', ', array_keys($claimData));
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring.PHP_EOL, FILE_APPEND);
|
|||
|
|
return array(
|
|||
|
|
'success' => true,
|
|||
|
|
'ticket_id' => $ticketIdNumeric,
|
|||
|
|
'ticket_number' => $ticketNo,
|
|||
|
|
'updated' => false,
|
|||
|
|
'message' => 'В claim_json не найдено полей для обновления. Проверьте логи UpdateWebClaimV2.log — там перечень полученных ключей.'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$logstring = date('Y-m-d H:i:s').' Элемент для revise: '.json_encode($element).PHP_EOL;
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring, FILE_APPEND);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$entity = vtws_revise($element, $current_user);
|
|||
|
|
} catch (WebServiceException $e) {
|
|||
|
|
$logstring = date('Y-m-d H:i:s').' Ошибка revise: '.$e->getMessage().PHP_EOL;
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring, FILE_APPEND);
|
|||
|
|
throw $e;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Курс (cf_2654) — сохраняем напрямую в БД для точности 4 знака (как в CreateWebClaimV2)
|
|||
|
|
if (array_key_exists('course', $claimData) && $claimData['course'] !== '' && $claimData['course'] !== null) {
|
|||
|
|
$courseFormatted = sprintf('%.4f', floatval($claimData['course']));
|
|||
|
|
try {
|
|||
|
|
$adb->pquery("UPDATE vtiger_ticketcf SET cf_2654 = ? WHERE ticketid = ?", array($courseFormatted, $ticketIdNumeric));
|
|||
|
|
} catch (Exception $ex) {
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', date('Y-m-d H:i:s').' Предупреждение: курс '.$courseFormatted.' — '.$ex->getMessage().PHP_EOL, FILE_APPEND);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$ticketNumber = isset($entity['ticket_no']) ? $entity['ticket_no'] : 'N/A';
|
|||
|
|
$logstring = date('Y-m-d H:i:s').' Заявка обновлена: id='.$ticketIdNumeric.' ticket_no='.$ticketNumber.PHP_EOL;
|
|||
|
|
file_put_contents('logs/UpdateWebClaimV2.log', $logstring, FILE_APPEND);
|
|||
|
|
|
|||
|
|
return array(
|
|||
|
|
'success' => true,
|
|||
|
|
'ticket_id' => $ticketIdNumeric,
|
|||
|
|
'ticket_number' => $ticketNumber,
|
|||
|
|
'updated' => true
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
Регистрация операции в БД (если ещё не добавлено):
|
|||
|
|
|
|||
|
|
INSERT INTO vtiger_ws_operation (name, handler_path, handler_method, type, prelogin)
|
|||
|
|
VALUES ('updatewebclaimv2', 'include/Webservices/UpdateWebClaimV2.php', 'vtws_updatewebclaimv2', 'POST', 0);
|
|||
|
|
|
|||
|
|
INSERT INTO vtiger_ws_operation_parameters (operationid, name, type, sequence)
|
|||
|
|
SELECT operationid, 'ticket_id', 'string', 1 FROM vtiger_ws_operation WHERE name = 'updatewebclaimv2';
|
|||
|
|
|
|||
|
|
INSERT INTO vtiger_ws_operation_parameters (operationid, name, type, sequence)
|
|||
|
|
SELECT operationid, 'claim_json', 'string', 2 FROM vtiger_ws_operation WHERE name = 'updatewebclaimv2';
|
|||
|
|
*/
|