Your IP : 18.216.69.239


Current Path : /var/www/axolotl/data/www/msk.axolotls.ru/bitrix/modules/timeman/classes/general/
Upload File :
Current File : /var/www/axolotl/data/www/msk.axolotls.ru/bitrix/modules/timeman/classes/general/timeman_user.php

<?

use Bitrix\Main\Context;
use Bitrix\Main\Web\Cookie;
use Bitrix\Timeman\Form\Worktime\WorktimeRecordForm;
use Bitrix\Timeman\Helper\TimeHelper;
use Bitrix\Timeman\Model\Worktime\Record\WorktimeRecord;
use Bitrix\Timeman\Model\Worktime\Record\WorktimeRecordTable;
use Bitrix\Timeman\Service\DependencyManager;
use Bitrix\Timeman\UseCase\Worktime\Manage;
use Bitrix\Timeman\Service\Worktime\Result\WorktimeServiceResult;

class CTimeManUser
{
	public $SITE_ID = SITE_ID;

	protected $USER_ID;
	protected $ABSENCE_TYPE_ID = null;
	protected $SETTINGS = null;
	protected $bTasksEnabled;
	protected $UF_DEPARTMENT;

	protected static $instance = null;
	protected static $LAST_ENTRY = [];

	public static function instance()
	{
		if (!self::$instance)
		{
			self::$instance = new CTimeManUser();
		}

		return self::$instance;
	}

	public function __construct($USER_ID = 0, $site_id = SITE_ID)
	{
		$this->USER_ID = $USER_ID > 0 ? $USER_ID : $GLOBALS['USER']->GetID();
		$this->SITE_ID = $site_id;
		$this->bTasksEnabled = CModule::IncludeModule('tasks');
	}

	public function GetID()
	{
		return $this->USER_ID;
	}

	public function isEntryValid($action, $timestamp)
	{
		global $APPLICATION;

		$arSettings = $this->GetSettings();

		if (!$arSettings['UF_TM_FREE'] && $timestamp !== false)
		{
			// $timestamp is in server time yet
			if (abs(time() - $timestamp) > $arSettings['UF_TM_ALLOWED_DELTA'])
			{
				$APPLICATION->ThrowException('Time was changed manually', 'TIME_CHANGE');
				return false;
			}
		}

		return true;
	}

	/**
	 * @param null $eventName
	 * @return WorktimeRecordForm
	 */
	private function createWorktimeRecordForm($eventName = null)
	{
		$recordForm = WorktimeRecordForm::createWithEventForm($eventName);
		$recordForm->userId = $this->USER_ID;

		return $recordForm;
	}

	private function buildEditForm($arParams)
	{
		$recordForm = WorktimeRecordForm::createWithEventForm();
		$recordForm->editedBy = $this->USER_ID;
		$recordForm->recordedStartSeconds = $arParams['TIME_START'];
		$recordForm->recordedStartDateFormatted = $arParams['DATE_START'];
		$recordForm->recordedStopSeconds = $arParams['TIME_FINISH'];
		$recordForm->recordedStopDateFormatted = $arParams['DATE_FINISH'];
		$recordForm->recordedBreakLength = $arParams['TIME_LEAKS'];
		$recordForm->device = $arParams['DEVICE'];
		$recordForm->getFirstEventForm()->reason = $arParams['REPORT'];
		if (isset($arParams['LAT_CLOSE']))
		{
			$recordForm->latitudeClose = $arParams['LAT_CLOSE'];
		}
		if (isset($arParams['LON_CLOSE']))
		{
			$recordForm->longitudeClose = $arParams['LAT_CLOSE'];
		}
		$recordForm->ipClose = $_SERVER['REMOTE_ADDR'];
		return $recordForm;
	}

	public function editDay($arParams)
	{
		global $APPLICATION;

		$recordForm = $this->buildEditForm($arParams);

		if ($recordForm->validate())
		{
			$result = (new Manage\Edit\Handler())->handle($recordForm);
			if ($result->isSuccess())
			{
				static::clearFullReportCache();
				return WorktimeRecordTable::convertFieldsCompatible($result->getWorktimeRecord()->collectValues());
			}
			if (!empty($result->getErrors()) && $result->getErrors()[0]->getCode() === WorktimeServiceResult::ERROR_FOR_USER)
			{
				$APPLICATION->ThrowException($result->getErrors()[0]->getMessage(), 'ALERT_WARNING');
			}
			else
			{
				$APPLICATION->ThrowException($result->getErrorMessages()[0]);
			}
			return false;
		}
		if ($recordForm->getFirstError()->getCode() === 'recordedBreakLength')
		{
			$APPLICATION->ThrowException($recordForm->getFirstError()->getMessage(), 'ALERT_WARNING');
		}
		else
		{
			$APPLICATION->ThrowException($recordForm->getFirstError()->getMessage());
		}
		return false;
	}

	private function buildStartForm($timestamp, $report, $extraInformation)
	{
		$recordForm = $this->createWorktimeRecordForm();
		$recordForm->recordedStartSeconds = $timestamp > 0 ? $timestamp : null;
		$recordForm->getFirstEventForm()->reason = $report;
		$recordForm->latitudeOpen = $extraInformation['LAT_OPEN'];
		$recordForm->longitudeOpen = $extraInformation['LON_OPEN'];
		$recordForm->ipOpen = $extraInformation['IP_OPEN'];
		$recordForm->device = $extraInformation['DEVICE'];
		$recordForm->recordedStartDateFormatted = isset($extraInformation['CUSTOM_DATE']) ? $extraInformation['CUSTOM_DATE'] : null;

		if (($lastEntry = $this->_getLastData()) && $lastEntry['TASKS'])
		{
			$arTasks = $this->getTasks($lastEntry['TASKS'], true);
			foreach ($arTasks as $task)
			{
				$recordForm->tasks[] = $task['ID'];
			}
		}
		return $recordForm;
	}

