public function Add(array &$arFields, $bUpdateSearch = true, $options = array())
{
global $DB;
if(!is_array($options))
{
$options = array();
}
if ($this->isUseOperation())
{
return $this->getCompatibilityAdapter()->performAdd($arFields, $options);
}
$this->LAST_ERROR = '';
$this->checkExceptions = [];
$isRestoration = isset($options['IS_RESTORATION']) && $options['IS_RESTORATION'];
// ALLOW_SET_SYSTEM_FIELDS is deprecated temporary option. It will be removed soon! Do not use it!
$allowSetSystemFields = $options['ALLOW_SET_SYSTEM_FIELDS'] ?? $isRestoration;
$userID = isset($options['CURRENT_USER'])
? (int)$options['CURRENT_USER'] : CCrmSecurityHelper::GetCurrentUserID();
if($userID <= 0 && $this->bCheckPermission)
{
$arFields['RESULT_MESSAGE'] = $this->LAST_ERROR = GetMessage('CRM_PERMISSION_USER_NOT_DEFINED');
return false;
}
unset($arFields['ID']);
if(!($allowSetSystemFields && isset($arFields['DATE_CREATE'])))
{
unset($arFields['DATE_CREATE']);
$arFields['~DATE_CREATE'] = $DB->CurrentTimeFunction();
}
if(!($allowSetSystemFields && isset($arFields['DATE_MODIFY'])))
{
unset($arFields['DATE_MODIFY']);
$arFields['~DATE_MODIFY'] = $DB->CurrentTimeFunction();
}
if($userID > 0)
{
if(!(isset($arFields['CREATED_BY_ID']) && $arFields['CREATED_BY_ID'] > 0))
{
$arFields['CREATED_BY_ID'] = $userID;
}
if(!(isset($arFields['MODIFY_BY_ID']) && $arFields['MODIFY_BY_ID'] > 0))
{
$arFields['MODIFY_BY_ID'] = $userID;
}
if(!(isset($arFields['ASSIGNED_BY_ID']) && $arFields['ASSIGNED_BY_ID'] > 0))
{
$arFields['ASSIGNED_BY_ID'] = $userID;
}
}
if((!isset($arFields['LAST_NAME']) || trim($arFields['LAST_NAME']) === '')
&& (!isset($arFields['NAME']) || trim($arFields['NAME']) === ''))
{
$arFields['LAST_NAME'] = self::GetDefaultTitle();
}
$fields = self::GetUserFields();
$this->fillEmptyFieldValues($arFields, $fields);
$arFields['CATEGORY_ID'] = $arFields['CATEGORY_ID'] ?? 0;
if (!$this->CheckFields($arFields, false, $options))
{
$arFields['RESULT_MESSAGE'] = &$this->LAST_ERROR;
$result = false;
}
else
{
if(isset($arFields['BIRTHDATE']))
{
if($arFields['BIRTHDATE'] !== '')
{
$birthDate = $arFields['BIRTHDATE'];
$arFields['~BIRTHDATE'] = $DB->CharToDateFunction($birthDate, 'SHORT', false);
$arFields['BIRTHDAY_SORT'] = \Bitrix\Crm\BirthdayReminder::prepareSorting($birthDate);
}
else
{
$arFields['BIRTHDAY_SORT'] = \Bitrix\Crm\BirthdayReminder::prepareSorting('');
}
unset($arFields['BIRTHDATE']);
}
else
{
$arFields['BIRTHDAY_SORT'] = \Bitrix\Crm\BirthdayReminder::prepareSorting('');
}
$arAttr = array();
if (!empty($arFields['OPENED']))
$arAttr['OPENED'] = $arFields['OPENED'];
$permissionEntityType = (new PermissionEntityTypeHelper(CCrmOwnerType::Contact))
->getPermissionEntityTypeForCategory((int)$arFields['CATEGORY_ID'])
;
$sPermission = 'ADD';
if (isset($arFields['PERMISSION']))
{
if ($arFields['PERMISSION'] == 'IMPORT')
$sPermission = 'IMPORT';
unset($arFields['PERMISSION']);
}
if($this->bCheckPermission)
{
$arEntityAttr = self::BuildEntityAttr($userID, $arAttr);
$userPerms = $userID == CCrmPerms::GetCurrentUserID() ? $this->cPerms : CCrmPerms::GetUserPermissions($userID);
$sEntityPerm = $userPerms->GetPermType($permissionEntityType, $sPermission, $arEntityAttr);
if ($sEntityPerm == BX_CRM_PERM_NONE)
{
$this->LAST_ERROR = GetMessage('CRM_PERMISSION_DENIED');
$arFields['RESULT_MESSAGE'] = &$this->LAST_ERROR;
return false;
}
$assignedByID = intval($arFields['ASSIGNED_BY_ID']);
if ($sEntityPerm == BX_CRM_PERM_SELF && $assignedByID != $userID)
{
$arFields['ASSIGNED_BY_ID'] = $userID;
}
}
$assignedByID = intval($arFields['ASSIGNED_BY_ID']);
$arEntityAttr = self::BuildEntityAttr($assignedByID, $arAttr);
$userPerms = $assignedByID == CCrmPerms::GetCurrentUserID() ? $this->cPerms : CCrmPerms::GetUserPermissions($assignedByID);
$sEntityPerm = $userPerms->GetPermType($permissionEntityType, $sPermission, $arEntityAttr);
$this->PrepareEntityAttrs($arEntityAttr, $sEntityPerm);
if(isset($arFields['PHOTO'])
&& is_array($arFields['PHOTO'])
&& CFile::CheckImageFile($arFields['PHOTO']) == '')
{
$arFields['PHOTO']['MODULE_ID'] = 'crm';
CFile::SaveForDB($arFields, 'PHOTO', 'crm');
}
elseif (!empty($arFields['FACE_ID']) && Main\Loader::includeModule('faceid'))
{
// Set photo from FaceId module
$face = \Bitrix\Faceid\FaceTable::getRowById($arFields['FACE_ID']);
if (!empty($face))
{
$file = \CFile::MakeFileArray($face['FILE_ID']);
$io = \CBXVirtualIo::GetInstance();
$filePath = $io->GetPhysicalName($file['tmp_name']);
$binaryImageContent = $io->GetFile($filePath)->GetContents();
$arFields['PHOTO'] = array(
'name' => 'face_'.$arFields['FACE_ID'].'.jpg',
'type' => 'image/jpeg',
'content' => $binaryImageContent
);
$arFields['PHOTO']['MODULE_ID'] = 'crm';
CFile::SaveForDB($arFields, 'PHOTO', 'crm');
}
}
$arFields['FULL_NAME'] = self::GetFullName($arFields);
//region Setup HAS_EMAIL & HAS_PHONE & HAS_IMOL fields
$arFields['HAS_EMAIL'] = $arFields['HAS_PHONE'] = $arFields['HAS_IMOL'] = 'N';
if(isset($arFields['FM']) && is_array($arFields['FM']))
{
if(CCrmFieldMulti::HasValues($arFields['FM'], CCrmFieldMulti::EMAIL))
{
$arFields['HAS_EMAIL'] = 'Y';
}
if(CCrmFieldMulti::HasValues($arFields['FM'], CCrmFieldMulti::PHONE))
{
$arFields['HAS_PHONE'] = 'Y';
}
if(CCrmFieldMulti::HasImolValues($arFields['FM']))
{
$arFields['HAS_IMOL'] = 'Y';
}
}
//endregion
//region Preparation of companies
$companyBindings = isset($arFields['COMPANY_BINDINGS']) && is_array($arFields['COMPANY_BINDINGS'])
? $arFields['COMPANY_BINDINGS'] : null;
$companyIDs = isset($arFields['COMPANY_IDS']) && is_array($arFields['COMPANY_IDS'])
? $arFields['COMPANY_IDS'] : null;
unset($arFields['COMPANY_IDS']);
//For backward compatibility only
$companyID = isset($arFields['COMPANY_ID']) ? max((int)$arFields['COMPANY_ID'], 0) : null;
if($companyID !== null && $companyIDs === null)
{
$companyIDs = array();
if($companyID > 0)
{
$companyIDs[] = $companyID;
}
}
unset($arFields['COMPANY_ID']);
if(is_array($companyIDs) && !is_array($companyBindings))
{
$companyBindings = EntityBinding::prepareEntityBindings(
\CCrmOwnerType::Company,
$companyIDs
);
EntityBinding::markFirstAsPrimary($companyBindings);
}
/* Please uncomment if required
elseif(is_array($companyBindings) && !is_array($companyIDs))
{
$companyIDs = EntityBinding::prepareEntityIDs(
CCrmOwnerType::Company,
$companyBindings
);
}
*/
//endregion
self::getLastActivityAdapter()->performAdd($arFields, $options);
self::getCommentsAdapter()->normalizeFields(null, $arFields);
//region Rise BeforeAdd event
$beforeEvents = GetModuleEvents('crm', 'OnBeforeCrmContactAdd');
while ($arEvent = $beforeEvents->Fetch())
{
if(ExecuteModuleEventEx($arEvent, array(&$arFields)) === false)
{
if(isset($arFields['RESULT_MESSAGE']))
{
$this->LAST_ERROR = $arFields['RESULT_MESSAGE'];
}
else
{
$this->LAST_ERROR = GetMessage('CRM_CONTACT_CREATION_CANCELED', array('#NAME#' => $arEvent['TO_NAME']));
$arFields['RESULT_MESSAGE'] = &$this->LAST_ERROR;
}
return false;
}
}
//endregion
unset($arFields['ID']);
$this->normalizeEntityFields($arFields);
$ID = (int) $DB->Add(self::TABLE_NAME, $arFields, [], 'FILE: '.__FILE__.'
LINE: '.__LINE__);
//Append ID to LAST_NAME if required
if($ID > 0 && $arFields['LAST_NAME'] === self::GetDefaultTitle())
{
$arFields['LAST_NAME'] = self::GetDefaultTitle($ID);
$sUpdate = $DB->PrepareUpdate('b_crm_contact', array('LAST_NAME' => $arFields['LAST_NAME']));
if($sUpdate <> '')
{
$DB->Query(
"UPDATE b_crm_contact SET {$sUpdate} WHERE ID = {$ID}",
false,
'FILE: '.__FILE__.'
LINE: '.__LINE__
);
};
}
$result = $arFields['ID'] = $ID;
if(defined('BX_COMP_MANAGED_CACHE'))
{
$GLOBALS['CACHE_MANAGER']->CleanDir('b_crm_contact');
}
$securityRegisterOptions = (new \Bitrix\Crm\Security\Controller\RegisterOptions())
->setEntityAttributes($arEntityAttr)
;
Crm\Security\Manager::getEntityController(CCrmOwnerType::Contact)
->register($permissionEntityType, $ID, $securityRegisterOptions)
;
//Statistics & History -->
Bitrix\Crm\Statistics\ContactGrowthStatisticEntry::register($ID, $arFields);
//<-- Statistics & History
//region Save companies
if (is_array($companyBindings))
{
\Bitrix\Crm\Binding\ContactCompanyTable::bindCompanies($ID, $companyBindings);
}
if (isset($GLOBALS['USER']) && $companyID > 0)
{
CUserOptions::SetOption('crm', 'crm_company_search', array('last_selected' => $companyID));
}
//endregion
//region Statistics & History
if(isset($arFields['LEAD_ID']) && $arFields['LEAD_ID'] > 0)
{
Bitrix\Crm\Statistics\LeadConversionStatisticsEntry::processBindingsChange($arFields['LEAD_ID']);
}
//endregion
if($isRestoration)
{
Bitrix\Crm\Timeline\ContactController::getInstance()->onRestore($ID, array('FIELDS' => $arFields));
}
else
{
Bitrix\Crm\Timeline\ContactController::getInstance()->onCreate($ID, array('FIELDS' => $arFields));
}
CCrmEntityHelper::registerAdditionalTimelineEvents([
'entityTypeId' => \CCrmOwnerType::Contact,
'entityId' => $ID,
'fieldsInfo' => static::GetFieldsInfo(),
'previousFields' => [],
'currentFields' => $arFields,
'options' => $options,
'bindings' => [
'entityTypeId' => \CCrmOwnerType::Company,
'previous' => [],
'current' => $companyBindings,
]
]);
EntityAddress::register(
CCrmOwnerType::Contact,
$ID,
EntityAddressType::Primary,
array(
'ADDRESS_1' => isset($arFields['ADDRESS']) ? $arFields['ADDRESS'] : null,
'ADDRESS_2' => isset($arFields['ADDRESS_2']) ? $arFields['ADDRESS_2'] : null,
'CITY' => isset($arFields['ADDRESS_CITY']) ? $arFields['ADDRESS_CITY'] : null,
'POSTAL_CODE' => isset($arFields['ADDRESS_POSTAL_CODE']) ? $arFields['ADDRESS_POSTAL_CODE'] : null,
'REGION' => isset($arFields['ADDRESS_REGION']) ? $arFields['ADDRESS_REGION'] : null,
'PROVINCE' => isset($arFields['ADDRESS_PROVINCE']) ? $arFields['ADDRESS_PROVINCE'] : null,
'COUNTRY' => isset($arFields['ADDRESS_COUNTRY']) ? $arFields['ADDRESS_COUNTRY'] : null,
'COUNTRY_CODE' => isset($arFields['ADDRESS_COUNTRY_CODE']) ? $arFields['ADDRESS_COUNTRY_CODE'] : null,
'LOC_ADDR_ID' => isset($arFields['ADDRESS_LOC_ADDR_ID']) ? (int)$arFields['ADDRESS_LOC_ADDR_ID'] : 0,
'LOC_ADDR' => isset($arFields['ADDRESS_LOC_ADDR']) ? $arFields['ADDRESS_LOC_ADDR'] : null
)
);
CCrmEntityHelper::NormalizeUserFields($arFields, self::$sUFEntityID, $GLOBALS['USER_FIELD_MANAGER'], array('IS_NEW' => true));
$GLOBALS['USER_FIELD_MANAGER']->Update(self::$sUFEntityID, $ID, $arFields);
//region Duplicate communication data
if (isset($arFields['FM']) && is_array($arFields['FM']))
{
$CCrmFieldMulti = new CCrmFieldMulti();
$CCrmFieldMulti->SetFields('CONTACT', $ID, $arFields['FM']);
}
//endregion
$duplicateCriterionRegistrar = DuplicateManager::getCriterionRegistrar(\CCrmOwnerType::Contact);
$data =
(new Crm\Integrity\CriterionRegistrar\Data())
->setEntityTypeId(\CCrmOwnerType::Contact)
->setEntityId($ID)
->setCurrentFields($arFields)
;
$duplicateCriterionRegistrar->register($data);
\Bitrix\Crm\Counter\Monitor::getInstance()->onEntityAdd(CCrmOwnerType::Contact, $arFields);
// tracking of entity
Tracking\Entity::onAfterAdd(CCrmOwnerType::Contact, $ID, $arFields);
//region save parent relations
Crm\Service\Container::getInstance()->getParentFieldManager()->saveParentRelationsForIdentifier(
new Crm\ItemIdentifier(\CCrmOwnerType::Contact, $ID),
$arFields
);
//endregion
if($bUpdateSearch)
{
CCrmSearch::UpdateSearch(array('ID' => $ID, 'CHECK_PERMISSIONS' => 'N'), 'CONTACT', true);
}
//region Search content index
Bitrix\Crm\Search\SearchContentBuilderFactory::create(
CCrmOwnerType::Contact
)->build($ID, ['checkExist' => true]);
//endregion
self::getCommentsAdapter()->performAdd($arFields, $options);
if(isset($options['REGISTER_SONET_EVENT']) && $options['REGISTER_SONET_EVENT'] === true)
{
$multiFields = isset($arFields['FM']) ? $arFields['FM'] : null;
$phones = CCrmFieldMulti::ExtractValues($multiFields, 'PHONE');
$emails = CCrmFieldMulti::ExtractValues($multiFields, 'EMAIL');
$assignedByID = intval($arFields['ASSIGNED_BY_ID']);
$createdByID = intval($arFields['CREATED_BY_ID']);
$liveFeedFields = array(
'USER_ID' => $createdByID,
'ENTITY_TYPE_ID' => CCrmOwnerType::Contact,
'ENTITY_ID' => $ID,
'TITLE' => GetMessage('CRM_CONTACT_EVENT_ADD'),
'MESSAGE' => '',
'PARAMS' => array(
'NAME' => isset($arFields['NAME']) ? $arFields['NAME'] : '',
'SECOND_NAME' => isset($arFields['SECOND_NAME']) ? $arFields['SECOND_NAME'] : '',
'LAST_NAME' => isset($arFields['LAST_NAME']) ? $arFields['LAST_NAME'] : '',
'HONORIFIC' => isset($arFields['HONORIFIC']) ? $arFields['HONORIFIC'] : '',
'PHOTO_ID' => isset($arFields['PHOTO']) ? $arFields['PHOTO'] : '',
'COMPANY_ID' => isset($arFields['COMPANY_ID']) ? $arFields['COMPANY_ID'] : '',
'PHONES' => $phones,
'EMAILS' => $emails,
'AUTHOR_ID' => intval($arFields['CREATED_BY_ID']),
'RESPONSIBLE_ID' => $assignedByID
)
);
//region Register company relation
if(is_array($companyBindings))
{
$parents = array();
CCrmLiveFeed::PrepareOwnershipRelations(
CCrmOwnerType::Company,
EntityBinding::prepareEntityIDs(CCrmOwnerType::Company, $companyBindings),
$parents
);
if(!empty($parents))
{
$liveFeedFields['PARENTS'] = array_values($parents);
}
}
//endregion
$isUntypedCategory = (int)$arFields['CATEGORY_ID'] === 0;
if ($isUntypedCategory && Crm\Settings\Crm::isLiveFeedRecordsGenerationEnabled())
{
CCrmSonetSubscription::RegisterSubscription(
CCrmOwnerType::Contact,
$ID,
CCrmSonetSubscriptionType::Responsibility,
$assignedByID
);
}
$logEventID = $isUntypedCategory
? CCrmLiveFeed::CreateLogEvent($liveFeedFields, CCrmLiveFeedEvent::Add, ['CURRENT_USER' => $userID])
: false;
if (
$logEventID !== false
&& $assignedByID != $createdByID
&& $isUntypedCategory
&& CModule::IncludeModule("im")
)
{
$url = CCrmOwnerType::GetEntityShowPath(CCrmOwnerType::Contact, $ID);
$serverName = (CMain::IsHTTPS() ? "https" : "http")."://".((defined("SITE_SERVER_NAME") && SITE_SERVER_NAME <> '') ? SITE_SERVER_NAME : COption::GetOptionString("main", "server_name", ""));
$arMessageFields = array(
"MESSAGE_TYPE" => IM_MESSAGE_SYSTEM,
"TO_USER_ID" => $assignedByID,
"FROM_USER_ID" => $createdByID,
"NOTIFY_TYPE" => IM_NOTIFY_FROM,
"NOTIFY_MODULE" => "crm",
"LOG_ID" => $logEventID,
//"NOTIFY_EVENT" => "contact_add",
"NOTIFY_EVENT" => "changeAssignedBy",
"NOTIFY_TAG" => "CRM|CONTACT_RESPONSIBLE|".$ID,
"NOTIFY_MESSAGE" => GetMessage("CRM_CONTACT_RESPONSIBLE_IM_NOTIFY", Array("#title#" => "".htmlspecialcharsbx($arFields['FULL_NAME'])."")),
"NOTIFY_MESSAGE_OUT" => GetMessage("CRM_CONTACT_RESPONSIBLE_IM_NOTIFY", Array("#title#" => htmlspecialcharsbx($arFields['FULL_NAME'])))." (".$serverName.$url.")"
);
CIMNotify::Add($arMessageFields);
}
}
//region Rise AfterAdd event
$afterEvents = GetModuleEvents('crm', 'OnAfterCrmContactAdd');
while ($arEvent = $afterEvents->Fetch())
{
ExecuteModuleEventEx($arEvent, array(&$arFields));
}
//endregion
if(isset($arFields['ORIGIN_ID']) && $arFields['ORIGIN_ID'] !== '')
{
$afterEvents = GetModuleEvents('crm', 'OnAfterExternalCrmContactAdd');
while ($arEvent = $afterEvents->Fetch())
{
ExecuteModuleEventEx($arEvent, array(&$arFields));
}
}
}
if ($result)
{
$item = $this->createPullItem($arFields);
Crm\Integration\PullManager::getInstance()->sendItemAddedEvent(
$item,
[
'TYPE' => self::$TYPE_NAME,
'SKIP_CURRENT_USER' => ($userID !== 0),
'CATEGORY_ID' => ($arFields['CATEGORY_ID'] ?? 0),
]
);
}
return $result;
}