Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/sender/lib/integration/crm/connectors/ |
Upload File : |
<?php /** * Bitrix Framework * @package bitrix * @subpackage sender * @copyright 2001-2012 Bitrix */ namespace Bitrix\Sender\Integration\Crm\Connectors; use Bitrix\Crm\Service\Container; use Bitrix\Main\Application; use Bitrix\Main\Config\Option; use Bitrix\Main\DB\SqlExpression; use Bitrix\Main\Entity; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Orm; use Bitrix\Main\UI\Filter\AdditionalDateType; use Bitrix\Main\UI\Filter\Type as UiFilterType; use Bitrix\Sender\Connector; use Bitrix\Sender\Integration; use Bitrix\Sender\Integration\Crm\Connectors\Personalize\BasePersonalize; use Bitrix\Sender\Integration\Crm\Connectors\Personalize\FactoryBased; use Bitrix\Sender\Recipient; Loc::loadMessages(__FILE__); /** * Class Helper * @package Bitrix\Sender\Integration\Crm\Connectors */ class Helper { public static $runtimeByEntity = []; private const PERSONALIZE_NAMESPACE = "\\Bitrix\\Sender\\Integration\\Crm\\Connectors\\Personalize\\"; /** * Create Orm expression field for selecting multi field. * * @param string $entityName Entity name. * @param string $multiFieldTypeId Multi-field type ID. * @return Orm\Fields\ExpressionField */ public static function createExpressionMultiField($entityName, $multiFieldTypeId) { $sqlHelper = Application::getConnection()->getSqlHelper(); return new Orm\Fields\ExpressionField( $multiFieldTypeId, '(' . $sqlHelper->getTopSql( " SELECT FM.VALUE FROM b_crm_field_multi FM WHERE FM.ENTITY_ID = '$entityName' AND FM.ELEMENT_ID = %s AND FM.TYPE_ID = '$multiFieldTypeId' ORDER BY CASE FM.VALUE_TYPE WHEN 'MAILING' THEN 0 WHEN 'HOME' THEN 1 WHEN 'MOBILE' THEN 1 ELSE 2 END, FM.ID ", 1 ) . ')', 'ID' ); } /** * Get personalize field list. * * @return array */ public static function getPersonalizeList() { return array( array('CODE' => 'CRM_ENTITY_TYPE'), array('CODE' => 'CRM_ENTITY_TYPE_ID'), array('CODE' => 'CRM_ENTITY_ID'), ); } public static function buildPersonalizeList($entityType) { $documentClass = self::PERSONALIZE_NAMESPACE.ucfirst($entityType); if(!is_subclass_of($documentClass, BasePersonalize::class)) { return []; } $fields = $documentClass::isFactoryBased($entityType) ? FactoryBased::getEntityFields($entityType) : $documentClass::getEntityFields($entityType); if(!$fields) { return []; } return static::preparePersonalizeList($entityType, $fields); } public static function preparePersonalizeList(string $entityType, array $fields): array { $result = []; $items = []; $counter = 0; foreach ($fields as $fieldCode => $field) { $items[$counter++] = [ 'CODE' => $entityType.'.'.$fieldCode, 'NAME' => $field['Name'], 'DESC' => $field['Name'], ]; if(isset($field['personalizeCode']) && $field['personalizeCode']) { $items[$counter - 1]['CODE'] = implode( "##", Integration\Crm\Connectors\Personalize\BasePersonalize::getMap()[$field['personalizeCode']] ); } } $name = Loc::getMessage('SENDER_INTEGRATION_CRM_CONNECTOR_'.$entityType); if (Loader::includeModule('crm')) { $name = \CCrmOwnerType::GetDescription(\CCrmOwnerType::ResolveID($entityType)); } $result[] = [ 'CODE' => $entityType, 'NAME' => $name, 'ITEMS' => $items ]; return $result; } public static function getData($entityType, $entityIds, $fields = ['*']) { $documentClass = self::PERSONALIZE_NAMESPACE.ucfirst($entityType); if(!is_subclass_of($documentClass, BasePersonalize::class)) { return []; } $fields = self::filterOnlyAllowedFields((string)$entityType, (array)$fields); if (empty($fields)) { return []; } return $documentClass::isFactoryBased($entityType) ? FactoryBased::getData($entityType, $entityIds, $fields) : $documentClass::getData($entityType, $entityIds, $fields) ; } public static function getPersonalizeFieldsFromConnectors($isTrigger = false) { $connectors = Connector\Manager::getConnectorList(); $items = []; foreach ($connectors as $connector) { if(!($connector instanceof Connector\BaseFilter)) { continue; } if(method_exists($connector, 'getPersonalizeList')) { $list = $connector->getPersonalizeList(); if($isTrigger) { foreach ($list as $key => $data) { if(isset($data['ITEMS']) && in_array($data['CODE'], ['LEAD', 'CONTACT', 'COMPANY'])) { unset($list[$key]); } } } if(!empty($list)) { $items = array_merge($items, $list); } } } return $items; } /** * Get filter user fields. * * @param integer $entityTypeId Entity type ID. * @param bool $checkAccessRights * * @return array */ public static function getFilterUserFields(int $entityTypeId, bool $checkAccessRights = true): array { $list = array(); $ufManager = is_object($GLOBALS['USER_FIELD_MANAGER']) ? $GLOBALS['USER_FIELD_MANAGER'] : null; if (!$ufManager) { return $list; } $ufEntityId = \CCrmOwnerType::resolveUserFieldEntityID($entityTypeId); $crmUserType = new \CCrmUserType($ufManager, $ufEntityId); $logicFilter = array(); $fieldsParams = []; if (!$checkAccessRights) { $fieldsParams = ['skipUserFieldVisibilityCheck' => true]; } $crmUserType->prepareListFilterFields($list, $logicFilter, $fieldsParams); $originalList = $crmUserType->getFields($fieldsParams); $restrictedTypes = ['address', 'file', 'crm', 'resourcebooking']; $list = array_filter( $list, function ($field) use ($originalList, $restrictedTypes) { if (empty($originalList[$field['id']])) { return false; } $type = $originalList[$field['id']]['USER_TYPE']['USER_TYPE_ID']; return !in_array($type, $restrictedTypes); } ); foreach ($list as $index => $field) { if ($field['type'] === 'date') { $list[$index]['include'] = [ AdditionalDateType::CUSTOM_DATE, AdditionalDateType::PREV_DAY, AdditionalDateType::NEXT_DAY, AdditionalDateType::MORE_THAN_DAYS_AGO, AdditionalDateType::AFTER_DAYS, ]; if (!isset($list[$index]['allow_years_switcher'])) { $list[$index]['allow_years_switcher'] = true; } } if ($originalList[$field['id']]['MULTIPLE'] == 'Y') { $list[$index]['multiple_uf'] = true; } } return $list; } /** * Prepare query select. * * @param Entity\Query $query Query. * @param integer $dataTypeId Data type ID. * @return Entity\Query */ public static function prepareQuery( Entity\Query $query, $dataTypeId = null, $entityDbName = null, $entityName = null ) { $map = array( Recipient\Type::EMAIL => [ 'name' => 'HAS_EMAIL', 'operator' => '=', 'value' => 'Y' ], Recipient\Type::PHONE => [ 'name' => 'HAS_PHONE', 'operator' => '=', 'value' => 'Y' ], Recipient\Type::IM => [ 'name' => 'HAS_IMOL', 'operator' => '=', 'value' => 'Y' ], Recipient\Type::CRM_CONTACT_ID => [ 'name' => 'CONTACT_ID', 'operator' => null, 'value' => null ], Recipient\Type::CRM_COMPANY_ID => [ 'name' => 'COMPANY_ID', 'operator' => null, 'value' => null ], ); $entityName = $entityName ?? mb_strtoupper($query->getEntity()->getName()); if ($dataTypeId) { if (!isset($map[$dataTypeId])) { return $query; } $field = $map[$dataTypeId]; if($field['operator'] === null) { return $query; } if ($dataTypeId == Recipient\Type::CRM_COMPANY_ID && in_array($entityName, ['CONTACT'])) { $field['name'] = 'CRM_COMPANY_ID'; } $query->where($field['name'], $field['operator'], $field['value']); if ($dataTypeId === Recipient\Type::IM) { $query->whereExists(self::getImSqlExpression($query, $entityDbName, $entityName)); } } else if (!in_array($entityName, ['CONTACT', 'COMPANY'])) { $filter = Entity\Query::filter(); foreach ($map as $dataTypeId => $field) { if($field['operator'] === null) { continue; } if ($dataTypeId === Recipient\Type::IM) { $filter->where( Entity\Query::filter() ->where($field['name'], $field['operator'], $field['value']) ->whereExists(self::getImSqlExpression($query, $entityDbName, $entityName)) ); } else { $filter->where($field['name'], $field['operator'], $field['value']); } } if (count($filter->getConditions()) > 0) { $filter->logic('or'); $query->where($filter); } } return $query; } protected static function getImSqlExpression( Entity\Query $query, $entityDbName = null, $entityName = null ) { $codes = Integration\Im\Service::getExcludedChannelCodes(); if (empty($codes)) { $codes = array('livechat', 'network'); } $column = $entityDbName ? 'CRM_ENTITY_ID' : 'ID'; $entityTypeName = $entityName ?? mb_strtoupper($query->getEntity()->getName()); $sqlHelper = Application::getConnection()->getSqlHelper(); $regexp = "'^imol\\\\|(" . implode('|', $codes) . ")'"; $filterImolSql = "SELECT FM.VALUE " . "FROM b_crm_field_multi FM " . "WHERE FM.ENTITY_ID = '$entityTypeName' AND FM.ELEMENT_ID = ?#.{$column} " . "AND FM.TYPE_ID = 'IM' " . "AND NOT {$sqlHelper->getRegexpOperator('FM.VALUE', $regexp)} " . "ORDER BY FM.ID LIMIT 1"; return new SqlExpression($filterImolSql, $query->getInitAlias()); } /** * Get runtime by entity. * * @param string $entityTypeName Entity type name. * @return Entity\ExpressionField[] */ public static function getRuntimeByEntity($entityTypeName = '') { if (isset(self::$runtimeByEntity[$entityTypeName])) { return self::$runtimeByEntity[$entityTypeName]; } return []; } protected static function processRuntimeFilter(array &$filter, $entityTypeName = '') { foreach ($filter as $key => $item) { if (!($item instanceof Connector\Filter\RuntimeFilter)) { continue; } unset($filter[$key]); $filter[$item->getKey()] = $item->getValue(); if (empty(self::$runtimeByEntity[$entityTypeName])) { self::$runtimeByEntity[$entityTypeName] = []; } self::$runtimeByEntity[$entityTypeName] = array_merge( self::$runtimeByEntity[$entityTypeName], array_map( function ($item) use ($entityTypeName) { $search = $entityTypeName ? $entityTypeName . '_' : ''; $runtimeName = $entityTypeName ? $entityTypeName . '.' : ''; $item['expression'] = str_replace( $search, $runtimeName, $item['expression'] ); $item['buildFrom'] = array_map( function ($from) use ($search, $runtimeName) { return str_replace($search, $runtimeName, $from); }, $item['buildFrom'] ); return $item; }, $item->getRuntime() ) ); } } /** * Get filter by entity. * * @param array $fields Fields. * @param array $values Values. * @param array $entityTypeNames Entity type names. * @return array */ public static function getFilterByEntity(array $fields = array(), array $values = array(), array $entityTypeNames = array()) { $map = array(); foreach ($entityTypeNames as $entityTypeName) { $map[$entityTypeName] = array($entityTypeName); } $map['CLIENT'] = array(\CCrmOwnerType::CompanyName, \CCrmOwnerType::ContactName); $map[\CCrmOwnerType::CompanyName] = array(\CCrmOwnerType::CompanyName); $map[\CCrmOwnerType::ContactName] = array(\CCrmOwnerType::ContactName); $result = array(); foreach ($fields as $field) { if (!self::isFieldFilterable($field, $values)) { continue; } $id = $field['id']; foreach ($map as $prefix => $entityTypes) { $search = $prefix . '_'; if (mb_strpos($id, $search) !== 0) { continue; } foreach ($entityTypes as $entityTypeName) { $filterKey = "$entityTypeName.".mb_substr($id, mb_strlen($search)); if (!self::isFieldTypeFilter($field['type'])) { $filterKey = "=$filterKey"; } if ($field['multiple_uf'] ?? false) { $filterKey .= "_SINGLE"; } $field['sender_segment_filter'] = $filterKey; if (!isset($result[$entityTypeName])) { $result[$entityTypeName] = array(); } $result[$entityTypeName][] = $field; } break; } } self::$runtimeByEntity = []; foreach ($result as $entityTypeName => $fields) { $items = self::getFilterByFields($fields, $values, $entityTypeName); $result[$entityTypeName] = $items; } return $result; } private static function isFieldFilterable(array $field = array(), array $values = array()) { $id = $field['id']; $codeKey = 'sender_segment_filter'; if (isset($field[$codeKey]) && $field[$codeKey] === false) { return false; } if (!isset($values[$id]) || (!$values[$id] && !is_numeric($values[$id]))) { return false; } return true; } /** * Get filter by fields. * * @param array $fields Fields. * @param array $values Values. * @param string $entityTypeName Entity type name. * @return array */ public static function getFilterByFields(array $fields = array(), array $values = array(), $entityTypeName = '') { if ($entityTypeName) { if (!empty(self::$runtimeByEntity[$entityTypeName])) { self::$runtimeByEntity[$entityTypeName] = []; } } else { self::$runtimeByEntity = []; } $filter = array(); foreach ($fields as $field) { if (!self::isFieldFilterable($field, $values)) { continue; } $isMultiple = false; if (isset($field['params']) && is_array($field['params'])) { if (isset($field['params']['multiple']) && $field['params']['multiple']) { $isMultiple = true; } } $id = $field['id']; $value = $values[$id]; $value = $isMultiple && !is_array($value) ? array($value) : ($value === "" ? null:$value); if($isMultiple) { foreach($value as &$val) { if($val === "") { $val = null; } } } $field['value'] = $value; if ($field['filter_callback'] ?? false) { $extraCallbackParams = [ 'FIELD' => $field, 'ENTITY_TYPE_NAME' => $entityTypeName ]; call_user_func_array($field['filter_callback'], [$value, &$filter, $extraCallbackParams]); continue; } $filterKey = self::getFilterFieldKey($field); if (is_array($filterKey)) { foreach ($filterKey as $fieldValue => $fieldFilter) { if ($value !== $fieldValue) { continue; } $filter[$fieldFilter[0]] = $fieldFilter[1]; } } elseif (self::isFieldTypeFilter($field['type'])) { self::setFieldTypeFilter($filterKey, $field, $filter); self::processRuntimeFilter($filter, $entityTypeName); } else { $filter[$filterKey] = $value; } } return $filter; } protected static function getFilterFieldKey(array $field) { $codeKey = 'sender_segment_filter'; $id = $field['id']; if (isset($field[$codeKey]) && $field[$codeKey]) { return $field[$codeKey]; } if (self::isFieldTypeFilter($field['type'])) { return "$id"; } return "=$id"; } protected static function isFieldTypeFilter($type) { $types = array( UiFilterType::DATE, UiFilterType::NUMBER, UiFilterType::DEST_SELECTOR ); return in_array(mb_strtoupper($type), $types); } protected static function setFieldTypeFilter($filterKey, array $fieldData, &$filter) { $fieldData['filter-key'] = $filterKey; switch(mb_strtoupper($fieldData['type'])) { case UiFilterType::DATE: Connector\Filter\DateField::create($fieldData)->applyFilter($filter); break; case UiFilterType::NUMBER: Connector\Filter\NumberField::create($fieldData)->applyFilter($filter); break; case UiFilterType::DEST_SELECTOR: Connector\Filter\DestSelectorField::create($fieldData)->applyFilter($filter); break; } } protected static function getCommunicationTypeFilter(array $commTypes, &$filter, $extraCallbackParams = []) { if (in_array(\CCrmFieldMulti::PHONE, $commTypes)) { $filter['=HAS_PHONE'] = 'Y'; } if (in_array(\CCrmFieldMulti::EMAIL, $commTypes)) { $filter['=HAS_EMAIL'] = 'Y'; } if (in_array(\CCrmFieldMulti::IM, $commTypes)) { $filter['=HAS_IMOL'] = 'Y'; } } protected static function getIdFilter($value, &$filter) { if (is_array($value)) { $value = $value[0]; } $filter['@CRM_ENTITY_ID'] = array_map('trim', explode(",", $value)); } protected static function getNoPurchasesFilter($value, &$filter, $extraCallbackParams = []) { $entityTypeName = $extraCallbackParams['ENTITY_TYPE_NAME']; $field = $extraCallbackParams['FIELD']; if (!$entityTypeName) return; if ($value[$field['id'] . '_datesel'] != 'NONE') { $filter['NO_PURCHASES'] = []; self::setFieldTypeFilter('%PURCHASE_DATE%', $field, $filter['NO_PURCHASES']); self::processRuntimeFilter($filter['NO_PURCHASES'], $entityTypeName); } } protected static function productSourceFilter($value, &$filter, $extraCallbackParams = []) { if ($filter['NO_PURCHASES'] || $filter['=COMPANY.PRODUCT_ID'] || $filter['=CONTACT.PRODUCT_ID']) { if (!is_array($value) || in_array("", $value)) // if PRODUCT_SOURCE wasn't set or has "everywhere" value { $value = []; } $filter['PRODUCT_SOURCE'] = $value; } } protected static function getDealCategoryFilter($values, &$filter, $extraCallbackParams = []) { $filter['DEAL'] = []; foreach ($values as $k => $value) { if(is_null($value)) { $filter['DEAL'][] = ['SGT_DEAL.ID', '=', $value]; $filter['DEAL']['JOIN_TYPE'] = 'LEFT'; unset($values[$k]); } } if(!empty($values)) { $filter['DEAL'][] = ['SGT_DEAL.CATEGORY_ID', 'in', $values]; } } /** * Callback on draw of result view. * * @param array &$row Row. * @return void */ public function onResultViewDraw(array &$row) { switch ($row['CRM_ENTITY_TYPE_ID']) { case \CCrmOwnerType::Company: $crmRow = \Bitrix\Crm\CompanyTable::getRowById($row['CRM_ENTITY_ID']); $row['~NAME'] = self::getResultViewTitle( $row['CRM_ENTITY_TYPE_ID'], $row['CRM_ENTITY_ID'], $row['NAME'], \CCrmOwnerType::GetDescription($row['CRM_ENTITY_TYPE_ID']), self::getCrmStatusName('COMPANY_TYPE', $crmRow['COMPANY_TYPE']) ); break; case \CCrmOwnerType::Contact: $crmRow = \Bitrix\Crm\ContactTable::getRowById($row['CRM_ENTITY_ID']); $row['~NAME'] = self::getResultViewTitle( $row['CRM_ENTITY_TYPE_ID'], $row['CRM_ENTITY_ID'], $row['NAME'], \CCrmOwnerType::GetDescription($row['CRM_ENTITY_TYPE_ID']), self::getCrmStatusName('SOURCE', $crmRow['SOURCE_ID']) ); break; case \CCrmOwnerType::Lead: $crmRow = \Bitrix\Crm\LeadTable::getRowById($row['CRM_ENTITY_ID']); $row['CRM_LEAD'] = $row['~CRM_LEAD'] = self::getResultViewTitle( $row['CRM_ENTITY_TYPE_ID'], $row['CRM_ENTITY_ID'], $crmRow['TITLE'], self::getCrmStatusName('SOURCE', $crmRow['SOURCE_ID']), $crmRow['IS_RETURN_CUSTOMER'] === 'Y' ? Loc::getMessage('SENDER_INTEGRATION_CRM_CONNECTOR_LEAD_FIELD_RC_LEAD') : null ); break; } } protected function getCrmStatusName($statusType, $statusId) { if (!$statusId) { return null; } $sources = \CCrmStatus::GetStatus($statusType); if (empty($sources[$statusId])) { return $statusId; } return $sources[$statusId]['NAME']; } protected function getResultViewTitle($entityTypeId, $entityId, $title, $secondTitle = null, $thirdTitle = null) { $url = self::getPathToDetail($entityTypeId, $entityId); $title = htmlspecialcharsbx($title); if ($url && \CCrmAuthorizationHelper::checkReadPermission($entityTypeId, $entityId)) { $title = '<a href="' . $url . '">' . $title . '</a>'; } if ($secondTitle) { $title .= "<br><span style=\"color:grey; font-size: 12px;\">"; $title .= htmlspecialcharsbx($secondTitle); $title .= "</span>"; } if ($thirdTitle) { $title .= "<br><span style=\"color:grey; font-size: 12px;\">"; $title .= htmlspecialcharsbx($thirdTitle); $title .= "</span>"; } return $title; } protected function getPathToDetail($entityTypeId, $entityId) { if (Loader::includeModule('crm')) { return Container::getInstance()->getRouter()->getItemDetailUrl($entityTypeId, $entityId); } switch ($entityTypeId) { case \CCrmOwnerType::Company: $optionName = 'path_to_company_details'; break; case \CCrmOwnerType::Contact: $optionName = 'path_to_contact_details'; break; case \CCrmOwnerType::Lead: $optionName = 'path_to_lead_details'; break; default: $optionName = null; } $url = $optionName ? Option::get('crm', $optionName) : null; if ($url) { $url = str_replace( ['#company_id#', '#contact_id#', '#lead_id#'], $entityId, $url ); } return $url; } public static function isCrmSaleEnabled() { return Loader::includeModule("sale") && (Option::get("crm", "crm_shop_enabled", "N") != 'N'); } /** * @param string $entityType * @param array<string> $fields * * @return array<string> */ private static function filterOnlyAllowedFields(string $entityType, array $fields): array { $allowedFields = self::getPersonalizeFieldsFromConnectors(); return array_filter($fields, static function (string $field) use ($allowedFields, $entityType): bool { if ($field === '*') { return true; } $entityField = "$entityType.$field"; foreach ($allowedFields as $allowedField) { if (($allowedField['CODE'] ?? null) !== $entityType) { continue; } $items = (array)($allowedField['ITEMS'] ?? []); foreach ($items as $item) { if (($item['CODE'] ?? null) === $entityField) { return true; } } } return false; }); } }