	public function openDay($timestamp = false, $report = '', $extraInformation = [])
	{
		global $APPLICATION;

		$recordForm = $this->buildStartForm($timestamp, $report, $extraInformation);

		if ($recordForm->validate())
		{
			$result = (new Manage\Start\Handler())->handle($recordForm);

			if ($result->isSuccess())
			{
				CUser::setLastActivityDate($this->USER_ID);
				$APPLICATION->resetException();
				$this->deleteLastPauseInfo($this->USER_ID);
				static::clearFullReportCache();

				$data = WorktimeRecordTable::convertFieldsCompatible($result->getWorktimeRecord()->collectValues());

				$e = GetModuleEvents('timeman', 'OnAfterTMDayStart');
				while ($a = $e->Fetch())
				{
					ExecuteModuleEventEx($a, [$data]);
				}

				return $data;
			}
			if ($result->getErrors()[0]->getCode() === WorktimeServiceResult::ERROR_FOR_USER)
			{
				$APPLICATION->ThrowException($result->getErrors()[0]->getMessage(), 'ALERT_WARNING');
			}
			else
			{
				$APPLICATION->ThrowException($result->getErrorMessages()[0]);
			}
			return false;
		}
		$APPLICATION->ThrowException($recordForm->getFirstError()->getMessage());
		return false;
	}

	private function deleteLastPauseInfo(int $userId)
	{
		$response = Context::getCurrent()->getResponse();
		$cookie = new Cookie('TIMEMAN_LAST_PAUSE_'.$userId, '', time() - 24 * 3600);
		$response->addCookie($cookie);
	}

	private function buildStopForm($timestamp, $report, $extraInformation)
	{
		$recordForm = $this->createWorktimeRecordForm();
		$recordForm->recordedStopSeconds = $timestamp > 0 ? $timestamp : null;
		$recordForm->getFirstEventForm()->reason = $report;
		$recordForm->latitudeClose = $extraInformation['LAT_CLOSE'];
		$recordForm->longitudeClose = $extraInformation['LON_CLOSE'];
		$recordForm->ipClose = $_SERVER['REMOTE_ADDR'];
		$recordForm->device = $extraInformation['DEVICE'];
		$recordForm->recordedStopDateFormatted = isset($extraInformation['CUSTOM_DATE']) ? $extraInformation['CUSTOM_DATE'] : null;
		return $recordForm;
	}

	public function closeDay($timestamp = false, $report = '', $bFieldsOnly = false, $extraInformation = [])
	{
		global $APPLICATION;
		if ($this->State() == 'EXPIRED' && !$timestamp)
		{
			$GLOBALS['APPLICATION']->ThrowException('Workday is expired', 'WD_EXPIRED');
			return false;
		}
		$recordForm = $this->buildStopForm($timestamp, $report, $extraInformation);
		if ($this->State() == 'EXPIRED')
		{
			$recordForm->editedBy = $this->USER_ID;
		}
		if ($recordForm->validate())
		{
			$result = (new Manage\Stop\Handler())->handle($recordForm);

			if ($result->isSuccess())
			{
				CUser::SetLastActivityDate($this->USER_ID);
				$recordFields = WorktimeRecordTable::convertFieldsCompatible($result->getWorktimeRecord()->collectRawValues());

				if (isset($arFields['ACTIVE']) && $recordFields['ACTIVE'] == 'N')
				{
					CTimeManNotify::SendMessage($recordFields['ID']);
				}

				static::clearFullReportCache();

				$e = GetModuleEvents('timeman', 'OnAfterTMDayEnd');
				while ($a = $e->Fetch())
				{
					ExecuteModuleEventEx($a, [$recordFields]);
				}

				return $recordFields;
			}
			else
			{
				foreach ($result->getErrors() as $error)
				{
					if ($error->getCode() === WorktimeServiceResult::ERROR_REASON_NEEDED
						|| $error->getCode() === WorktimeServiceResult::ERROR_EXPIRED_REASON_NEEDED)
					{
						$APPLICATION->ThrowException($error->getMessage(), 'REPORT_NEEDED');
					}
				}
				if ($result->getErrors()[0]->getCode() === WorktimeServiceResult::ERROR_FOR_USER)
				{
					$APPLICATION->ThrowException($result->getErrors()[0]->getMessage(), 'ALERT_WARNING');
				}
			}
		}
		return false;
	}

	private function buildReopenForm($extraInformation)
	{
		$recordForm = $this->createWorktimeRecordForm();
		$recordForm->device = $extraInformation['DEVICE'];
		return $recordForm;
	}

	public function reopenDay($bSkipCheck = false, $site_id = SITE_ID, $extraInformation = [])
	{
		global $APPLICATION;
		$lastEntry = $this->_GetLastData(true);

		$recordForm = $this->buildReopenForm($extraInformation);

		if ($recordForm->validate())
		{
			$result = (new Manage\Relaunch\Handler())->handle($recordForm);

			if ($result->isSuccess())
			{
				$ts_finish = MakeTimeStamp($lastEntry['DATE_FINISH']) - CTimeZone::GetOffset();
				$leak = time() - $ts_finish;
				CUser::SetLastActivityDate($this->USER_ID);
				CTimeManReport::Reopen($lastEntry['ID']);
				CTimeManReportDaily::Reopen($lastEntry['ID']);

				if ($leak > BX_TIMEMAN_ALLOWED_TIME_DELTA)
				{
					$this->setLastPauseInfo($this->USER_ID, $ts_finish, $ts_finish + $leak);

					$report = \Bitrix\Timeman\Model\Worktime\Report\WorktimeReport::createReopenReport(
						$lastEntry['USER_ID'],
						$lastEntry['ID']
					);
					$report->save();
				}
				static::clearFullReportCache();

				$data = WorktimeRecordTable::convertFieldsCompatible($result->getWorktimeRecord()->collectValues());

				$e = GetModuleEvents('timeman', 'OnAfterTMDayContinue');
				while ($a = $e->Fetch())
				{
					ExecuteModuleEventEx($a, [$data]);
				}

				return $data;
			}
			if ($result->getErrors()[0]->getCode() === WorktimeServiceResult::ERROR_FOR_USER)
			{
				$APPLICATION->ThrowException($result->getErrors()[0]->getMessage(), 'ALERT_WARNING');
			}
		}

		return false;
	}

