Files
crm.clientright.ru/upload_file_crm_v2_backup.php

414 lines
18 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
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Для POST запросов отключаем вывод ошибок в HTML
$IS_POST = ($_SERVER['REQUEST_METHOD'] === 'POST');
if ($IS_POST) {
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
}
// Функция для записи в лог
function writeLog($message) {
$timestamp = date('Y-m-d H:i:s');
$line = "[$timestamp] $message\n";
// Пробуем писать в основной лог
@file_put_contents(__DIR__ . '/logs/upload_documents_v2.log', $line, FILE_APPEND | LOCK_EX);
// И в /tmp как запасной вариант
@file_put_contents('/tmp/upload_documents_v2.log', $line, FILE_APPEND | LOCK_EX);
// И в системный лог
error_log('[upload_documents_v2] ' . $message);
}
// Функция для JSON ответа
function json_response($data, $code = 200) {
if (!headers_sent()) {
http_response_code($code);
header('Content-Type: application/json; charset=utf-8');
}
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
// Быстрый ping
if (isset($_GET['ping'])) {
header('Content-Type: text/plain; charset=utf-8');
echo 'pong v2';
exit;
}
// Для POST запросов регистрируем обработчик фатальных ошибок
if ($IS_POST) {
register_shutdown_function(function() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
writeLog('FATAL: ' . $error['message'] . ' in ' . $error['file'] . ':' . $error['line']);
json_response([
'success' => false,
'error' => [
'type' => 'fatal',
'message' => 'Internal error'
]
], 500);
}
});
}
// Инициализация CRM (как в upload_documents_to_crm.php)
require_once 'config.inc.php';
require_once 'include/utils/utils.php';
require_once 'includes/Loader.php';
vimport('includes.runtime.Globals');
require_once 'include/database/PearDatabase.php';
require_once 'modules/Users/Users.php';
require_once 'include/Webservices/Utils.php';
require_once 'include/Webservices/Create.php';
require_once 'include/Webservices/Login.php';
require_once 'include/Webservices/AuthToken.php';
require_once 'include/Webservices/AddRelated.php';
require_once 'data/CRMEntity.php';
require_once 'modules/Vtiger/CRMEntity.php';
$adb = PearDatabase::getInstance();
// Вспомогательные функции
function getUserWsPrefix() {
global $adb;
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['Users']);
return ($rs && $adb->num_rows($rs) > 0) ? $adb->query_result($rs, 0, 'id') : 19;
}
function getProjectWsIdFromDB($projectId) {
global $adb;
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['Project']);
return ($rs && $adb->num_rows($rs) > 0) ? $adb->query_result($rs, 0, 'id') . 'x' . (int)$projectId : null;
}
function getDocumentFoldersWsPrefix() {
global $adb;
$rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['DocumentFolders']);
return ($rs && $adb->num_rows($rs) > 0) ? (int)$adb->query_result($rs, 0, 'id') : 22;
}
function getFolderWsIdByName($folderName) {
global $adb;
$rs = $adb->pquery('SELECT folderid FROM vtiger_attachmentsfolder WHERE foldername = ? LIMIT 1', [$folderName]);
if ($rs && $adb->num_rows($rs) > 0) {
$folderId = (int)$adb->query_result($rs, 0, 'folderid');
$prefix = getDocumentFoldersWsPrefix();
return $prefix . 'x' . $folderId;
}
return null;
}
// 🚀 НОВАЯ ФУНКЦИЯ: Создание документов с готовой сессией (формат v3)
function createDocumentsWithSession($data) {
global $adb, $current_user;
$sessionName = $data['sessionName'];
$projectId = (int)$data['projectid'];
$contactId = (int)$data['contactid'];
$userId = (int)($data['user_id'] ?? 1);
$filesArray = $data['files'] ?? [];
writeLog('🚀 Начинаем создание документов с готовой сессией: ' . $sessionName);
writeLog("📋 Проект: $projectId, Контакт: $contactId, Пользователь: $userId");
writeLog('📋 Файлов к обработке: ' . count($filesArray));
// Пропускаем инициализацию current_user, так как используем только webservice API
// $current_user = new Users();
// $current_user->retrieveCurrentUserInfoFromFile($userId);
// Упрощенный подход: используем только webservice API без обращений к БД
$projectWsId = '20x' . $projectId; // Предполагаем стандартный префикс для Project
$assignedUserWsId = '19x' . $userId; // Предполагаем стандартный префикс для Users
$folderWsId = '22x1'; // Default папка
writeLog("✅ Project WS ID: $projectWsId");
writeLog("👤 User WS ID: $assignedUserWsId");
writeLog("📁 Папка: $folderWsId");
// Инициализируем CURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://crm.clientright.ru/webservice.php');
curl_setopt($ch, CURLOPT_POST, 1);
$results = [];
foreach ($filesArray as $i => $file) {
$fileName = $file['original_file_name'] ?? $file['file_name'] ?? 'Unknown';
writeLog("📄 Обрабатываем файл #{$i}: $fileName");
try {
// Проверяем обязательные поля
if (empty($file['file_url'])) {
throw new Exception("Отсутствует file_url");
}
// Определяем название и описание документа
$documentTitle = $file['upload_description'] ?? $file['original_file_name'] ?? $file['file_name'] ?? 'Документ';
$documentContent = sprintf(
'Загружено через n8n. ID: %s, Контакт: %d, Проект: %d, Загружено: %s',
$file['id'] ?? 'N/A',
$contactId,
$projectId,
$file['uploaded_at'] ?? date('Y-m-d H:i:s')
);
// Создаём документ
$docElement = [
'notes_title' => $documentTitle,
'filename' => $file['file_url'],
'assigned_user_id' => $assignedUserWsId,
'notecontent' => $documentContent,
'filetype' => 'application/pdf',
'filesize' => '0',
'filelocationtype' => 'E', // External URL
'fileversion' => '1.0',
'filestatus' => '1', // Active
'folderid' => $folderWsId,
];
writeLog("📤 Создаём документ '$documentTitle'");
// Отправляем запрос на создание документа
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'operation' => 'create',
'sessionName' => $sessionName,
'elementType' => 'Documents',
'element' => json_encode($docElement, JSON_UNESCAPED_UNICODE),
]);
$resp = curl_exec($ch);
if ($resp === false) {
throw new Exception('CURL error: ' . curl_error($ch));
}
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
writeLog("📥 Ответ создания документа: " . substr($resp, 0, 300));
$doc = json_decode($resp, true);
if (!$doc || !$doc['success'] || empty($doc['result']['id'])) {
throw new Exception('Failed to create document: ' . substr($resp, 0, 200));
}
$documentWsId = $doc['result']['id'];
list(, $docNumericId) = explode('x', $documentWsId, 2);
writeLog("✅ Документ создан: $documentWsId");
// Привязываем к проекту
writeLog("🔗 Привязываем документ $documentWsId к проекту $projectWsId");
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'operation' => 'AddRelated',
'sessionName' => $sessionName,
'sourceRecordId' => $projectWsId,
'relatedRecordId' => $documentWsId,
]);
$resp = curl_exec($ch);
$relationOk = false;
if ($resp !== false) {
$resp = ltrim($resp, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
writeLog("📥 Ответ AddRelated: " . substr($resp, 0, 200));
$rel = json_decode($resp, true);
$relationOk = isset($rel['result']['message']) && $rel['result']['message'] === 'successfull';
}
// Если webservice не сработал - используем прямую привязку
if (!$relationOk) {
writeLog("⚠️ AddRelated не сработал, используем прямую привязку");
try {
// Устанавливаем current_user для CRMEntity
if (!isset($current_user) || !$current_user) {
$current_user = new Users();
$current_user->retrieveCurrentUserInfoFromFile($userId);
}
$focus = CRMEntity::getInstance('Project');
relateEntities($focus, 'Project', $projectId, 'Documents', (int)$docNumericId);
writeLog("✅ Прямая привязка успешна");
} catch (Exception $directRelationError) {
writeLog("❌ Прямая привязка не удалась: " . $directRelationError->getMessage());
}
} else {
writeLog("✅ AddRelated успешен");
}
// Возвращаем результат с сохранением всех исходных данных
$result = array_merge($file, [
'status' => 'success',
'projectid' => $projectId,
'contactid' => $contactId,
'crm_result' => [
'document_id' => $documentWsId,
'document_numeric_id' => $docNumericId,
'project_id' => $projectId,
'contact_id' => $contactId,
'folder_id' => $folderWsId,
'message' => 'Документ создан и привязан к проекту' . (!$relationOk ? ' (прямая привязка)' : '')
]
]);
$results[] = $result;
writeLog("✅ Файл '$fileName' успешно обработан");
} catch (Exception $e) {
writeLog("❌ Ошибка для файла '$fileName': " . $e->getMessage());
$results[] = array_merge($file, [
'status' => 'error',
'projectid' => $projectId,
'contactid' => $contactId,
'crm_result' => [
'message' => $e->getMessage()
]
]);
}
}
curl_close($ch);
$successCount = count(array_filter($results, function($r) { return $r['status'] === 'success'; }));
writeLog("🏁 Обработка завершена. Успешно: $successCount/" . count($results));
return $results;
}
// Обработка запроса
if ($IS_POST) {
writeLog('=== START POST REQUEST V2 ===');
writeLog('Headers: ' . json_encode(getallheaders(), JSON_UNESCAPED_UNICODE));
// Получаем и проверяем входные данные
$input = file_get_contents('php://input');
writeLog('Raw input: ' . substr($input, 0, 1000) . (strlen($input) > 1000 ? '...(truncated)' : ''));
$input = ltrim($input, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20");
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
writeLog('❌ JSON Error: ' . json_last_error_msg());
json_response([
'success' => false,
'error' => ['message' => 'Invalid JSON: ' . json_last_error_msg()]
], 400);
}
writeLog('Parsed data keys: ' . implode(', ', array_keys($data)));
// 🔑 НОВЫЙ ФОРМАТ: Ожидаем все данные в одном объекте
if (empty($data['sessionName'])) {
writeLog('❌ Error: sessionName is required');
json_response([
'success' => false,
'error' => ['message' => 'sessionName is required in request data']
], 400);
}
if (empty($data['projectid'])) {
writeLog('❌ Error: projectid is required');
json_response([
'success' => false,
'error' => ['message' => 'projectid is required in request data']
], 400);
}
if (empty($data['contactid'])) {
writeLog('❌ Error: contactid is required');
json_response([
'success' => false,
'error' => ['message' => 'contactid is required in request data']
], 400);
}
// Поддерживаем оба формата: files и documents
$filesArray = null;
if (!empty($data['files']) && is_array($data['files'])) {
$filesArray = $data['files'];
} elseif (!empty($data['documents']) && is_array($data['documents'])) {
$filesArray = $data['documents'];
}
if (empty($filesArray)) {
writeLog('❌ Error: files or documents array is required');
json_response([
'success' => false,
'error' => ['message' => 'files or documents array is required in request data']
], 400);
}
writeLog("🔑 Сессия: {$data['sessionName']}");
writeLog("📋 Проект: {$data['projectid']}, Контакт: {$data['contactid']}");
writeLog('📄 Файлов: ' . count($filesArray));
// Нормализуем данные: всегда используем 'files' внутри функции
$normalizedData = $data;
$normalizedData['files'] = $filesArray;
// Создаём документы с готовой сессией
$results = createDocumentsWithSession($normalizedData);
// Успешный ответ
writeLog('✅ Success: processed ' . count($results) . ' files');
json_response([
'success' => true,
'total_processed' => count($filesArray),
'results' => $results,
'session_used' => $sessionName
]);
} else {
// GET запрос - документация API
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'success' => true,
'message' => 'Upload Documents API v2 - Ready',
'endpoint' => 'POST to this URL with complete data object',
'format' => [
'sessionName' => 'string (required - from n8n CRM login)',
'projectid' => 'string (required - project ID)',
'contactid' => 'string (required - contact ID)',
'user_id' => 'string (optional - default "1")',
'files' => [ // или 'documents' - поддерживаются оба формата
[
'id' => 'string (file UUID)',
'file_id' => 'string (S3 path)',
'file_url' => 'string (required - full S3 URL)',
'file_name' => 'string (S3 filename)',
'field_name' => 'string (form field)',
'uploaded_at' => 'string (ISO datetime)',
'original_file_name' => 'string (user filename)',
'upload_description' => 'string (document description)',
'filename_for_upload' => 'string (display filename)'
]
]
],
'example' => [
'sessionName' => '27c160f968cc0cea6dd38',
'projectid' => '392790',
'contactid' => '320096',
'user_id' => '1',
'documents' => [ // пример с 'documents' (как от n8n)
[
'id' => 'd0d3d844-2db0-4c68-82e4-a803881b7527',
'file_id' => 'clpr_claims/5eac8512-1bc5-49ee-952c-3ec390db6964/d0d3d844-2db0-4c68-82e4-a803881b7527__pretenzionnaya-rabota.pdf',
'file_url' => 'https://s3.twcstorage.ru/f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c/clpr_claims/5eac8512-1bc5-49ee-952c-3ec390db6964/d0d3d844-2db0-4c68-82e4-a803881b7527__pretenzionnaya-rabota.pdf',
'file_name' => 'd0d3d844-2db0-4c68-82e4-a803881b7527__pretenzionnaya-rabota.pdf',
'field_name' => 'uploads[1][0]',
'uploaded_at' => '2025-09-15T22:46:51.913+03:00',
'original_file_name' => '1757965604702.pdf',
'upload_description' => 'Претензионная работа',
'filename_for_upload' => '1757965604702.pdf'
]
]
]
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
?>