false, 'error' => [ 'type' => 'fatal', 'message' => 'Internal error', ] ], 500); } }); } /** * Функция для получения entity ID по имени модуля */ function vtws_getEntityId($entityName) { global $adb; $wsrs = $adb->pquery('select id from vtiger_ws_entity where name=?', array($entityName)); if ($wsrs && $adb->num_rows($wsrs) == 1) { $wsid = $adb->query_result($wsrs, 0, 0); } else { $wsid = 0; } return $wsid; } /** * Функция для получения webservice ID пользователя */ function getWebserviceUserId($userId) { global $adb; $entityId = vtws_getEntityId('Users'); return $entityId . 'x' . $userId; } /** * Функция для получения webservice ID проекта */ function getWebserviceProjectId($projectId) { global $adb; $entityId = vtws_getEntityId('Project'); return $entityId . 'x' . $projectId; } /** * Префикс WS для папок документов */ function getDocumentFoldersWsPrefix() { global $adb; $rs = $adb->pquery("SELECT id FROM vtiger_ws_entity WHERE name=?", ['DocumentFolders']); if ($rs && $adb->num_rows($rs) > 0) { return (int)$adb->query_result($rs, 0, 'id'); } return 22; } /** * Получить WS ID папки по имени, иначе null */ 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; } /** * Безопасная аутентификация через challenge/response */ function authenticateWithCRM($username) { // 1) Получаем challenge токен $challenge = vtws_getchallenge($username); if (empty($challenge['token'])) { throw new Exception('Не удалось получить challenge token'); } // 2) Берем accesskey пользователя $user = new Users(); $userId = $user->retrieve_user_id($username); if (!$userId) { throw new Exception("Пользователь {$username} не найден"); } $accessKey = vtws_getUserAccessKey($userId); if (!$accessKey) { throw new Exception('Access key пользователя не найден'); } // 3) Логинимся $generatedKey = md5($challenge['token'] . $accessKey); $loggedUser = vtws_login($username, $generatedKey); // Для отладки можно писать в лог // error_log("WS login ok for $username, token={$challenge['token']} md5=$generatedKey"); // Установим $current_user для операций CRMEntity global $current_user; $current_user = new Users(); $current_user->retrieveCurrentUserInfoFromFile((int)$userId); return $loggedUser; } /** * Функция для получения ID папки по умолчанию */ function getDefaultFolderId() { global $adb; $result = $adb->pquery('SELECT folderid FROM vtiger_attachmentsfolder WHERE foldername = ? LIMIT 1', array('Default')); if ($result && $adb->num_rows($result) > 0) { $folderId = $adb->query_result($result, 0, 'folderid'); $entityId = vtws_getEntityId('DocumentFolders'); return $entityId . 'x' . $folderId; } return '22x1'; // Fallback ID } /** * Основная функция для создания документов в CRM */ function createDocumentsInCRM($filesArray, $adminUsername = 'api') { global $adb; writeLog("🚀 Начинаем создание документов для пользователя: $adminUsername"); try { writeLog("🔐 Попытка аутентификации..."); // Проверяем существование пользователя $user = new Users(); $userId = $user->retrieve_user_id($adminUsername); if (!$userId) { throw new Exception("Пользователь '$adminUsername' не найден"); } writeLog("✅ Пользователь найден, ID: $userId"); // Безопасная аутентификация $authenticatedUser = authenticateWithCRM($adminUsername); writeLog("✅ Аутентификация успешна"); $results = []; foreach ($filesArray as $index => $fileData) { writeLog("📄 Обрабатываем файл #$index: " . ($fileData['file_name'] ?? 'Unknown')); try { // Получаем webservice ID пользователя и проекта $assignedUserId = getWebserviceUserId($fileData['user_id']); $projectId = getWebserviceProjectId($fileData['projectid']); // Папка: сначала пробуем 'Суд', иначе Default $folderId = getFolderWsIdByName('Суд'); if (!$folderId) { $folderId = getDefaultFolderId(); writeLog("⚠️ Папка 'Суд' не найдена, используем по умолчанию: $folderId"); } else { writeLog("📁 Используем папку 'Суд': $folderId"); } writeLog("🔗 WebService IDs - User: $assignedUserId, Project: $projectId, Folder: $folderId"); // Формируем данные документа $documentData = [ 'notes_title' => $fileData['description'] ?: $fileData['file_name'], 'filename' => $fileData['url'], 'assigned_user_id' => $assignedUserId, 'notecontent' => 'Автоматически загружен из S3. Контакт: ' . $fileData['contactid'], 'filetype' => 'application/pdf', 'filesize' => '0', 'filelocationtype' => 'E', 'fileversion' => '1.0', 'filestatus' => '1', 'filedownloadcount' => '0', 'folderid' => $folderId, 'created_user_id' => $assignedUserId, 'starred' => '0', 'tags' => 'S3,автозагрузка' ]; writeLog("📋 Данные документа подготовлены: " . json_encode($documentData, JSON_UNESCAPED_UNICODE)); // Создаем документ $document = vtws_create('Documents', $documentData, $authenticatedUser); if ($document && isset($document['id'])) { writeLog("✅ Документ создан с ID: " . $document['id']); // Привязываем документ к проекту try { vtws_add_related($projectId, $document['id'], false, $authenticatedUser); writeLog("✅ Документ привязан к проекту"); $results[] = [ 'status' => 'success', 'file_name' => $fileData['file_name'], 'document_id' => $document['id'], 'project_id' => $fileData['projectid'], 'url' => $fileData['url'], 'message' => 'Документ успешно создан и привязан к проекту' ]; } catch (Exception $e) { // Fallback: привязываем напрямую через CRMEntity writeLog("⚠️ Webservice AddRelated не сработал: " . $e->getMessage() . ". Пытаемся привязать напрямую..."); require_once 'data/CRMEntity.php'; require_once 'modules/Vtiger/CRMEntity.php'; $docIds = vtws_getIdComponents($document['id']); $docNumericId = (int)$docIds[1]; $focus = CRMEntity::getInstance('Project'); relateEntities($focus, 'Project', (int)$fileData['projectid'], 'Documents', $docNumericId); writeLog("✅ Привязка выполнена напрямую (Project {$fileData['projectid']} -> Document {$docNumericId})"); $results[] = [ 'status' => 'success', 'file_name' => $fileData['file_name'], 'document_id' => $document['id'], 'project_id' => $fileData['projectid'], 'url' => $fileData['url'], 'message' => 'Документ создан и привязан к проекту (fallback)' ]; } } else { writeLog("❌ Ошибка создания документа"); $results[] = [ 'status' => 'error', 'file_name' => $fileData['file_name'], 'project_id' => $fileData['projectid'], 'url' => $fileData['url'], 'message' => 'Ошибка создания документа' ]; } } catch (Exception $e) { writeLog("❌ Критическая ошибка для файла: " . $e->getMessage()); $results[] = [ 'status' => 'error', 'file_name' => $fileData['file_name'] ?? 'Unknown', 'project_id' => $fileData['projectid'] ?? 'Unknown', 'url' => $fileData['url'] ?? 'Unknown', 'message' => 'Ошибка: ' . $e->getMessage() ]; } } writeLog("🏁 Обработка завершена. Всего файлов: " . count($filesArray)); return $results; } catch (Exception $e) { writeLog("💥 Критическая ошибка: " . $e->getMessage()); return [ 'status' => 'critical_error', 'message' => 'Ошибка аутентификации в CRM: ' . $e->getMessage() ]; } } /** * Получение access key пользователя */ function vtws_getUserAccessKey($userId) { global $adb; $sql = "SELECT accesskey FROM vtiger_users WHERE id = ?"; $result = $adb->pquery($sql, array($userId)); if ($result && $adb->num_rows($result) > 0) { return $adb->query_result($result, 0, 'accesskey'); } return null; } // Пример использования: if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Получаем JSON данные (без BOM) и пробуем распознать формат writeLog('POST '.$_SERVER['REQUEST_URI'].' CT='.(isset($_SERVER['CONTENT_TYPE'])?$_SERVER['CONTENT_TYPE']:'').', CL='.(isset($_SERVER['CONTENT_LENGTH'])?$_SERVER['CONTENT_LENGTH']:'').' UA='.(isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'').', IP='.(isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'') ); $input = file_get_contents('php://input'); writeLog('RAW BODY: '.substr($input,0,2048).(strlen($input)>2048?'...':'')); $input = ltrim($input, "\xEF\xBB\xBF\x00\x09\x0A\x0D\x20"); $filesArray = 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); exit; } // Авто-распаковка n8n-форматов: // 1) [{"data":[...]}] if (is_array($filesArray) && count($filesArray) === 1 && isset($filesArray[0]['data']) && is_array($filesArray[0]['data'])) { $filesArray = $filesArray[0]['data']; } // 2) {"data":[...]} if (is_array($filesArray) && isset($filesArray['data']) && is_array($filesArray['data'])) { $filesArray = $filesArray['data']; } if (!is_array($filesArray)) { writeLog('BAD FORMAT: not an array after normalization'); json_response(['success' => false, 'error' => ['message' => 'JSON must be an array of file items']], 400); exit; } writeLog('ITEMS: '.count($filesArray)); // Создаем документы $results = createDocumentsInCRM($filesArray); writeLog('DONE: processed='.count($filesArray)); // Возвращаем результат json_response([ 'success' => true, 'results' => $results, 'total_processed' => count($filesArray) ]); } else { // Тестовый запуск с вашими данными $testData = [ [ "session_token" => "sess_dedc540e-f60d-491b-b6bb-ef7f7d04a366", "user_id" => 1, "contactid" => 120374, "description" => "Иск", "projectid" => 354918, "pages" => 2, "url" => "https://s3.twcstorage.ru/f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c/clientright/120374/1757346451126.pdf", "file_name" => "1757346451126.pdf" ], [ "session_token" => "sess_dedc540e-f60d-491b-b6bb-ef7f7d04a366", "user_id" => 1, "contactid" => 120374, "description" => "доказательство направления претензии", "projectid" => 354918, "pages" => 4, "url" => "https://s3.twcstorage.ru/f9825c87-4e3558f6-f9b6-405c-ad3d-d1535c49b61c/clientright/120374/1757346454717.pdf", "file_name" => "1757346454717.pdf" ] ]; $results = createDocumentsInCRM($testData); header('Content-Type: application/json'); echo json_encode([ 'success' => true, 'results' => $results, 'total_processed' => count($testData) ], JSON_UNESCAPED_UNICODE); } ?>