	private function getLastPauseInfo(int $userId): array
	{
		$lastPause = Context::getCurrent()->getRequest()->getCookie('TIMEMAN_LAST_PAUSE_'.$userId);
		if ($lastPause)
		{
			$explode = explode('|', $lastPause);
			return [
				'DATE_START' => (int) $explode[0],
				'DATE_FINISH' => (int) $explode[1],
			];
		}
		else
		{
			return [];
		}
	}

	private function setLastPauseInfo(int $userId, int $dateStart, int $dateFinish): void
	{
		$lastPause = $dateStart.'|'.$dateFinish;
		$cookie = new Cookie('TIMEMAN_LAST_PAUSE_'.$userId, $lastPause, 0);
		Context::getCurrent()->getResponse()->addCookie($cookie);
	}

	private function buildPauseForm($extraInformation)
	{
		$recordForm = $this->createWorktimeRecordForm();
		$recordForm->latitudeClose = empty($extraInformation['LAT_CLOSE']) ? null : $extraInformation['LAT_CLOSE'];
		$recordForm->longitudeClose = empty($extraInformation['LON_CLOSE']) ? null : $extraInformation['LON_CLOSE'];
		$recordForm->ipClose = empty($extraInformation['IP_CLOSE']) ? null : $extraInformation['IP_CLOSE'];
		$recordForm->device = $extraInformation['DEVICE'];
		return $recordForm;
	}

	public function pauseDay($extraInformation = [])
	{
		global $APPLICATION;

		$recordForm = $this->buildPauseForm($extraInformation);

		if ($recordForm->validate())
		{
			$result = (new Manage\Pause\Handler())->handle($recordForm);

			if ($result->isSuccess())
			{
				CUser::SetLastActivityDate($this->USER_ID);

				static::clearFullReportCache();

				$data = WorktimeRecordTable::convertFieldsCompatible($result->getWorktimeRecord()->collectValues());

				$e = GetModuleEvents('timeman', 'OnAfterTMDayPause');
				while ($a = $e->Fetch())
				{
					ExecuteModuleEventEx($a, [$data]);
				}

				return $data;
			}
			if ($result->getErrors()[0]->getCode() === WorktimeServiceResult::ERROR_FOR_USER)
			{
				$APPLICATION->ThrowException($result->getErrors()[0]->getMessage(), 'ALERT_WARNING');
			}
			else
			{
				$APPLICATION->ThrowException('WD_NOT_OPEN');
			}
		}

		return false;
	}

	public function SetReport($report, $report_ts, $entry_id = null)
	{
		global $USER, $APPLICATION;

		if ($last_entry = $this->_GetLastData())
		{
			if ($entry_id && $entry_id != $last_entry['ID'])
			{
				$report_ts = 0;
			}

			$dbRes = CTimeManReport::GetList([], ['ENTRY_ID' => $last_entry['ID'], 'REPORT_TYPE' => 'REPORT']);
			if ($arRes = $dbRes->Fetch())
			{
				$ID = $arRes['ID'];

				$current_report_ts = MakeTimeStamp($arRes['TIMESTAMP_X']);
				if ($current_report_ts > $report_ts)
				{
					return $arRes;
				}

				$arFields = ['REPORT' => $report];
				if (!CTimeManReport::Update($ID, $arFields))
				{
					return false;
				}
			}
			else
			{
				$arFields = [
					'ENTRY_ID' => $last_entry['ID'],
					'USER_ID' => $USER->GetID(), // not $last_entry['USER_ID']!
					'ACTIVE' => 'Y',
					'REPORT_TYPE' => 'REPORT',
					'REPORT' => $report,
				];

				if (!($ID = CTimeManReport::Add($arFields)))
				{
					return false;
				}
			}

			$dbRes = CTimeManReport::GetByID($ID);
			return $dbRes->Fetch();
		}
		else
		{
			$APPLICATION->ThrowException('No entry', 'SAVE_REPORT_NO_ENTRY');
		}
	}

	public function GetCurrentInfo($clear = false)
	{
		return $this->_GetLastData($clear);
	}

	public function State()
	{
		return ($this->isDayExpired()
			? 'EXPIRED'
			: ($this->isDayOpen()
				? 'OPENED'
				: ($this->isDayPaused()
					? 'PAUSED'
					: 'CLOSED'
				)
			)
		);
	}

	public function GetExpiredRecommendedDate()
	{
		$recDefaultSeconds = COption::GetOptionInt('timeman', 'workday_finish', 18 * 3600);

		if ($lastEntry = $this->_GetLastData())
		{
			$schedule = DependencyManager::getInstance()
				->getScheduleProvider()
				->getScheduleWithShifts($lastEntry['SCHEDULE_ID']);
			$shift = null;

			if ($schedule && $lastEntry['SHIFT_ID'] > 0)
			{
				$shift = $schedule->obtainShiftByPrimary($lastEntry['SHIFT_ID']);
			}
			$manager = DependencyManager::getInstance()
				->buildWorktimeRecordManager(
					WorktimeRecord::wakeUpRecord($lastEntry),
					$schedule,
					$shift
				);
			$recommendedTimestamp = $manager->getRecommendedStopTimestamp();
			if ($recommendedTimestamp > 0)
			{
				return TimeHelper::getInstance()->convertUtcTimestampToDaySeconds(
					$recommendedTimestamp,
					TimeHelper::getInstance()->getUserTimezone($lastEntry['USER_ID'])
				);
			}
		}

		return $recDefaultSeconds;
	}

