• Модуль: crm
  • Путь к файлу: ~/bitrix/modules/crm/lib/agent/duplicate/automatic/rebuilduserduplicateindexagent.php
  • Класс: Bitrix\Crm\Agent\Duplicate\Automatic\RebuildUserDuplicateIndexAgent
  • Вызов: RebuildUserDuplicateIndexAgent::rebuildIndex
protected function rebuildIndex(AutoSearchUserSettings $userSettings): bool
{
	$progressData = $userSettings->getProgressData();
	$userId = $userSettings->getUserId();
	$entityTypeId = $userSettings->getEntityTypeId();

	$dedupeConfig = $this->getDedupeConfig($entityTypeId, $userId);
	$enablePermissionCheck = !\CCrmPerms::IsAdmin($userId);
	$isStart = ($userSettings->getStatusId() === AutoSearchUserSettings::STATUS_NEW);
	if ($isStart)
	{
		$userSettings
			->setStatusId(AutoSearchUserSettings::STATUS_INDEX_REBUILDING)
			->save()
		;

		// types and scope used in previous run:
		$prevTypeIds = $progressData['TYPE_IDS'] ?? [];
		$prevScope = $progressData['CURRENT_SCOPE'] ?? DuplicateIndexType::DEFAULT_SCOPE;

		$progressData = [
			'PREV_TYPE_IDS' => $prevTypeIds,
			'PREV_SCOPE' => $prevScope,
			'TYPE_IDS' => $dedupeConfig['typeIDs'],
			'CURRENT_SCOPE' => $dedupeConfig['scope'],
			'CURRENT_TYPE_INDEX' => 0,
			'PROCESSED_ITEMS' => 0,
			'FOUND_CHANGED_ITEMS' => 0,
			'FOUND_ITEMS' => 0,
			'TOTAL_ENTITIES' => 0,
			'STARTED_TIMESTAMP' => time(),
		];

		$effectiveTypeIDs = $progressData['TYPE_IDS'];
		$effectiveScope = $progressData['CURRENT_SCOPE'];
		$currentTypeIndex = $progressData['CURRENT_TYPE_INDEX'];
	}
	else
	{
		$effectiveTypeIDs = $progressData['TYPE_IDS'] ?? null;
		if (!is_array($effectiveTypeIDs) || empty($effectiveTypeIDs))
		{
			$effectiveTypeIDs = $dedupeConfig['typeIDs'];
		}
		$effectiveScope = $progressData['CURRENT_SCOPE'] ?? DuplicateIndexType::DEFAULT_SCOPE;
		$currentTypeIndex = isset($progressData['CURRENT_TYPE_INDEX'])
			? (int)$progressData['CURRENT_TYPE_INDEX'] : 0;
	}

	$effectiveTypeQty = count($effectiveTypeIDs);
	if ($currentTypeIndex >= $effectiveTypeQty)
	{
		$userSettings->calcAndSetNextExecTime();
		$userSettings->setStatusId(AutoSearchUserSettings::STATUS_NEW);
		$userSettings->save();

		return false;
	}

	// fast algorithm can be used only
	// if data for this typeId was already processed at least once
	// and scope was not changed:
	$checkChangedOnly =
		$userSettings->getCheckChangedOnly() &&
		$progressData['PREV_SCOPE'] == $progressData['CURRENT_SCOPE'] &&
		in_array($effectiveTypeIDs[$currentTypeIndex], $progressData['PREV_TYPE_IDS']);

	$builder = DuplicateManager::createAutomaticIndexBuilder(
		$effectiveTypeIDs[$currentTypeIndex],
		$entityTypeId,
		$userId,
		$enablePermissionCheck,
		[
			'SCOPE' => $effectiveScope,
			'LAST_INDEX_DATE' => $userSettings->getLastExecTime(),
			'CHECK_CHANGED_ONLY' => $checkChangedOnly,
		]
	);

	$buildData = $progressData['BUILD_DATA'] ?? [];

	$offset = (int)($buildData['OFFSET'] ?? 0);
	if ($offset === 0)
	{
		$builder->remove();
	}

	$limit = (int)($buildData['LIMIT'] ?? 0);
	if ($limit === 0)
	{
		$buildData['LIMIT'] = $this->getRebuildIndexLimit();
	}

	$isInProgress = $builder->build($buildData);
	if (isset($buildData['PROCESSED_ITEM_COUNT']))
	{
		$progressData['PROCESSED_ITEMS'] = (int)($progressData['PROCESSED_ITEMS'] ?? 0);
		$progressData['PROCESSED_ITEMS'] += $buildData['PROCESSED_ITEM_COUNT'];
	}

	if (isset($buildData['EFFECTIVE_ITEM_COUNT']))
	{
		$progressData['FOUND_CHANGED_ITEMS'] = (int)($progressData['FOUND_CHANGED_ITEMS'] ?? 0);
		$progressData['FOUND_CHANGED_ITEMS'] += $buildData['EFFECTIVE_ITEM_COUNT'];
	}

	$progressData['BUILD_DATA'] = $buildData;

	$isFinal = false;
	if (!$isInProgress)
	{
		$isFinal = $currentTypeIndex === ($effectiveTypeQty - 1);
		if (!$isFinal)
		{
			$progressData['CURRENT_TYPE_INDEX'] = ++$currentTypeIndex;
			unset($progressData['BUILD_DATA']);
		}
	}

	if ($isFinal)
	{
		if ($userSettings->getCheckChangedOnly())
		{
			// if search settings was changed,
			// possibly there are indexes for invalid types or scopes
			// and necessary to remove them:
			$unusedTypeIds = array_diff($progressData['PREV_TYPE_IDS'], $progressData['TYPE_IDS']);
			if (count($unusedTypeIds))
			{
				$builder->removeUnusedIndexByTypeIds($unusedTypeIds);
			}
			if ($progressData['PREV_SCOPE'] != $progressData['CURRENT_SCOPE'])
			{
				$builder->removeUnusedIndexByScope($progressData['PREV_SCOPE']);
			}
		}

		$foundItems = AutomaticDuplicateList::getTotalItems(
			$userId,
			$entityTypeId,
			$effectiveTypeIDs,
			$effectiveScope
		);

		$isMergeEnabled = $userSettings->getIsMergeEnabled();
		if ($foundItems > 0)
		{
			$totalEntries = AutomaticDuplicateList::getTotalEntityCount(
				$userId,
				$entityTypeId,
				$effectiveTypeIDs,
				$effectiveScope
			);

			$userSettings->setStatusId(
				$isMergeEnabled ?
					AutoSearchUserSettings::STATUS_MERGING :
					AutoSearchUserSettings::STATUS_READY_TO_MERGE
			);
		}
		else
		{
			$totalEntries = 0;
			$userSettings->setStatusId(AutoSearchUserSettings::STATUS_NEW);
		}
		$userSettings->calcAndSetNextExecTime();

		$userSettings->setLastExecTime(
			DateTime::createFromTimestamp($progressData['STARTED_TIMESTAMP'] ?? time())
		);
		$userSettings->setCheckChangedOnly(true);

		if ($totalEntries > 0 && Loader::includeModule('pull'))
		{
			\Bitrix\Pull\Event::add($userId, [
				'module_id' => 'crm',
				'command' => 'dedupe.autosearch.startMerge',
				'params' => $isMergeEnabled ?
					[
						'status' => 'MERGING',
						'entityTypeId' => $entityTypeId,
					] :
					[
						'status' => 'READY_TO_MERGE',
						'entityTypeId' => $entityTypeId,
						'progressData' => [
							'TOTAL_ENTITIES' => $totalEntries,
							'FOUND_ITEMS' => $foundItems,
						],
					],
			]);
		}

		$progressData['TOTAL_ENTITIES'] = $totalEntries;
		$progressData['FOUND_ITEMS'] = $foundItems;
	}

	$userSettings->setProgressData($progressData);
	$userSettings->save();

	return !$isFinal;
}