Files
crm.clientright.ru/modules/WSAPP/Handlers/vtigerCRMHandler.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

531 lines
17 KiB
PHP

<?php
/* +***********************************************************************************
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
* ("License"); You may not use this file except in compliance with the License
* The Original Code is: vtiger CRM Open Source
* The Initial Developer of the Original Code is vtiger.
* Portions created by vtiger are Copyright (C) vtiger.
* All Rights Reserved.
* *********************************************************************************** */
require_once 'modules/WSAPP/WSAPP.php';
require_once 'include/Webservices/Utils.php';
require_once 'include/database/PearDatabase.php';
require_once 'include/Webservices/GetUpdates.php';
require_once 'include/utils/CommonUtils.php';
require_once 'modules/WSAPP/Utils.php';
require_once 'include/Webservices/Update.php';
require_once 'include/Webservices/Revise.php';
require_once 'modules/WSAPP/Handlers/SyncHandler.php';
class vtigerCRMHandler extends SyncHandler {
private $assignToChangedRecords;
protected $clientSyncType = 'user';
public function __construct($appkey) {
$this->key = $appkey;
$this->assignToChangedRecords = array();
}
public function get($module, $token, $user) {
$syncModule = $module;
$this->user = $user;
$syncModule = $module;
$syncType = 'user';
if (!$this->isClientUserSyncType()) {
if($this->isClientUserAndGroupSyncType()){
$syncType = 'userandgroup';
}else{
$syncType = 'application';
}
}
$result = vtws_sync($token, $syncModule, $syncType, $this->user);
$result['updated'] = $this->translateTheReferenceFieldIdsToName($result['updated'], $syncModule, $user);
return $this->nativeToSyncFormat($result);
}
public function put($recordDetails, $user) {
global $current_user;
$current_user = $user;
$this->user = $user;
$recordDetails = $this->syncToNativeFormat($recordDetails);
$createdRecords = $recordDetails['created'];
$updatedRecords = $recordDetails['updated'];
$deletedRecords = $recordDetails['deleted'];
$updateDuplicateRecords = array();
if (count($createdRecords) > 0) {
$createdRecords = $this->translateReferenceFieldNamesToIds($createdRecords, $user);
$createdRecords = $this->fillNonExistingMandatoryPicklistValues($createdRecords);
$createdRecords = $this->fillMandatoryFields($createdRecords, $user);
}
foreach ($createdRecords as $index => $record) {
try {
$createdRecords[$index] = vtws_create($record['module'], $record, $this->user);
} catch (DuplicateException $e) {
$skipped = true;
$duplicateRecordIds = $e->getDuplicateRecordIds();
$duplicatesResult = $this->triggerSyncActionForDuplicate($record, $duplicateRecordIds);
if ($duplicatesResult) {
$updateDuplicateRecords[$index] = $duplicatesResult;
$skipped = false;
}
if ($skipped) {
$recordDetails['skipped'][] = array('record' => $createdRecords[$index],
'messageidentifier' => '',
'message' => $e->getMessage());
}
unset($createdRecords[$index]);
continue;
} catch (Exception $e) {
$recordDetails['skipped'][] = array('record' => $createdRecords[$index],
'messageidentifier' => '',
'message' => $e->getMessage());
unset($createdRecords[$index]);
continue;
}
}
if (count($updatedRecords) > 0) {
$updatedRecords = $this->translateReferenceFieldNamesToIds($updatedRecords, $user);
}
$crmIds = array();
foreach ($updatedRecords as $index => $record) {
$webserviceRecordId = $record["id"];
$recordIdComp = vtws_getIdComponents($webserviceRecordId);
$crmIds[] = $recordIdComp[1];
}
$assignedRecordIds = array();
if ($this->isClientUserSyncType()|| $this->isClientUserAndGroupSyncType()) {
$assignedRecordIds = wsapp_checkIfRecordsAssignToUser($crmIds, $this->user->id);
// To check if the record assigned to group
if($this->isClientUserAndGroupSyncType()){
$groupIds = $this->getGroupIds($this->user->id);
foreach ($groupIds as $group) {
$groupRecordId = wsapp_checkIfRecordsAssignToUser($crmIds, $group);
$assignedRecordIds = array_merge($assignedRecordIds, $groupRecordId);
}
}
// End
}
foreach ($updatedRecords as $index => $record) {
$webserviceRecordId = $record["id"];
$recordIdComp = vtws_getIdComponents($webserviceRecordId);
try {
if (in_array($recordIdComp[1], $assignedRecordIds)) {
$updatedRecords[$index] = vtws_revise($record, $this->user);
} else if (!$this->isClientUserSyncType()) {
$updatedRecords[$index] = vtws_revise($record, $this->user);
} else {
$this->assignToChangedRecords[$index] = $record;
}
} catch (DuplicateException $e) {
$skipped = true;
$duplicateRecordIds = $e->getDuplicateRecordIds();
$duplicatesResult = $this->triggerSyncActionForDuplicate($record, $duplicateRecordIds);
if ($duplicatesResult) {
$updateDuplicateRecords[$index] = $duplicatesResult;
$skipped = false;
}
if ($skipped) {
$recordDetails['skipped'][] = array('record' => $updatedRecords[$index],
'messageidentifier' => '',
'message' => $e->getMessage());
}
unset($updatedRecords[$index]);
continue;
} catch (Exception $e) {
$recordDetails['skipped'][] = array('record' => $updatedRecords[$index],
'messageidentifier' => '',
'message' => $e->getMessage());
unset($updatedRecords[$index]);
continue;
}
}
foreach ($updateDuplicateRecords as $index => $record) {
$updatedRecords[$index] = $record;
}
$hasDeleteAccess = null;
$deletedCrmIds = array();
foreach ($deletedRecords as $index => $record) {
$webserviceRecordId = $record;
$recordIdComp = vtws_getIdComponents($webserviceRecordId);
$deletedCrmIds[] = $recordIdComp[1];
}
$assignedDeletedRecordIds = wsapp_checkIfRecordsAssignToUser($deletedCrmIds, $this->user->id);
// To get record id's assigned to group of the current user
if($this->isClientUserAndGroupSyncType()){
foreach ($groupIds as $group) {
$groupRecordId = wsapp_checkIfRecordsAssignToUser($deletedCrmIds, $group);
$assignedDeletedRecordIds = array_merge($assignedDeletedRecordIds, $groupRecordId);
}
}
// End
foreach ($deletedRecords as $index => $record) {
$idComp = vtws_getIdComponents($record);
if (empty($hasDeleteAccess)) {
$handler = vtws_getModuleHandlerFromId($idComp[0], $this->user);
$meta = $handler->getMeta();
$hasDeleteAccess = $meta->hasDeleteAccess();
}
if ($hasDeleteAccess) {
if (in_array($idComp[1], $assignedDeletedRecordIds)) {
try {
vtws_delete($record, $this->user);
} catch (Exception $e) {
$recordDetails['skipped'][] = array('record' => $deletedRecords[$index],
'messageidentifier' => '',
'message' => $e->getMessage());
unset($deletedRecords[$index]);
continue;
}
}
}
}
$recordDetails['created'] = $createdRecords;
$recordDetails['updated'] = $updatedRecords;
$recordDetails['deleted'] = $deletedRecords;
return $this->nativeToSyncFormat($recordDetails);
}
public function nativeToSyncFormat($element) {
return $element;
}
public function syncToNativeFormat($element) {
$syncCreatedRecords = $element['created'];
$nativeCreatedRecords = array();
foreach ($syncCreatedRecords as $index => $createRecord) {
if (empty($createRecord['assigned_user_id'])) {
$createRecord['assigned_user_id'] = vtws_getWebserviceEntityId("Users", $this->user->id);
}
$nativeCreatedRecords[$index] = $createRecord;
}
$element['created'] = $nativeCreatedRecords;
return $element;
}
public function map($element, $user) {
return $element;
}
public function translateReferenceFieldNamesToIds($entityRecords, $user) {
$entityRecordList = array();
foreach ($entityRecords as $index => $record) {
$entityRecordList[$record['module']][$index] = $record;
}
foreach ($entityRecordList as $module => $records) {
$handler = vtws_getModuleHandlerFromName($module, $user);
$meta = $handler->getMeta();
$referenceFieldDetails = $meta->getReferenceFieldDetails();
foreach ($referenceFieldDetails as $referenceFieldName => $referenceModuleDetails) {
$recordReferenceFieldNames = array();
foreach ($records as $index => $recordDetails) {
if (!empty($recordDetails[$referenceFieldName])) {
$recordReferenceFieldNames[] = $recordDetails[$referenceFieldName];
}
}
$entityNameIds = wsapp_getRecordEntityNameIds(array_values($recordReferenceFieldNames), $referenceModuleDetails, $user);
foreach ($records as $index => $recordInfo) {
if(array_key_exists($referenceFieldName, $recordInfo)){
$array = explode('x',$record[$referenceFieldName]);
if(is_numeric($array[0]) && is_numeric($array[1])){
$recordInfo[$referenceFieldName] = $recordInfo[$referenceFieldName];
}elseif (!empty($entityNameIds[strtolower($recordInfo[$referenceFieldName])])) {
$recordInfo[$referenceFieldName] = $entityNameIds[strtolower($recordInfo[$referenceFieldName])];
} else {
$recordInfo[$referenceFieldName] = "";
}
}
$records[$index] = $recordInfo;
}
}
$entityRecordList[$module] = $records;
}
$crmRecords = array();
foreach ($entityRecordList as $module => $entityRecords) {
foreach ($entityRecords as $index => $record) {
$crmRecords[$index] = $record;
}
}
return $crmRecords;
}
public function translateTheReferenceFieldIdsToName($records, $module, $user) {
$db = PearDatabase::getInstance();
global $current_user;
$current_user = $user;
$handler = vtws_getModuleHandlerFromName($module, $user);
$meta = $handler->getMeta();
$referenceFieldDetails = $meta->getReferenceFieldDetails();
foreach ($referenceFieldDetails as $referenceFieldName => $referenceModuleDetails) {
$referenceFieldIds = array();
$referenceModuleIds = array();
$referenceIdsName = array();
foreach ($records as $recordDetails) {
$referenceWsId = $recordDetails[$referenceFieldName];
if (!empty($referenceWsId)) {
$referenceIdComp = vtws_getIdComponents($referenceWsId);
$webserviceObject = VtigerWebserviceObject::fromId($db, $referenceIdComp[0]);
if ($webserviceObject->getEntityName() == 'Currency') {
continue;
}
$referenceModuleIds[$webserviceObject->getEntityName()][] = $referenceIdComp[1];
$referenceFieldIds[] = $referenceIdComp[1];
}
}
foreach ($referenceModuleIds as $referenceModule => $idLists) {
$nameList = getEntityName($referenceModule, $idLists);
foreach ($nameList as $key => $value)
$referenceIdsName[$key] = $value;
}
$recordCount = count($records);
for ($i = 0; $i < $recordCount; $i++) {
$record = $records[$i];
if (!empty($record[$referenceFieldName])) {
$wsId = vtws_getIdComponents($record[$referenceFieldName]);
$record[$referenceFieldName] = decode_html($referenceIdsName[$wsId[1]]);
}
$records[$i] = $record;
}
}
return $records;
}
public function getAssignToChangedRecords() {
return $this->assignToChangedRecords;
}
public function fillNonExistingMandatoryPicklistValues($recordList) {
//Meta is cached to eliminate overhead of doing the query every time to get the meta details(retrieveMeta)
$modulesMetaCache = array();
foreach ($recordList as $index => $recordDetails) {
if (!array_key_exists($recordDetails['module'], $modulesMetaCache)) {
$handler = vtws_getModuleHandlerFromName($recordDetails['module'], $this->user);
$meta = $handler->getMeta();
$modulesMetaCache[$recordDetails['module']] = $meta;
}
$moduleMeta = $modulesMetaCache[$recordDetails['module']];
$mandatoryFieldsList = $meta->getMandatoryFields();
$moduleFields = $meta->getModuleFields();
foreach ($mandatoryFieldsList as $fieldName) {
$fieldInstance = $moduleFields[$fieldName];
if (empty($recordDetails[$fieldName]) &&
($fieldInstance->getFieldDataType() == "multipicklist" || $fieldInstance->getFieldDataType() == "picklist")) {
if($fieldInstance->hasDefault() && trim($fieldInstance->getDefault())) {
$defaultValue = decode_html($fieldInstance->getDefault());
} else {
$pickListDetails = $fieldInstance->getPicklistDetails($webserviceField);
$defaultValue = $pickListDetails[0]['value'];
}
$recordDetails[$fieldName] = $defaultValue;
}
}
$recordList[$index] = $recordDetails;
}
return $recordList;
}
/**
* Function to fillMandatory fields in vtiger with given values
* @param type $recordLists
* @param type $user
* @return type
*/
public function fillMandatoryFields($recordLists, $user) {
$transformedRecords = array();
foreach ($recordLists as $index => $record) {
$handler = vtws_getModuleHandlerFromName($record['module'], $user);
$meta = $handler->getMeta();
$fields = $meta->getModuleFields();
$mandatoryFields = $meta->getMandatoryFields();
$ownerFields = $meta->getOwnerFields();
foreach ($mandatoryFields as $fieldName) {
// ignore owner fields
if (in_array($fieldName, $ownerFields)) {
continue;
}
$fieldInstance = $fields[$fieldName];
$currentFieldValue = $record[$fieldName];
if (!empty($currentFieldValue)) {
continue;
}
$fieldDataType = $fieldInstance->getFieldDataType();
$defaultValue = $fieldInstance->getDefault();
$value = '';
switch ($fieldDataType) {
case 'date':
$value = $defaultValue;
if (empty($defaultValue)) {
$dateObject = new DateTime();
$value = $dateObject->format('Y-m-d');
}
break;
case 'time' :
$value = '00:00:00';
if(!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'text':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'phone':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'boolean':
$value = false;
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'email':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'string':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'url':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'integer':
$value = 0;
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'double':
$value = 00.00;
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'currency':
$value = 0.00;
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
case 'skype':
$value = '?????';
if (!empty($defaultValue)) {
$value = $defaultValue;
}
break;
}
$record[$fieldName] = $value;
}
// New field added to show Record Source
if(!isset($record['source'])){
$record['source'] = Vtiger_Cache::get('WSAPP', 'appName');
}
$transformedRecords[$index] = $record;
}
return $transformedRecords;
}
public function setClientSyncType($syncType = 'user') {
$this->clientSyncType = $syncType;
return $this;
}
public function isClientUserSyncType() {
return ($this->clientSyncType == 'user') ? true : false;
}
public function isClientUserAndGroupSyncType() {
return ($this->clientSyncType == 'userandgroup') ? true : false;
}
public function triggerSyncActionForDuplicate($recordData, $duplicateRecordIds) {
$db = PearDatabase::getInstance();
$result = array();
$user = $this->user;
$moduleName = $recordData['module'];
$moduleModel = Vtiger_Module_Model::getInstance($moduleName);
if ($moduleModel && $moduleModel->isSyncable) {
$webSeviceModuleModel = VtigerWebserviceObject::fromName($db, $moduleName);
$moduleId = $webSeviceModuleModel->getEntityId();
$recordId = $recordData['id'];
$recordIdComponents = vtws_getIdComponents($recordId);
if (count($recordIdComponents) == 2 && in_array($moduleId, $recordIdComponents)) {
return array();
}
$elemId = reset($duplicateRecordIds);
$recordId = vtws_getId($moduleId, $elemId);
try {
$vtigerRecordData = vtws_retrieve($recordId, $user);
} catch (Exception $e) {
return $result;
}
global $skipDuplicateCheck;
$skipDuplicateCheck = true;
switch ($moduleModel->syncActionForDuplicate) {
case 1 : //Prefer latest record
$finalRecordData = $vtigerRecordData;
if ($recordData['modifiedtime'] > $vtigerRecordData['modifiedtime']) {
$finalRecordData = $recordData;
$finalRecordData['id'] = $recordId;
$finalRecordData = vtws_revise($finalRecordData, $user);
}
$result = $finalRecordData;
break;
// case 3 : //Prefer Vtiger Record
// $result = $vtigerRecordData;
// break;
case 4 : //Prefer external record
$recordData['id'] = $recordId;
foreach ($recordData as $fieldName => $fieldValue) {
if (!$fieldValue) {
unset($recordData[$fieldName]);
}
}
$result = vtws_revise($recordData, $user);
break;
case 2 : //Prefer internal record
default : $result = array();
break;
}
$skipDuplicateCheck = false;
}
return $result;
}
}
?>