	// check if day is currently opened
	public function isDayOpen()
	{
		return WorktimeRecord::isRecordOpened($this->_GetLastData());
	}

	// check if day is paused
	public function isDayPaused()
	{
		return WorktimeRecord::isRecordPaused($this->_GetLastData());
	}

	public function getDayStartOffset($entry, $bTs = false)
	{
		$ts_start = $bTs ? $entry['DATE_START'] : (MakeTimeStamp($entry['DATE_START']) - CTimeZone::GetOffset());
		$ts_start_day = MakeTimeStamp(ConvertTimeStamp($ts_start, 'SHORT'));

		$time_start = $ts_start - $ts_start_day;

		return $entry['TIME_START'] - $time_start;
	}

	public function isDayOpenedToday()
	{
		// server time at the moment of day start
		$ts_start = !empty(CTimeManUser::$LAST_ENTRY[$this->USER_ID]['DATE_START']) ? (MakeTimeStamp(CTimeManUser::$LAST_ENTRY[$this->USER_ID]['DATE_START']) - CTimeZone::GetOffset()) : time();
		$ts_start_day = MakeTimeStamp(ConvertTimeStamp($ts_start, 'SHORT'));

		// server time that was at the day start
		$time_start = $ts_start - $ts_start_day;

		// server timezone diff with server that was at the day start
		$timezone_diff = CTimeManUser::$LAST_ENTRY[$this->USER_ID]['TIME_START'] - $time_start;

		// current date with such timezone_diff;
		$t = time();
		$date_current = date('Y-m-d', $t + $timezone_diff);
		$date_current_day = date('Y-m-d', $ts_start_day);

		return $date_current == $date_current_day;
	}

	// check if user forgot to close wd
	public function isDayExpired()
	{
		$recordData = $this->_GetLastData(true);
		if (!$recordData)
		{
			return false;
		}
		$schedule = DependencyManager::getInstance()
			->getScheduleProvider()
			->getScheduleWithShifts($recordData['SCHEDULE_ID']);
		$shift = null;

		if ($schedule && $recordData['SHIFT_ID'] > 0)
		{
			$shift = $schedule->obtainShiftByPrimary($recordData['SHIFT_ID']);
		}
		$manager = DependencyManager::getInstance()
			->buildWorktimeRecordManager(
				WorktimeRecord::wakeUpRecord($recordData),
				$schedule,
				$shift
			);
		return $manager->isRecordExpired();
	}

	/**
	 * @deprecated
	 */
	public function OpenAction($bSkipCheck = false)
	{
		$list = \Bitrix\Timeman\Service\DependencyManager::getInstance()
			->getWorktimeActionList();
		$actionList = $list->buildPossibleActionsListForUser($this->USER_ID);

		if (empty($actionList->getStartActions()) && empty($actionList->getReopenActions()) && empty($actionList->getContinueActions()))
		{
			return false;
		}
		if (!empty($actionList->getReopenActions()) && !empty($actionList->getStartActions()))
		{
			return 'OPEN'; // as main action
		}
		if (!empty($actionList->getReopenActions()) || !empty($actionList->getContinueActions()))
		{
			return 'REOPEN';
		}
		return 'OPEN';
	}

	public function GetEvents($date)
	{
		$arEvents = [];

		if (CBXFeatures::IsFeatureEnabled('Calendar'))
		{
			$ts = CTimeMan::RemoveHoursTS(MakeTimeStamp($date));

			if ($ts > 0)
			{
				$calendar2 = COption::GetOptionString("intranet", "calendar_2", "N") == "Y" && CModule::IncludeModule('calendar');

				if ($calendar2)
				{
					$arFilter = [
						'arFilter' => [
							"OWNER_ID" => $this->USER_ID,
							"FROM_LIMIT" => ConvertTimeStamp($ts, 'FULL'),
							"TO_LIMIT" => ConvertTimeStamp($ts + 86399, 'FULL'),
						],
						'parseRecursion' => true,
						'userId' => $this->USER_ID,
						'skipDeclined' => true,
						'fetchAttendees' => false,
						'fetchMeetings' => true,
					];

					$arNewEvents = CCalendarEvent::GetList($arFilter);
					if (count($arNewEvents) > 0)
					{
						foreach ($arNewEvents as $arEvent)
						{
							if ($arEvent['RRULE'])
							{
								$ts_from = MakeTimeStamp($arEvent['DT_FROM']);
								$ts_to = MakeTimeStamp($arEvent['DT_TO']);

								if ($ts_to < $ts || $ts_from > $ts + 86399)
								{
									continue;
								}
							}

							$arEvents[] = [
								'ID' => $arEvent['ID'],
								'OWNER_ID' => $this->USER_ID,
								'CREATED_BY' => $arEvent['CREATED_BY'],
								'NAME' => $arEvent['NAME'],
								'DETAIL_TEXT' => $arEvent['DESCRIPTION'],
								'DATE_FROM' => $arEvent['DT_FROM'],
								'DATE_TO' => $arEvent['DT_TO'],
								'IMPORTANCE' => $arEvent['IMPORTANCE'],
								'ACCESSIBILITY' => $arEvent['ACCESSIBILITY'],
							];
						}
					}
				}
				else
				{
					$arEvents = CEventCalendar::GetNearestEventsList(
						[
							'userId' => $this->USER_ID,
							'bCurUserList' => true,
							'fromLimit' => ConvertTimeStamp($ts, 'FULL'),
							'toLimit' => ConvertTimeStamp($ts + 86399, 'FULL'),
							'iblockId' => COption::GetOptionInt('intranet', 'iblock_calendar'),
						]
					);

					foreach ($arEvents as $key => $event)
					{
						if ($event['STATUS'] === 'N')
						{
							unset($arEvents[$key]);
						}
					}
				}

				return array_values($arEvents);
			}
		}

		return false;
	}

