• Модуль: timeman
  • Путь к файлу: ~/bitrix/modules/timeman/lib/update/timemanversion19converter.php
  • Класс: BitrixTimemanUpdateTimemanVersion19Converter
  • Вызов: TimemanVersion19Converter::migrateSchedulesSettings
private function migrateSchedulesSettings()
{
	$this->createTemporaryTables();
	$this->logMessage('migrate Schedules Settings started', __LINE__);

	try
	{
		$this->deleteOldSchedules();
	}
	catch (MaximumExecutionSecondsExceededException $exc)
	{
		$this->logMessage('Maximum Execution Seconds Exceeded Exception - deleteOldSchedules', __LINE__);
		return false;
	}

	$defaultSettings = $this->getDefaultTimemanSettings();
	$departmentsTree = $this->buildDepartmentsTree();

	$commonScheduleForm = $this->createOrRestoreSchedulesData(reset(array_keys($departmentsTree)));

	# fetch all users/departments personal timeman settings
	# and group them
	try
	{
		$this->logMessage(json_encode($departmentsTree), __LINE__);
	}
	catch (Exception $exc)
	{
	}

	try
	{
		foreach ($departmentsTree as $departmentId => $departmentData)
		{
			$this->processDepartmentScheduleMigration(
				$departmentData,
				$defaultSettings,
				[(int)$departmentId]
			);
		}
	}
	catch (MaximumExecutionSecondsExceededException $exc)
	{
		$this->logMessage('Maximum Execution Seconds Exceeded Exception - saveTempDataForRestore', __LINE__);

		# save results in a temp table
		# that on the next run we can start from this point and continue grouping
		$this->saveTempDataForRestore();
		return false;
	}


	foreach ($this->scheduleForms as $key => $scheduleForm)
	{
		$scheduleForm->validate();
		if ($this->isMaxExecutionSecondsExceeded())
		{
			$this->saveTempDataForRestore();
			return false;
		}
		if ($this->savedSchedulesMap[$key]['scheduleId'] > 0)
		{
			// schedule already saved to db
			continue;
		}
		$result = DependencyManager::getInstance()
			->getScheduleService()
			->add($scheduleForm);
		if ($result->isSuccess())
		{
			$this->savedSchedulesMap[$key] = [
				'scheduleId' => $result->getSchedule()->getId(),
				'shiftId' => $result->getSchedule()->getShifts() ? reset($result->getSchedule()->getShifts()->getIdList()) : 0,
				'usersUpdated' => false,
			];
		}
	}
	$this->logMessage('schedules added', __LINE__);
	$commonScheduleId = 0;
	foreach ($this->scheduleForms as $scheduleFormKey => $scheduleForm)
	{
		if ($scheduleForm->isForAllUsers)
		{
			$commonScheduleId = (int)$this->savedSchedulesMap[$scheduleFormKey]['scheduleId'];
			break;
		}
	}
	$violationRows = [];
	foreach ($this->violationForms as $entityCode => $violationForm)
	{
		$violationForm->validate();
		$violationRules = ViolationRules::create($commonScheduleId, $violationForm, $entityCode);
		$violationRows[] = [
			'SCHEDULE_ID' => $violationRules->getScheduleId(),
			'ENTITY_CODE' => Application::getConnection()->getSqlHelper()->forSql($violationRules->getEntityCode()),
			'MAX_EXACT_START' => (int)$violationRules->getMaxExactStart(),
			'MIN_EXACT_END' => (int)$violationRules->getMinExactEnd(),
			'MIN_DAY_DURATION' => (int)$violationRules->getMinDayDuration(),
			'MAX_ALLOWED_TO_EDIT_WORK_TIME' => (int)$violationRules->getMaxAllowedToEditWorkTime(),
			'USERS_TO_NOTIFY' => $violationRules->getUsersToNotify(),
		];
	}
	if (!empty($violationRows) && $commonScheduleId > 0 && !$this->violationRulesSaved)
	{
		if (!empty(array_column($violationRows, 'ENTITY_CODE')))
		{
			Application::getConnection()->query("DELETE FROM `" . ViolationRulesTable::getTableName() . "` 
			WHERE SCHEDULE_ID = " . (int)$commonScheduleId
												. ' AND ENTITY_CODE IN ("' . implode('", "', array_column($violationRows, 'ENTITY_CODE')) . '")'
			);
		}
		$resultMulti = ViolationRulesTable::addMulti($violationRows, true);
		if ($resultMulti->isSuccess())
		{
			Application::getConnection()->query("UPDATE b_timeman_converter_violation_rules SET VIOLATION_RULES_SAVED = 1");
			$this->violationRulesSaved = true;
		}
	}
	if ($this->isMaxExecutionSecondsExceeded())
	{
		$this->saveTempDataForRestore();
		return false;
	}

	# build mapping - for every userId find scheduleId by user's settings
	foreach ($departmentsTree as $departmentId => $departmentData)
	{
		$this->saveUserToScheduleMap($departmentData, 'department');
	}

	# build mapping - scheduleId to array of userIds
	$scheduleKeyUserIdsMap = [];
	foreach ($this->userToScheduleMap as $userId => $userScheduleKey)
	{
		$scheduleKeyUserIdsMap[$userScheduleKey][] = (int)$userId;
	}


	foreach ($scheduleKeyUserIdsMap as $scheduleKey => $userIds)
	{
		if ($this->isMaxExecutionSecondsExceeded())
		{
			$this->saveTempDataForRestore();
			return false;
		}
		if ($this->savedSchedulesMap[$scheduleKey]['usersUpdated'] === true)
		{
			# columns SCHEDULE_ID and SHIFT_ID are already updated
			continue;
		}
		$chunks = array_chunk($userIds, 500);
		foreach ($chunks as $chunkUserIds)
		{
			Application::getConnection()->query("
				UPDATE b_timeman_entries
				SET 
				TIMESTAMP_X = TIMESTAMP_X,
				SCHEDULE_ID = " . (int)$this->savedSchedulesMap[$scheduleKey]['scheduleId'] . ",
				SHIFT_ID =  " . (int)$this->savedSchedulesMap[$scheduleKey]['shiftId'] . "
				WHERE USER_ID IN (" . implode(', ', $chunkUserIds) . ');'
			);
			$this->savedSchedulesMap[$scheduleKey]['usersUpdated'] = true;
		}
	}
	$this->logMessage('update SCHEDULE_ID for records - done', __LINE__);

	# just to prevent any record data with no scheduleId mapping
	foreach ($this->scheduleForms as $scheduleFormKey => $scheduleForm)
	{
		if ($scheduleForm->isForAllUsers)
		{
			Application::getConnection()->query("
				UPDATE b_timeman_entries
				SET 
				TIMESTAMP_X = TIMESTAMP_X,
				SCHEDULE_ID = " . (int)$this->savedSchedulesMap[$scheduleFormKey]['scheduleId'] . ",
				SHIFT_ID =  " . (int)$this->savedSchedulesMap[$scheduleFormKey]['shiftId'] . "
				WHERE SCHEDULE_ID = 0;"
			);
			break;
		}
	}

	Application::getConnection()->query("DROP TABLE `b_timeman_converter_collected_schedules`;");
	Application::getConnection()->query("DROP TABLE `b_timeman_converter_violation_rules`;");
	Application::getConnection()->query("DROP TABLE `b_timeman_converter_processed_entities`;");
	if ($this->dropLogAfterExecution)
	{
		Application::getConnection()->query("DROP TABLE `b_timeman_converter_log`;");
	}
	$this->logMessage('DROP helpers tables - done', __LINE__);

	return true;
}