Files
crm.clientright.ru/include/Webservices/UpsertProject.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

461 lines
22 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-интерфейс для создания/обновления Проекта (Upsert)
*
* Логика:
* - Если передан project_id → обновляем существующий проект
* - Если project_id не передан → создаём новый
*
* Принимает JSON с данными проекта
*
* Автор: Фёдор, 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');
/**
* Получить список документов из PostgreSQL по claim_id
*
* @param string $claim_id - UUID заявки
* @return array|null - Массив документов или null если не найдено
*/
function getDocumentsFromPostgres($claim_id) {
$logFile = 'logs/UpsertProject.log';
// Параметры подключения к PostgreSQL
// Можно переопределить через config_override.php или использовать значения по умолчанию
$pg_host = defined('POSTGRES_HOST') ? POSTGRES_HOST : '147.45.189.234';
$pg_port = defined('POSTGRES_PORT') ? POSTGRES_PORT : 5432;
$pg_db = defined('POSTGRES_DB') ? POSTGRES_DB : 'default_db'; // Уточнить название БД если нужно
$pg_user = defined('POSTGRES_USER') ? POSTGRES_USER : 'gen_user';
$pg_password = defined('POSTGRES_PASSWORD') ? POSTGRES_PASSWORD : '2~~9_^kVsU?2\\S';
try {
$dsn = sprintf('pgsql:host=%s;port=%d;dbname=%s', $pg_host, $pg_port, $pg_db);
$pdo = new PDO($dsn, $pg_user, $pg_password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
// Запрос: ищем по id (UUID) или по payload->>'claim_id' (строка)
$query = "
SELECT
payload->'documents_meta' AS documents_meta
FROM clpr_claims
WHERE
id::text = :claim_id
OR payload->>'claim_id' = :claim_id
LIMIT 1
";
$stmt = $pdo->prepare($query);
$stmt->execute([':claim_id' => $claim_id]);
$row = $stmt->fetch();
if ($row && !empty($row['documents_meta'])) {
// Если это JSON строка, декодируем
$documents_meta = $row['documents_meta'];
if (is_string($documents_meta)) {
$documents_meta = json_decode($documents_meta, true);
}
if (is_array($documents_meta) && count($documents_meta) > 0) {
$logstring = date('Y-m-d H:i:s') . ' ✅ Получено документов из PostgreSQL: ' . count($documents_meta);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
return $documents_meta;
}
}
$logstring = date('Y-m-d H:i:s') . ' ⚠️ Документы не найдены для claim_id: ' . $claim_id;
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
return null;
} catch (PDOException $e) {
$logstring = date('Y-m-d H:i:s') . ' ❌ Ошибка PostgreSQL: ' . $e->getMessage();
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
return null;
} catch (Exception $e) {
$logstring = date('Y-m-d H:i:s') . ' ❌ Ошибка получения документов: ' . $e->getMessage();
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
return null;
}
}
/**
* Upsert проекта
*
* @param string $project_json - JSON с данными проекта:
* {
* "project_id": "12345", // Опционально - если есть, обновляем
* "claim_id": "uuid", // ID заявки из PostgreSQL
* "contact_id": "320096", // ID контакта (обязательно для создания)
* "result": "JSON string", // Результат UpsertAccounts (парсится автоматически)
* "offender_ids": ["390680"], // Альтернатива result - массив ID контрагентов
* "projectdata": { // Данные проекта (cf_* поля)
* "cf_2206": "SMS код",
* "cf_1830": "категория",
* ...
* },
* "bank": "Название банка", // Банк для выплаты (cf_2626)
* "documents_meta": [ // Список документов (cf_2644)
* {
* "field_label": "Название документа",
* "field_name": "document_type",
* "file_name": "document.pdf",
* "original_file_name": "original.pdf"
* }
* ]
* }
*
* Контрагенты распределяются:
* - accounts[0] → cf_2274 (основной ответчик)
* - accounts[1] → cf_2276 (агент/второй ответчик)
*
* @param mixed $user - пользователь CRM
* @return string JSON с результатом
*/
function vtws_upsertproject($project_json, $user = false) {
$logFile = 'logs/UpsertProject.log';
$logstring = date("Y-m-d H:i:s") . ' REQUEST: ' . substr($project_json, 0, 2000);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
global $adb, $current_user;
// Очистка JSON
$project_json = trim($project_json);
$project_json = preg_replace('/^\xEF\xBB\xBF/', '', $project_json);
if (preg_match('/^".*"$/s', $project_json)) {
$project_json = substr($project_json, 1, -1);
$project_json = stripcslashes($project_json);
}
// Парсим JSON
$data = json_decode($project_json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$error = 'Ошибка парсинга JSON: ' . json_last_error_msg();
file_put_contents($logFile, date("Y-m-d H:i:s") . ' ❌ ' . $error . PHP_EOL, FILE_APPEND);
throw new WebServiceException(WebServiceErrorCode::$INVALIDID, $error);
}
// Результат
$result = array(
'success' => false,
'project_id' => null,
'claim_id' => null,
'action' => null,
'offender_id' => null,
'agent_id' => null,
'message' => ''
);
// Извлекаем данные
$project_id = trim($data['project_id'] ?? '');
$claim_id = trim($data['claim_id'] ?? '');
// Поддерживаем и contact_id и contactid (для обратной совместимости)
$contact_id = trim($data['contact_id'] ?? $data['contactid'] ?? '');
$projectdata = $data['projectdata'] ?? [];
// ✅ Банк приходит в корне JSON, а не в projectdata
$bank = trim($data['bank'] ?? '');
// ✅ Список документов: сначала проверяем переданный, если нет - получаем из PostgreSQL
$documents_meta = $data['documents_meta'] ?? null;
// Если documents_meta не передан, но есть claim_id - получаем из PostgreSQL
if (empty($documents_meta) && !empty($claim_id)) {
$documents_meta = getDocumentsFromPostgres($claim_id);
}
// Если всё ещё пусто, используем пустой массив
if (empty($documents_meta)) {
$documents_meta = [];
}
// Извлекаем контрагентов из result (если передан) или из offender_ids
$offender_ids = [];
if (!empty($data['result'])) {
// Парсим result от UpsertAccounts
$accountsResult = $data['result'];
if (is_string($accountsResult)) {
$accountsResult = json_decode($accountsResult, true);
}
// Извлекаем account_id из accounts[]
if (isset($accountsResult['accounts']) && is_array($accountsResult['accounts'])) {
foreach ($accountsResult['accounts'] as $account) {
if (!empty($account['account_id'])) {
$offender_ids[] = $account['account_id'];
}
}
}
$logstring = date('Y-m-d H:i:s') . ' Извлечены offender_ids из result: ' . json_encode($offender_ids);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
} elseif (!empty($data['offender_ids'])) {
$offender_ids = $data['offender_ids'];
}
// cf_2274 = первый контрагент (основной ответчик)
// cf_2276 = второй контрагент (агент/второй ответчик)
$offender_id = count($offender_ids) > 0 ? $offender_ids[0] : '';
$agent_id = count($offender_ids) > 1 ? $offender_ids[1] : '';
$logstring = date('Y-m-d H:i:s') . " Данные: project_id=$project_id, claim_id=$claim_id, contact_id=$contact_id, offender_id=$offender_id, agent_id=$agent_id";
if (!empty($documents_meta)) {
$doc_count = is_array($documents_meta) ? count($documents_meta) : 0;
$logstring .= ", documents_meta count=$doc_count";
}
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
try {
// ========================================
// ПРОВЕРКА СУЩЕСТВОВАНИЯ ПРОЕКТА
// ========================================
$existingProjectId = null;
if (!empty($project_id)) {
$project_id = preg_replace('/[^0-9]/', '', $project_id);
$query = "SELECT p.projectid FROM vtiger_project p
LEFT JOIN vtiger_crmentity e ON e.crmid = p.projectid
WHERE e.deleted = 0 AND p.projectid = ? LIMIT 1";
$res = $adb->pquery($query, array($project_id));
if ($adb->num_rows($res) > 0) {
$existingProjectId = $adb->query_result($res, 0, 'projectid');
}
}
// ========================================
// ФОРМИРУЕМ ПАРАМЕТРЫ
// ========================================
$params = array();
// Если создаём новый проект - нужны contact_id и offender_id
if (empty($existingProjectId)) {
if (empty($contact_id) || empty($offender_id)) {
throw new Exception('Для создания проекта нужны contact_id и offender_ids');
}
// Получаем название контакта
$query = "SELECT c.lastname 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));
$contactName = $adb->num_rows($res) > 0 ? $adb->query_result($res, 0, 'lastname') : 'Клиент';
// Получаем название контрагента
$query = "SELECT a.accountname FROM vtiger_account a
LEFT JOIN vtiger_crmentity e ON e.crmid = a.accountid
WHERE e.deleted = 0 AND a.accountid = ? LIMIT 1";
$res = $adb->pquery($query, array($offender_id));
$accountName = $adb->num_rows($res) > 0 ? $adb->query_result($res, 0, 'accountname') : 'Контрагент';
// Название проекта
$params['projectname'] = $contactName . ' ' . $accountName;
$params['linktoaccountscontacts'] = '12x' . $contact_id;
$params['cf_2274'] = '11x' . $offender_id; // Основной ответчик
$params['projectstatus'] = 'модерация';
$params['projecttype'] = 'Претензионно - исковая работа';
$params['assigned_user_id'] = vtws_getWebserviceEntityId('Users', $current_user->id);
// Заявитель по умолчанию
if (!isset($projectdata['cf_1994'])) {
$params['cf_1994'] = vtws_getWebserviceEntityId('Accounts', 62345); // МОО КЛИЕНТПРАВ
}
}
// Агент (второй ответчик)
if (!empty($agent_id)) {
$params['cf_2276'] = '11x' . $agent_id;
}
// Связь контакт/оффендер для обновления тоже можно передать
if (!empty($contact_id) && !empty($existingProjectId)) {
$params['linktoaccountscontacts'] = '12x' . $contact_id;
}
if (!empty($offender_id) && !empty($existingProjectId)) {
$params['cf_2274'] = '11x' . $offender_id;
}
// Маппинг полей из projectdata
$fieldMapping = array(
'cf_2206' => 'cf_2206', // SMS код
'cf_2210' => 'cf_2210', // IP
'cf_2212' => 'cf_2212', // Источник
'cf_2214' => 'cf_2214', // Регион
'cf_2208' => 'cf_2208', // Form ID
'cf_1830' => 'cf_1830', // Категория
'cf_1469' => 'cf_1469', // Направление
'cf_1191' => 'cf_1191', // Цена договора
'cf_1189' => 'cf_1189', // Предмет договора
'cf_1203' => 'cf_1203', // Дата договора
'cf_1839' => 'cf_1839', // Дата начала
'cf_1841' => 'cf_1841', // Дата окончания
'cf_1207' => 'cf_1207', // Ущерб
'cf_1479' => 'cf_1479', // Стоимость услуг
'cf_1227' => 'cf_1227', // Прогресс
'cf_1231' => 'cf_1231', // Страна
'cf_1239' => 'cf_1239', // Отель
'cf_1566' => 'cf_1566', // Транспорт
'cf_1564' => 'cf_1564', // Страховка
'cf_1249' => 'cf_1249', // Прочее
'cf_1471' => 'cf_1471', // Самостоятельно
'cf_1473' => 'cf_1473', // Дата претензии
'cf_1475' => 'cf_1475', // Возвращено
'cf_1994' => 'cf_1994', // Заявитель
'description' => 'description'
);
// Поля дат, которые нужно преобразовать из DD.MM.YYYY в YYYY-MM-DD
$dateFields = array('cf_1203', 'cf_1839', 'cf_1841', 'cf_1473');
foreach ($fieldMapping as $input => $crm) {
if (isset($projectdata[$input]) && $projectdata[$input] !== null) {
$value = $projectdata[$input];
// Для cf_1994 (Заявитель) нужен формат 11xID
if ($crm === 'cf_1994' && !empty($value) && strpos($value, 'x') === false) {
$value = vtws_getWebserviceEntityId('Accounts', $value);
}
// ✅ Преобразование дат из DD.MM.YYYY в YYYY-MM-DD
if (in_array($crm, $dateFields) && !empty($value)) {
// Проверяем формат DD.MM.YYYY
if (preg_match('/^(\d{2})\.(\d{2})\.(\d{4})$/', $value, $matches)) {
$day = $matches[1];
$month = $matches[2];
$year = $matches[3];
// Проверяем валидность даты
if (checkdate((int)$month, (int)$day, (int)$year)) {
$value = sprintf('%s-%s-%s', $year, $month, $day);
$logstring = date('Y-m-d H:i:s') . ' ✅ Дата преобразована для ' . $crm . ': ' . $projectdata[$input] . ' → ' . $value;
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
} else {
$logstring = date('Y-m-d H:i:s') . ' ⚠️ Некорректная дата для ' . $crm . ': ' . $value;
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
}
}
// Если уже в формате YYYY-MM-DD, оставляем как есть
}
$params[$crm] = $value;
}
}
// ✅ Банк (cf_2626) приходит в корне JSON, а не в projectdata
if (!empty($bank)) {
$params['cf_2626'] = $bank;
$logstring = date('Y-m-d H:i:s') . ' ✅ Банк (cf_2626) установлен: ' . $bank;
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
}
// ✅ Список документов (cf_2644) - формируем читаемый список
if (!empty($documents_meta) && is_array($documents_meta)) {
$documents_list = array();
$doc_counter = 1;
foreach ($documents_meta as $doc) {
// Поддерживаем разные форматы: field_label или field_name
$doc_name = trim($doc['field_label'] ?? $doc['field_name'] ?? '');
if (!empty($doc_name)) {
// Формируем строку: "1. Название документа"
$doc_line = $doc_counter . '. ' . $doc_name;
// Опционально добавляем имя файла, если есть
$file_name = trim($doc['file_name'] ?? $doc['original_file_name'] ?? '');
if (!empty($file_name)) {
$doc_line .= ' (' . $file_name . ')';
}
$documents_list[] = $doc_line;
$doc_counter++;
}
}
if (!empty($documents_list)) {
// Объединяем в многострочный текст
$documents_text = implode("\n", $documents_list);
$params['cf_2644'] = $documents_text;
$logstring = date('Y-m-d H:i:s') . ' ✅ Список документов (cf_2644) установлен: ' . count($documents_list) . ' документов';
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
}
}
// ========================================
// СОЗДАНИЕ ИЛИ ОБНОВЛЕНИЕ
// ========================================
if (!empty($existingProjectId)) {
// === ОБНОВЛЕНИЕ ===
$params['id'] = '33x' . $existingProjectId; // 33x для Project
$logstring = date('Y-m-d H:i:s') . ' 📝 Обновляем проект ' . $existingProjectId . ': ' . json_encode($params, JSON_UNESCAPED_UNICODE);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
$project = vtws_revise($params, $current_user);
$result['success'] = true;
$result['project_id'] = $existingProjectId;
$result['claim_id'] = $claim_id;
$result['action'] = 'updated';
$result['offender_id'] = $offender_id;
$result['agent_id'] = $agent_id ?: null;
$result['message'] = 'Проект обновлён';
$logstring = date('Y-m-d H:i:s') . ' ✅ Проект ' . $existingProjectId . ' обновлён';
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
} else {
// === СОЗДАНИЕ ===
$logstring = date('Y-m-d H:i:s') . ' 🆕 Создаём проект: ' . json_encode($params, JSON_UNESCAPED_UNICODE);
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
$project = vtws_create('Project', $params, $current_user);
$newProjectId = substr($project['id'], strpos($project['id'], 'x') + 1); // Убираем префикс (63x)
$result['success'] = true;
$result['project_id'] = $newProjectId;
$result['claim_id'] = $claim_id;
$result['action'] = 'created';
$result['offender_id'] = $offender_id;
$result['agent_id'] = $agent_id ?: null;
$result['message'] = 'Проект создан';
$logstring = date('Y-m-d H:i:s') . ' ✅ Создан проект ' . $newProjectId;
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') . ' ❌ WebService ошибка: ' . $ex->getMessage();
file_put_contents($logFile, $logstring . PHP_EOL, FILE_APPEND);
throw $ex;
} catch (Exception $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 new WebServiceException(WebServiceErrorCode::$INVALIDID, $ex->getMessage());
}
$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);
}