	public function GetTasks($arIDs = [], $bOpened = false, $USER_ID = null)
	{
		$res = null;

		if (!is_array($arIDs) && $arIDs <> '')
		{
			$arIDs = unserialize($arIDs);
		}

		$arIDs = array_values($arIDs);

		if (!$USER_ID)
		{
			$USER_ID = $this->USER_ID;
		}

		if (CBXFeatures::IsFeatureEnabled('Tasks') && CModule::IncludeModule('tasks'))
		{
			$res = [];
			if (count($arIDs) > 0)
			{
				$arFilter = ['ID' => $arIDs];
				if ($bOpened)
				{
					$arFilter['!STATUS'] = [4, 5, 7];
				}

				$dbRes = CTasks::GetList([], $arFilter);
				while ($arRes = $dbRes->Fetch())
				{
					$arRes['ACCOMPLICES'] = $arRes['AUDITORS'] = [];
					$rsMembers = CTaskMembers::GetList(
						[],
						['TASK_ID' => $arRes['ID']]
					);

					while ($arMember = $rsMembers->Fetch())
					{
						if ($arMember['TYPE'] == 'A')
						{
							$arRes['ACCOMPLICES'][] = $arMember['USER_ID'];
						}
						elseif ($arMember['TYPE'] == 'U')
						{
							$arRes['AUDITORS'][] = $arMember['USER_ID'];
						}
					}

					// Permit only for responsible user, accomplices or auditors
					$isPermited = (($arRes['RESPONSIBLE_ID'] == $USER_ID)
								   || in_array($USER_ID, $arRes['ACCOMPLICES'])
								   || in_array($USER_ID, $arRes['AUDITORS'])
					);

					if (!$isPermited)
					{
						continue;
					}

					$res[] = [
						'ID' => $arRes['ID'],
						'PRIORITY' => $arRes['PRIORITY'],
						'STATUS' => $arRes['STATUS'],
						'TITLE' => $arRes['TITLE'],
						'TASK_CONTROL' => $arRes['TASK_CONTROL'],
						'URL' => str_replace(
							['#USER_ID#', '#TASK_ID#'],
							[$this->USER_ID, $arRes['ID']],
							COption::GetOptionString('intranet', 'path_task_user_entry', '/company/personal/user/#USER_ID#/tasks/task/view/#TASK_ID#/')
						),
					];
				}
			}
		}

		return $res;
	}

	public function TaskStatus($id, $status)
	{
		global $BX_TIMEMAN_TASKS_MIGRATION_RULES;

		$arTasks = $this->GetTasks([$id]);

		if (is_array($arTasks) && count($arTasks) === 1)
		{
			$current_status = $arTasks[0]['STATUS'];
			if ($status === 5)
			{
				$status = 4;
			}

			if (
				is_array($BX_TIMEMAN_TASKS_MIGRATION_RULES[$current_status])
				&& in_array($status, $BX_TIMEMAN_TASKS_MIGRATION_RULES[$current_status])
			)
			{
				if ($status === 4)
				{
					$status = $arTasks[0]['TASK_CONTROL'] == 'Y' ? 4 : 5;
				}

				$obt = new CTasks();
				if ($obt->Update($id, ['STATUS' => $status]))
				{
					return $this->_GetLastData(true);
				}
			}
		}
	}

	public function TaskActions($arActions, $site_id = SITE_ID)
	{
		if (
		($last_entry = $this->_GetLastData())
			//&& $this->isDayOpen()
			//&& !$this->isDayExpired()
		)
		{
			$this->SITE_ID = $site_id;
			$arTasks = $last_entry['TASKS'];

			if (!is_array($arTasks))
			{
				$arTasks = [];
			}

			if ($arActions['name'] <> '')
			{
				$obt = new CTasks();
				if ($ID = $obt->Add([
					'RESPONSIBLE_ID' => $this->USER_ID,
					'TITLE' => $arActions['name'],
					'TAGS' => [],
					'STATUS' => 2,
					'SITE_ID' => $this->SITE_ID,
				]))
				{
					if (!is_array($arActions['add']))
					{
						$arActions['add'] = [$ID];
					}
					else
					{
						$arActions['add'][] = $ID;
					}
				}
			}

			if (is_array($arActions['add']))
			{
				foreach ($arActions['add'] as $task_id)
				{
					$arTasks[] = intval($task_id);
				}

				$GLOBALS['BX_TIMEMAN_RECENTLY_ADDED_TASK_ID'] = $task_id;
			}

			$arTasks = array_unique($arTasks);

			if (is_array($arActions['remove']))
			{
				$arActions['remove'] = array_unique($arActions['remove']);

				foreach ($arActions['remove'] as $task_id)
				{
					$task_id = intval($task_id);

					if (false !== ($key = array_search($task_id, $arTasks)))
					{
						unset($arTasks[$key]);
					}
				}
			}

			$arFields = ['TASKS' => []];

			if (count($arTasks) > 0)
			{
				$arCheck = $this->GetTasks($arTasks);
				foreach ($arCheck as $a)
				{
					$arFields['TASKS'][] = $a['ID'];
				}
			}

			if (CTimeManEntry::Update($last_entry['ID'], $arFields))
			{
				return $this->_GetLastData(true);
			}
		}

		return false;
	}

	public function GetSettings($arNeededSettings = null)
	{
		return $this->__GetSettings($arNeededSettings, false);
	}

