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 = array();
$isRestoration = isset($options['IS_RESTORATION']) && $options['IS_RESTORATION'];
$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(!($isRestoration && isset($arFields['DATE_CREATE'])))
{
unset($arFields['DATE_CREATE']);
$arFields['~DATE_CREATE'] = $DB->CurrentTimeFunction();
}
if(!($isRestoration && isset($arFields['DATE_MODIFY'])))
{
unset($arFields['DATE_MODIFY']);
$arFields['~DATE_MODIFY'] = $DB->CurrentTimeFunction();
}
if(!($isRestoration && isset($arFields['MOVED_TIME'])))
{
unset($arFields['MOVED_TIME']);
}
if(!($isRestoration && isset($arFields['MOVED_BY_ID'])))
{
unset($arFields['MOVED_BY_ID']);
}
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['OPPORTUNITY']))
{
$arFields['OPPORTUNITY'] = 0.0;
}
if(!isset($arFields['TITLE']) || !is_string($arFields['TITLE']) || trim($arFields['TITLE']) === '')
{
$arFields['TITLE'] = self::GetDefaultTitle();
}
$fields = self::GetUserFields();
$this->fillEmptyFieldValues($arFields, $fields);
if(!$this->CheckFields($arFields, false, $options))
{
$arFields['RESULT_MESSAGE'] = &$this->LAST_ERROR;
return false;
}
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('');
}
if (!isset($arFields['MOVED_BY_ID']))
{
$arFields['MOVED_BY_ID'] = (int)$userID;
}
if (!isset($arFields['MOVED_TIME']))
{
$arFields['MOVED_TIME'] = (new \Bitrix\Main\Type\DateTime())->toString();
}
self::getLastActivityAdapter()->performAdd($arFields, $options);
self::getCommentsAdapter()->normalizeFields(null, $arFields);
$permissionTypeId = (
$this->bCheckPermission
? Bitrix\Crm\Security\EntityPermissionType::CREATE
: Bitrix\Crm\Security\EntityPermissionType::UNDEFINED
);
if(!isset($arFields['STATUS_ID']) || (string)$arFields['STATUS_ID'] === '')
{
$arFields['STATUS_ID'] = self::GetStartStatusID($permissionTypeId);
}
$viewMode = ($options['ITEM_OPTIONS']['VIEW_MODE'] ?? null);
$viewModeActivitiesStatusId = null;
if ($viewMode === ViewMode::MODE_ACTIVITIES)
{
$viewModeActivitiesStatusId = $arFields['STATUS_ID'];
$arFields['STATUS_ID'] = self::GetStartStatusID($permissionTypeId);
}
$isStatusExist = self::IsStatusExists($arFields['STATUS_ID']);
$arFields['STATUS_SEMANTIC_ID'] = $isStatusExist
? self::GetSemanticID($arFields['STATUS_ID'])
: Bitrix\Crm\PhaseSemantics::UNDEFINED
;
if (isset($arFields['DATE_CLOSED']))
unset($arFields['DATE_CLOSED']);
self::EnsureStatusesLoaded();
if (in_array($arFields['STATUS_ID'], self::$LEAD_STATUSES_BY_GROUP['FINISHED']))
$arFields['~DATE_CLOSED'] = $DB->CurrentTimeFunction();
$observerIDs = isset($arFields['OBSERVER_IDS']) && is_array($arFields['OBSERVER_IDS'])
? $arFields['OBSERVER_IDS'] : null;
unset($arFields['OBSERVER_IDS']);
$arAttr = array();
$arAttr['STATUS_ID'] = $arFields['STATUS_ID'];
if (!empty($arFields['OPENED']))
{
$arAttr['OPENED'] = $arFields['OPENED'];
}
if(!empty($observerIDs))
{
$arAttr['CONCERNED_USER_IDS'] = $observerIDs;
}
$sPermission = 'ADD';
if (isset($arFields['PERMISSION']))
{
if ($arFields['PERMISSION'] == 'IMPORT')
$sPermission = 'IMPORT';
unset($arFields['PERMISSION']);
}
$assignedByID = (int)$arFields['ASSIGNED_BY_ID'];
if($this->bCheckPermission)
{
$arEntityAttr = self::BuildEntityAttr($userID, $arAttr);
$userPerms = $userID == CCrmPerms::GetCurrentUserID() ? $this->cPerms : CCrmPerms::GetUserPermissions($userID);
$sEntityPerm = $userPerms->GetPermType('LEAD', $sPermission, $arEntityAttr);
if ($sEntityPerm == BX_CRM_PERM_NONE)
{
$this->LAST_ERROR = GetMessage('CRM_PERMISSION_DENIED');
$arFields['RESULT_MESSAGE'] = &$this->LAST_ERROR;
return false;
}
if ($sEntityPerm == BX_CRM_PERM_SELF && $assignedByID != $userID)
{
$arFields['ASSIGNED_BY_ID'] = $assignedByID = $userID;
}
if ($sEntityPerm == BX_CRM_PERM_OPEN && $userID == $assignedByID)
{
$arFields['OPENED'] = 'Y';
}
}
$assignedByID = (int)$arFields['ASSIGNED_BY_ID'];
$arEntityAttr = self::BuildEntityAttr($assignedByID, $arAttr);
$userPerms = $assignedByID == CCrmPerms::GetCurrentUserID() ? $this->cPerms : CCrmPerms::GetUserPermissions($assignedByID);
$sEntityPerm = $userPerms->GetPermType(self::$TYPE_NAME, $sPermission, $arEntityAttr);
$this->PrepareEntityAttrs($arEntityAttr, $sEntityPerm);
//Prepare currency & exchange rate
if(!isset($arFields['CURRENCY_ID']))
{
$arFields['CURRENCY_ID'] = CCrmCurrency::GetBaseCurrencyID();
}
if(!isset($arFields['EXCH_RATE']))
{
$arFields['EXCH_RATE'] = CCrmCurrency::GetExchangeRate($arFields['CURRENCY_ID']);
}
$arFields = array_merge($arFields, \CCrmAccountingHelper::calculateAccountingData($arFields));
if (isset($arFields['NAME']) || isset($arFields['LAST_NAME']))
{
$arFields['FULL_NAME'] = trim((isset($arFields['NAME'])? $arFields['NAME']: '').' '.(isset($arFields['LAST_NAME'])? $arFields['LAST_NAME']: ''));
}
//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 contacts
$contactBindings = isset($arFields['CONTACT_BINDINGS']) && is_array($arFields['CONTACT_BINDINGS'])
? $arFields['CONTACT_BINDINGS'] : null;
$contactIDs = isset($arFields['CONTACT_IDS']) && is_array($arFields['CONTACT_IDS'])
? $arFields['CONTACT_IDS'] : null;
unset($arFields['CONTACT_IDS']);
//For backward compatibility only
$contactID = isset($arFields['CONTACT_ID']) ? max((int)$arFields['CONTACT_ID'], 0) : null;
if($contactID !== null && $contactIDs === null && $contactBindings === null)
{
$contactIDs = array();
if($contactID > 0)
{
$contactIDs[] = $contactID;
}
}
unset($arFields['CONTACT_ID']);
if(is_array($contactIDs) && !is_array($contactBindings))
{
$contactBindings = EntityBinding::prepareEntityBindings(
\CCrmOwnerType::Contact,
$contactIDs
);
EntityBinding::markFirstAsPrimary($contactBindings);
}
elseif(is_array($contactBindings))
{
if(EntityBinding::findPrimaryBinding($contactBindings) === null)
{
EntityBinding::markFirstAsPrimary($contactBindings);
}
$contactIDs = EntityBinding::prepareEntityIDs(
CCrmOwnerType::Contact,
$contactBindings
);
}
//endregion
//region Synchronize CustomerType
$customerType = isset($arFields['IS_RETURN_CUSTOMER']) && $arFields['IS_RETURN_CUSTOMER'] === 'Y'
? CustomerType::RETURNING : CustomerType::GENERAL;
$effectiveCustomerType = CustomerType::GENERAL;
$companyID = isset($arFields['COMPANY_ID']) ? (int)$arFields['COMPANY_ID'] : 0;
if($companyID > 0)
{
$effectiveCustomerType = CustomerType::RETURNING;
}
elseif((is_array($contactIDs) && !empty($contactIDs))
|| !is_array($contactIDs) && !empty($originalContactIDs)
)
{
$effectiveCustomerType = CustomerType::RETURNING;
}
if($customerType !== $effectiveCustomerType)
{
$arFields['IS_RETURN_CUSTOMER'] = $effectiveCustomerType === CustomerType::RETURNING ? 'Y' : 'N';
}
//endregion
//region Rise BeforeAdd event
$beforeEvents = GetModuleEvents('crm', 'OnBeforeCrmLeadAdd');
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_LEAD_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, [], '', false, 'FILE: '.__FILE__.'
LINE: '.__LINE__);
//Append ID to TITLE if required
if($ID > 0 && $arFields['TITLE'] === self::GetDefaultTitle())
{
$arFields['TITLE'] = self::GetDefaultTitle($ID);
$sUpdate = $DB->PrepareUpdate('b_crm_lead', array('TITLE' => $arFields['TITLE']));
if($sUpdate <> '')
{
$DB->Query("UPDATE b_crm_lead SET {$sUpdate} WHERE ID = {$ID}", false, 'FILE: '.__FILE__.'
LINE: '.__LINE__);
};
}
if(defined('BX_COMP_MANAGED_CACHE'))
{
$GLOBALS['CACHE_MANAGER']->CleanDir('b_crm_lead');
}
$arFields['ID'] = $ID;
$arFields['DATE_CREATE'] = $arFields['DATE_MODIFY'] = ConvertTimeStamp(time() + CTimeZone::GetOffset(), 'FULL');
//region Save Observers
if(!empty($observerIDs))
{
Crm\Observer\ObserverManager::registerBulk($observerIDs, \CCrmOwnerType::Lead, $ID);
}
//endregion
$securityRegisterOptions = (new \Bitrix\Crm\Security\Controller\RegisterOptions())
->setEntityAttributes($arEntityAttr)
;
Crm\Security\Manager::getEntityController(CCrmOwnerType::Lead)
->register(self::$TYPE_NAME, $ID, $securityRegisterOptions)
;
$addressFields = 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
);
if(!\Bitrix\Crm\EntityAddress::isEmpty($addressFields) || $addressFields['LOC_ADDR'])
{
\Bitrix\Crm\EntityAddress::register(
CCrmOwnerType::Lead,
$ID,
EntityAddressType::Primary,
$addressFields
);
}
CCrmEntityHelper::NormalizeUserFields($arFields, self::$sUFEntityID, $GLOBALS['USER_FIELD_MANAGER'], array('IS_NEW' => true));
$GLOBALS['USER_FIELD_MANAGER']->Update(self::$sUFEntityID, $ID, $arFields);
//Statistics & History -->
Bitrix\Crm\Statistics\LeadSumStatisticEntry::register($ID, $arFields);
Bitrix\Crm\History\LeadStatusHistoryEntry::register($ID, $arFields, array('IS_NEW' => !$isRestoration));
if($arFields['STATUS_ID'] === 'CONVERTED')
{
Bitrix\Crm\Statistics\LeadConversionStatisticsEntry::register($ID, $arFields, array('IS_NEW' => !$isRestoration));
}
//Bitrix\Crm\Statistics\LeadProcessStatisticsEntry::register($ID, $arFields, array('IS_NEW' => true));
//<-- Statistics & History
if($isRestoration)
{
Bitrix\Crm\Timeline\LeadController::getInstance()->onRestore($ID, array('FIELDS' => $arFields));
}
else
{
Bitrix\Crm\Timeline\LeadController::getInstance()->onCreate($ID, array('FIELDS' => $arFields));
}
CCrmEntityHelper::registerAdditionalTimelineEvents([
'entityTypeId' => \CCrmOwnerType::Lead,
'entityId' => $ID,
'fieldsInfo' => static::GetFieldsInfo(),
'previousFields' => [],
'currentFields' => $arFields,
'previousStageSemantics' => Crm\PhaseSemantics::UNDEFINED,
'currentStageSemantics' => $arFields['STATUS_SEMANTIC_ID'] ?? Crm\PhaseSemantics::UNDEFINED,
'options' => $options,
'bindings' => [
'entityTypeId' => \CCrmOwnerType::Contact,
'previous' => [],
'current' => $contactBindings,
],
'isMarkEventRegistrationEnabled' => false,
]);
//region Duplicate communication data
if (isset($arFields['FM']) && is_array($arFields['FM']))
{
$CCrmFieldMulti = new CCrmFieldMulti();
$CCrmFieldMulti->SetFields('LEAD', $ID, $arFields['FM']);
}
//endregion
$duplicateCriterionRegistrar = DuplicateManager::getCriterionRegistrar(\CCrmOwnerType::Lead);
$data =
(new Crm\Integrity\CriterionRegistrar\Data())
->setEntityTypeId(\CCrmOwnerType::Lead)
->setEntityId($ID)
->setCurrentFields($arFields)
;
$duplicateCriterionRegistrar->register($data);
\Bitrix\Crm\Counter\Monitor::getInstance()->onEntityAdd(CCrmOwnerType::Lead, $arFields);
// tracking of entity
Tracking\Entity::onAfterAdd(CCrmOwnerType::Lead, $ID, $arFields);
//region Save contacts
if(!empty($contactBindings))
{
LeadContactTable::bindContacts($ID, $contactBindings);
if(isset($GLOBALS['USER']) && !empty($contactIDs))
{
CUserOptions::SetOption(
'crm',
'crm_contact_search',
array('last_selected' => $contactIDs[count($contactIDs) - 1])
);
}
}
//endregion
//region save parent relations
Crm\Service\Container::getInstance()->getParentFieldManager()->saveParentRelationsForIdentifier(
new Crm\ItemIdentifier(\CCrmOwnerType::Lead, $ID),
$arFields
);
//endregion
if($bUpdateSearch)
{
CCrmSearch::UpdateSearch(array('ID' => $ID, 'CHECK_PERMISSIONS' => 'N'), 'LEAD', true);
}
//region Search content index
Bitrix\Crm\Search\SearchContentBuilderFactory::create(
CCrmOwnerType::Lead
)->build($ID, ['checkExist' => true]);
//endregion
self::getCommentsAdapter()->performAdd($arFields, $options);
if(isset($options['REGISTER_SONET_EVENT']) && $options['REGISTER_SONET_EVENT'] === true)
{
$opportunity = round((isset($arFields['OPPORTUNITY']) ? doubleval($arFields['OPPORTUNITY']) : 0.0), 2);
$currencyID = isset($arFields['CURRENCY_ID']) ? $arFields['CURRENCY_ID'] : '';
if($currencyID === '')
{
$currencyID = CCrmCurrency::GetBaseCurrencyID();
}
$assignedByID = intval($arFields['ASSIGNED_BY_ID']);
$createdByID = intval($arFields['CREATED_BY_ID']);
$liveFeedFields = array(
'USER_ID' => $createdByID,
'ENTITY_TYPE_ID' => CCrmOwnerType::Lead,
'ENTITY_ID' => $ID,
//'EVENT_ID' => $eventID,
'TITLE' => GetMessage('CRM_LEAD_EVENT_ADD'),
'MESSAGE' => '',
'PARAMS' => array(
'TITLE' => $arFields['TITLE'],
'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'] : '',
'COMPANY_TITLE' => isset($arFields['COMPANY_TITLE']) ? $arFields['COMPANY_TITLE'] : '',
'STATUS_ID' => $arFields['STATUS_ID'],
'OPPORTUNITY' => strval($opportunity),
'CURRENCY_ID' => $currencyID,
'AUTHOR_ID' => intval($arFields['CREATED_BY_ID']),
'RESPONSIBLE_ID' => $assignedByID
)
);
if (Crm\Settings\Crm::isLiveFeedRecordsGenerationEnabled())
{
CCrmSonetSubscription::RegisterSubscription(
CCrmOwnerType::Lead,
$ID,
CCrmSonetSubscriptionType::Responsibility,
$assignedByID
);
}
$logEventID = CCrmLiveFeed::CreateLogEvent($liveFeedFields, CCrmLiveFeedEvent::Add, ['CURRENT_USER' => $userID]);
if (
$logEventID !== false
&& $assignedByID != $createdByID
&& CModule::IncludeModule("im")
&& \Bitrix\Crm\Settings\LeadSettings::isEnabled()
)
{
$url = CCrmOwnerType::GetEntityShowPath(CCrmOwnerType::Lead, $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" => "lead_add",
"NOTIFY_EVENT" => "changeAssignedBy",
"NOTIFY_TAG" => "CRM|LEAD|".$ID,
"NOTIFY_MESSAGE" => GetMessage("CRM_LEAD_RESPONSIBLE_IM_NOTIFY", Array("#title#" => "".htmlspecialcharsbx($arFields['TITLE'])."")),
"NOTIFY_MESSAGE_OUT" => GetMessage("CRM_LEAD_RESPONSIBLE_IM_NOTIFY", Array("#title#" => htmlspecialcharsbx($arFields['TITLE'])))." (".$serverName.$url.")"
);
CIMNotify::Add($arMessageFields);
}
}
//region Rise AfterAdd event
$afterEvents = GetModuleEvents('crm', 'OnAfterCrmLeadAdd');
while ($arEvent = $afterEvents->Fetch())
{
ExecuteModuleEventEx($arEvent, array(&$arFields));
}
//endregion
if(isset($arFields['ORIGIN_ID']) && $arFields['ORIGIN_ID'] !== '')
{
$afterEvents = GetModuleEvents('crm', 'OnAfterExternalCrmLeadAdd');
while ($arEvent = $afterEvents->Fetch())
{
ExecuteModuleEventEx($arEvent, array(&$arFields));
}
}
if ($ID>0)
{
if (
$viewMode === ViewMode::MODE_ACTIVITIES
&& $viewModeActivitiesStatusId
)
{
$deadline = (new Crm\Kanban\EntityActivityDeadline())->getDeadline($viewModeActivitiesStatusId);
if ($deadline)
{
\Bitrix\Crm\Activity\Entity\ToDo::createWithDefaultDescription(
\CCrmOwnerType::Lead,
$ID,
$deadline
);
}
}
$item = Crm\Kanban\Entity::getInstance(self::$TYPE_NAME)
->createPullItem($arFields);
PullManager::getInstance()->sendItemAddedEvent(
$item,
[
'TYPE' => self::$TYPE_NAME,
'SKIP_CURRENT_USER' => ($userID !== 0),
]
);
}
return $ID;
}