	public function GetPersonalSettings($arNeededSettings = null)
	{
		$arSettings = $this->__GetSettings($arNeededSettings, true);

		if (isset($arSettings['UF_TIMEMAN']) && $arSettings['UF_TIMEMAN'] !== '')
		{
			$arSettings['UF_TIMEMAN'] = $arSettings['UF_TIMEMAN'] == 'Y';
		}
		if (isset($arSettings['UF_TM_MAX_START']) && $arSettings['UF_TM_MAX_START'] == '0')
		{
			$arSettings['UF_TM_MAX_START'] = '';
		}
		if (isset($arSettings['UF_TM_MIN_FINISH']) && $arSettings['UF_TM_MIN_FINISH'] == '0')
		{
			$arSettings['UF_TM_MIN_FINISH'] = '';
		}
		if (isset($arSettings['UF_TM_MIN_DURATION']) && $arSettings['UF_TM_MIN_DURATION'] == '0')
		{
			$arSettings['UF_TM_MIN_DURATION'] = '';
		}
		if (isset($arSettings['UF_TM_FREE']) && $arSettings['UF_TM_FREE'] !== '')
		{
			$arSettings['UF_TM_FREE'] = $arSettings['UF_TM_FREE'] == 'Y';
		}
		if (isset($arSettings['UF_TM_ALLOWED_DELTA']) && $arSettings['UF_TM_ALLOWED_DELTA'] >= 0)
		{
			$arSettings['UF_TM_ALLOWED_DELTA'] = CTimeMan::MakeShortTS($arSettings['UF_TM_ALLOWED_DELTA']);
		}

		return $arSettings;
	}

	protected function __GetSettings($arNeededSettings, $bPersonal = false)
	{
		global $CACHE_MANAGER;

		$cat = intval($bPersonal);

		if (!is_array($this->SETTINGS[$cat]))
		{
			$this->SETTINGS[$cat] = [];

			$cache_id = 'timeman|structure_settings|u' . $this->USER_ID . '_' . $cat;

			if (CACHED_timeman_settings !== false
				&& $CACHE_MANAGER->Read(
					CACHED_timeman_settings,
					$cache_id,
					"timeman_structure_" . COption::GetOptionInt('intranet', 'iblock_structure', false)
				)
			)
			{
				$this->SETTINGS[$cat] = $CACHE_MANAGER->Get($cache_id);
			}
			else
			{
				$this->SETTINGS[$cat] = $bPersonal ? $this->_GetPersonalSettings() : $this->_GetSettings();

				if (CACHED_timeman_settings !== false)
				{
					$CACHE_MANAGER->Set($cache_id, $this->SETTINGS[$cat]);
				}
			}
		}
		if (is_null($arNeededSettings) ||
			(is_array($arNeededSettings) && array_key_exists('UF_TM_FREE', $arNeededSettings)))
		{
			$this->SETTINGS[$cat]['UF_TM_FREE'] = false;
			$userSchedules = \Bitrix\Timeman\Service\DependencyManager::getInstance()
				->getScheduleProvider()
				->findSchedulesByUserId($this->USER_ID, ['select' => ['ID', 'SCHEDULE_TYPE',]]);
			foreach ($userSchedules as $userSchedule)
			{
				if ($userSchedule->isFlextime())
				{
					$this->SETTINGS[$cat]['UF_TM_FREE'] = true;
					break;
				}
			}
		}

		$arSettings = $this->SETTINGS[$cat];

		if (is_array($arNeededSettings) && count($arNeededSettings) > 0)
		{
			foreach ($arSettings as $set => $value)
			{
				if (!in_array($set, $arNeededSettings))
				{
					unset($arSettings[$set]);
				}
			}
		}

		return $arSettings;
	}

	protected function _GetPersonalSettings()
	{
		global $USER_FIELD_MANAGER;

		$arPersonalSettings = [];

		$dbRes = CUser::GetByID($this->USER_ID);
		if ($arUser = $dbRes->Fetch())
		{
			$arPersonalSettings = [
				'UF_TIMEMAN' => $arUser['UF_TIMEMAN'],
				'UF_TM_MAX_START' => CTimeMan::MakeShortTS($arUser['UF_TM_MAX_START']),
				'UF_TM_MIN_FINISH' => CTimeMan::MakeShortTS($arUser['UF_TM_MIN_FINISH']),
				'UF_TM_MIN_DURATION' => CTimeMan::MakeShortTS($arUser['UF_TM_MIN_DURATION']),
				'UF_TM_REPORT_REQ' => $arUser['UF_TM_REPORT_REQ'],
				'UF_LAST_REPORT_DATE' => $arUser['UF_LAST_REPORT_DATE'],

				'UF_REPORT_PERIOD' => $arUser['UF_REPORT_PERIOD'],
				'UF_TM_REPORT_DATE' => $arUser['UF_TM_REPORT_DATE'],
				'UF_TM_TIME' => $arUser['UF_TM_TIME'],
				'UF_TM_DAY' => $arUser['UF_TM_DAY'],
				'UF_DELAY_TIME' => $arUser['UF_DELAY_TIME'],
				'UF_TM_REPORT_TPL' => $arUser['UF_TM_REPORT_TPL'],
				'UF_TM_ALLOWED_DELTA' => $arUser['UF_TM_ALLOWED_DELTA'],
			];

			$this->UF_DEPARTMENT = $arUser['UF_DEPARTMENT'];

			if ($arPersonalSettings['UF_TIMEMAN'] || $arPersonalSettings['UF_TM_REPORT_REQ'] || $arPersonalSettings['UF_REPORT_PERIOD'])
			{
				$arAllFields = $USER_FIELD_MANAGER->GetUserFields('USER');

				if ($arPersonalSettings['UF_TIMEMAN'])
				{
					$dbRes = CUserFieldEnum::GetList([], [
						'USER_FIELD_ID' => $arAllFields['UF_TIMEMAN']['ID'],
						'ID' => $arPersonalSettings['UF_TIMEMAN'],
					]);

					if ($arRes = $dbRes->Fetch())
					{
						$arPersonalSettings['UF_TIMEMAN'] = $arRes['XML_ID'];
					}
				}
				if ($arPersonalSettings['UF_REPORT_PERIOD'])
				{
					$dbRes = CUserFieldEnum::GetList([], [
						'USER_FIELD_ID' => $arAllFields['UF_REPORT_PERIOD']['ID'],
						'ID' => $arPersonalSettings['UF_REPORT_PERIOD'],
					]);

					if ($arRes = $dbRes->Fetch())
					{
						$arPersonalSettings['UF_REPORT_PERIOD'] = $arRes['XML_ID'];
					}

				}
				if ($arPersonalSettings['UF_TM_REPORT_REQ'])
				{
					$dbRes = CUserFieldEnum::GetList([], [
						'USER_FIELD_ID' => $arAllFields['UF_TM_REPORT_REQ']['ID'],
						'ID' => $arPersonalSettings['UF_TM_REPORT_REQ'],
					]);

					if ($arRes = $dbRes->Fetch())
					{
						$arPersonalSettings['UF_TM_REPORT_REQ'] = $arRes['XML_ID'];
					}
				}
			}
		}

		return $arPersonalSettings;
	}

	protected function _GetSettings()
	{
		global $USER_FIELD_MANAGER;

		$arRes = [];

		$arRes = $this->_GetPersonalSettings();
		if ($arRes)
		{
			if ($arRes['UF_TIMEMAN'] === 'N')
			{
				return ['UF_TIMEMAN' => false];
			}

			$cnt = 0;
			if ($arRes['UF_TIMEMAN'] !== 'Y')
			{
				$cnt++;
			}
			foreach ($arRes as $fld => $value)
			{
				if (!$arRes[$fld] || $arRes[$fld] == '00:00')
				{
					$cnt++;
				}
			}

			if ($cnt > 0)
			{
				if (is_array($this->UF_DEPARTMENT) && count($this->UF_DEPARTMENT) > 0)
				{
					$allSet = [
						'UF_TIMEMAN' => $arRes['UF_TIMEMAN'] ? $arRes['UF_TIMEMAN'] : false,
						'UF_TM_MAX_START' => 86401,
						'UF_TM_MIN_FINISH' => false,
						'UF_TM_MIN_DURATION' => false,
						'UF_TM_REPORT_REQ' => false,
						'UF_REPORT_PERIOD' => $arRes['UF_REPORT_PERIOD'],
						'UF_TM_REPORT_DATE' => $arRes['UF_TM_REPORT_DATE'],
						'UF_TM_TIME' => $arRes['UF_TM_TIME'],
						'UF_TM_DAY' => $arRes['UF_TM_DAY'],
						'UF_TM_REPORT_TPL' => [],
						'UF_TM_ALLOWED_DELTA' => -1,
					];

					foreach ($this->UF_DEPARTMENT as $dpt)
					{
						$dptSet = CTimeMan::GetSectionSettings($dpt);

						if ($allSet['UF_TIMEMAN'] !== 'Y' && $dptSet['UF_TIMEMAN'])
						{
							$allSet['UF_TIMEMAN'] = $dptSet['UF_TIMEMAN'];
						}
						if ($dptSet['UF_TM_MAX_START'])
						{
							$allSet['UF_TM_MAX_START'] = min($dptSet['UF_TM_MAX_START'], $allSet['UF_TM_MAX_START']);
						}

						$allSet['UF_TM_MAX_START'] = min($dptSet['UF_TM_MAX_START'], $allSet['UF_TM_MAX_START']);
						$allSet['UF_TM_MIN_FINISH'] = max($dptSet['UF_TM_MIN_FINISH'], $allSet['UF_TM_MIN_FINISH']);
						$allSet['UF_TM_MIN_DURATION'] = max($dptSet['UF_TM_MIN_DURATION'], $allSet['UF_TM_MIN_DURATION']);

						if ($dptSet['UF_TM_REPORT_REQ'])
						{
							$allSet['UF_TM_REPORT_REQ'] = $dptSet['UF_TM_REPORT_REQ'];
						}

						if ((!is_array($allSet['UF_TM_REPORT_TPL']) || count($allSet['UF_TM_REPORT_TPL']) <= 0) && $dptSet['UF_TM_REPORT_TPL'])
						{
							$allSet['UF_TM_REPORT_TPL'] = $dptSet['UF_TM_REPORT_TPL'];
						}

						if ($dptSet['UF_TM_ALLOWED_DELTA'])
						{
							if ($allSet['UF_TM_ALLOWED_DELTA'] == -1 || $dptSet['UF_TM_ALLOWED_DELTA'] < $allSet['UF_TM_ALLOWED_DELTA'])
							{
								$allSet['UF_TM_ALLOWED_DELTA'] = $dptSet['UF_TM_ALLOWED_DELTA'];
							}
						}
					}

					//report fields
					$allSet["UF_REPORT_PERIOD"] = (!$allSet["UF_REPORT_PERIOD"] && $dptSet["UF_REPORT_PERIOD"]) ? $dptSet["UF_REPORT_PERIOD"] : $allSet["UF_REPORT_PERIOD"];
					$allSet["UF_TM_TIME"] = (!$allSet["UF_TM_TIME"] && $dptSet["UF_TM_TIME"]) ? $dptSet["UF_TM_TIME"] : $allSet["UF_TM_TIME"];
					$allSet["UF_TM_DAY"] = (!$allSet["UF_TM_DAY"] && $dptSet["UF_TM_DAY"]) ? $dptSet["UF_TM_DAY"] : $allSet["UF_TM_DAY"];
					$allSet["UF_TM_REPORT_DATE"] = (!$allSet["UF_TM_REPORT_DATE"] && $dptSet["UF_TM_REPORT_DATE"]) ? $dptSet["UF_TM_REPORT_DATE"] : $allSet["UF_TM_REPORT_DATE"];

					if ($arRes['UF_TM_ALLOWED_DELTA'] === '0')
					{
						unset($allSet['UF_TM_ALLOWED_DELTA']);
					}
					foreach ($allSet as $key => $value)
					{
						if (!$arRes[$key] || $arRes[$key] === '00:00')
						{
							$arRes[$key] = $value;
						}
					}

					if ($arRes['UF_TIMEMAN'] === 'N')
					{
						return ($arRes = ['UF_TIMEMAN' => false]);
					}
				}
				elseif ($arRes['UF_TIMEMAN'] !== 'Y')
				{
					// if user is not attached to company structure tm can be allowed only in his own profile
					return ($arRes = ['UF_TIMEMAN' => false]);
				}
			} //if ($cnt > 0)

			$arRes['UF_TIMEMAN'] = true; // it can be only Y|null at this moment
			$arRes['UF_TM_MAX_START'] = $arRes['UF_TM_MAX_START'];
			$arRes['UF_TM_MAX_START'] = $arRes['UF_TM_MAX_START'] > 0
				? $arRes['UF_TM_MAX_START']
				: COption::GetOptionInt('timeman', 'workday_max_start', 33300);
			$arRes['UF_TM_MIN_FINISH'] = $arRes['UF_TM_MIN_FINISH'];
			$arRes['UF_TM_MIN_FINISH'] = $arRes['UF_TM_MIN_FINISH'] > 0
				? $arRes['UF_TM_MIN_FINISH']
				: COption::GetOptionInt('timeman', 'workday_min_finish', 63900);
			$arRes['UF_TM_MIN_DURATION'] = $arRes['UF_TM_MIN_DURATION'];
			$arRes['UF_TM_MIN_DURATION'] = $arRes['UF_TM_MIN_DURATION'] > 0
				? $arRes['UF_TM_MIN_DURATION']
				: COption::GetOptionInt('timeman', 'workday_min_duration', 28800);
			$arRes['UF_TM_REPORT_REQ'] = $arRes['UF_TM_REPORT_REQ']
				? $arRes['UF_TM_REPORT_REQ']
				: COption::GetOptionString('timeman', 'workday_report_required', 'A');
			$arRes['UF_TM_REPORT_TPL'] = $arRes['UF_TM_REPORT_TPL']
				? $arRes['UF_TM_REPORT_TPL']
				: [];
			$arRes['UF_TM_ALLOWED_DELTA'] = $arRes['UF_TM_ALLOWED_DELTA'] > -1
				? $arRes['UF_TM_ALLOWED_DELTA']
				: COption::GetOptionInt('timeman', 'workday_allowed_delta', '900');
		}
		else
		{
			return ['UF_TIMEMAN' => false];
		}

		return $arRes;
	}

	public function ClearCache()
	{
		return $this->_GetLastData(true);
	}

	protected function clearFullReportCache()
	{
		global $CACHE_MANAGER;

		$cacheId = CUserReportFull::getInfoCacheId($this->USER_ID);
		$CACHE_MANAGER->Clean($cacheId, 'timeman_report_info');
	}

	public function isSocservEnabledByUser()
	{
		return CUserOptions::GetOption("socialservices", "user_socserv_enable", "N", $this->USER_ID) == 'Y';
	}

	protected function _cacheId()
	{
		if ($this->CACHE_ID)
		{
			return $this->CACHE_ID;
		}
		else
		{
			return ($this->CACHE_ID = 'TIMEMAN_USER_' . $this->USER_ID . '|' . FORMAT_DATETIME);
		}
	}

	protected function _GetLastData($clear = false)
	{
		global $CACHE_MANAGER;

		if ($clear)
		{
			CTimeManUser::$LAST_ENTRY[$this->USER_ID] = CTimeManEntry::GetLast($this->USER_ID);
			$CACHE_MANAGER->Clean($this->_cacheId(), 'b_timeman_entries');
		}
		else
		{
			if (!CTimeManUser::$LAST_ENTRY[$this->USER_ID])
			{
				if ($CACHE_MANAGER->Read(86400, $this->_cacheId(), 'b_timeman_entries'))
				{
					$DATA = $CACHE_MANAGER->Get($this->_cacheId());
				}
				else
				{
					$DATA = CTimeManEntry::GetLast($this->USER_ID);
					$CACHE_MANAGER->Set($this->_cacheId(), $DATA);
				}

				CTimeManUser::$LAST_ENTRY[$this->USER_ID] = $DATA;
			}
		}

		if (!empty(CTimeManUser::$LAST_ENTRY[$this->USER_ID]))
		{
			$lastPauseInfo = $this->getLastPauseInfo($this->USER_ID);
			if ($lastPauseInfo)
			{
				CTimeManUser::$LAST_ENTRY[$this->USER_ID]['LAST_PAUSE'] = $lastPauseInfo;
			}
			else
			{
				unset(CTimeManUser::$LAST_ENTRY[$this->USER_ID]['LAST_PAUSE']);
			}
		}

		return CTimeManUser::$LAST_ENTRY[$this->USER_ID];
	}

	protected function _ReopenGetActivity($entry_id)
	{
		$dbRes = CTimeManReport::GetList(
			['ID' => 'ASC'],
			['ENTRY_ID' => $entry_id, 'REPORT_TYPE' => 'ERR_OPEN', 'ACTIVE' => 'Y']
		);

		if ($arRes = $dbRes->Fetch())
		{
			return false;
		}
		else
		{
			return true;
		}
	}
}

?>