403Webshell
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/cvetdv.ru/bitrix/modules/bizproc/lib/Api/Service/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/lib/Api/Service/WorkflowStateService.php
<?php

namespace Bitrix\Bizproc\Api\Service;

use Bitrix\Bizproc\Api\Data\WorkflowStateService\WorkflowStateToGet;
use Bitrix\Bizproc\Api\Request\WorkflowAccessService\CanViewTimelineRequest;
use Bitrix\Bizproc\Api\Request\WorkflowStateService\GetAverageWorkflowDurationRequest;
use Bitrix\Bizproc\Api\Request\WorkflowStateService\GetExecutionTimeRequest;
use Bitrix\Bizproc\Api\Request\WorkflowStateService\GetEfficiencyDataRequest;
use Bitrix\Bizproc\Api\Request\WorkflowStateService\GetTimelineRequest;
use Bitrix\Bizproc\Api\Response\Error;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetAverageWorkflowDurationResponse;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetExecutionTimeResponse;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetEfficiencyDataResponse;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetFullFilledListResponse;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetListResponse;
use Bitrix\Bizproc\Api\Response\WorkflowStateService\GetTimelineResponse;
use Bitrix\Bizproc\Workflow\Entity\EO_WorkflowState_Collection;
use Bitrix\Bizproc\Workflow\Entity\WorkflowDurationStatTable;
use Bitrix\Bizproc\Workflow\Entity\WorkflowInstanceTable;
use Bitrix\Bizproc\Workflow\Entity\WorkflowStateTable;
use Bitrix\Bizproc\Workflow\Entity\WorkflowUserTable;
use Bitrix\Bizproc\Workflow\Task\EO_Task_Collection;
use Bitrix\Bizproc\Workflow\Task\TaskTable;
use Bitrix\Bizproc\Workflow\Timeline;
use Bitrix\Bizproc\Workflow\WorkflowState;
use Bitrix\Main\SystemException;
use Bitrix\Main\Type\DateTime;

class WorkflowStateService
{
	private const CONVERTER_VERSION = 2;

	private const THREE_DAYS_IN_SECONDS = 259200; // 3600 * 24 *3

	public function getList(WorkflowStateToGet $toGet): GetListResponse
	{
		$this->convertProcesses($toGet->getFilterUserId());

		$response = new GetListResponse();
		$responseCollection = new EO_WorkflowState_Collection();

		$query = WorkflowUserTable::query()
			->addSelect('WORKFLOW_ID')
			->addSelect('MODIFIED')
			->setFilter($toGet->getOrmFilter())
			->setOrder($toGet->getOrder())
			->setLimit($toGet->getLimit())
			->setOffset($toGet->getOffset())
			->countTotal($toGet->isCountingTotal())
		;
		$runtimeField = $toGet->getOrmRuntime();

		if ($runtimeField)
		{
			$query->registerRuntimeField($runtimeField);
		}

		$queryResult = $query->exec();

		if ($toGet->isCountingTotal())
		{
			$response->setTotalCount($queryResult->getCount());
		}
		$workflowStates = $queryResult->fetchAll();
		$ids = array_column($workflowStates, 'WORKFLOW_ID');
		$mods = array_column($workflowStates, 'MODIFIED');

		if ($ids)
		{
			$query = WorkflowStateTable::query()->setSelect($toGet->getSelect());
			if (count($ids) === 1)
			{
				$query->where('ID', '=', $ids[0]);
			}
			else
			{
				$query->whereIn('ID', $ids);
			}

			$collection = $query->exec()->fetchCollection();

			foreach ($ids as $k => $id)
			{
				$workflowState = $collection->getByPrimary($id);
				if ($workflowState)
				{
					$responseCollection->add($workflowState);
					$response->setUserModified($id, $mods[$k]);
					$workflowTasks = $this->getWorkflowTasks($workflowState, $toGet);
					if (isset($workflowTasks))
					{
						$response->setWorkflowTasks($id, $workflowTasks);
					}
				}
			}
		}

		return $response->setWorkflowStatesCollection($responseCollection);
	}

	private function getWorkflowTasks(WorkflowState $workflowState, WorkflowStateToGet $toGet): ?EO_Task_Collection
	{
		$taskFields = $toGet->getSelectTaskFields();
		if ($taskFields)
		{
			$activeTasksQuery = TaskTable::query()
				->setSelect($taskFields)
				->setFilter([
					'=WORKFLOW_ID' => $workflowState->getId(),
					'=TASK_USERS.USER_ID' => $toGet->getFilterUserId(),
					'=TASK_USERS.STATUS' => \CBPTaskUserStatus::Waiting,
				])
				->setOrder(['ID' => 'DESC'])
			;

			$taskLimit = $toGet->getSelectTaskLimit();
			if (isset($taskLimit))
			{
				$activeTasksQuery->setLimit($taskLimit);
			}

			$userActiveTasks = $activeTasksQuery->exec()->fetchCollection();

			$remainingTasksCount = null;
			if (isset($taskLimit))
			{
				$remainingTasksCount = $taskLimit - $userActiveTasks->count();
			}

			if (isset($remainingTasksCount) && $remainingTasksCount <= 0)
			{
				return $userActiveTasks;
			}

			$workflowTasksQuery = TaskTable::query()
				->setSelect($taskFields)
				->setFilter([
					'=WORKFLOW_ID' => $workflowState->getId(),
					[
						'LOGIC' => 'OR',
						'!=TASK_USERS.USER_ID' => $toGet->getFilterUserId(),
						'!=TASK_USERS.STATUS' => \CBPTaskUserStatus::Waiting,
					],
				])
				->setOrder(['ID' => 'DESC'])
			;

			if (isset($remainingTasksCount))
			{
				$workflowTasksQuery->setLimit($remainingTasksCount);
			}

			return $userActiveTasks->merge($workflowTasksQuery->exec()->fetchCollection());
		}

		return null;
	}

	public function getFullFilledList(WorkflowStateToGet $toGet): GetFullFilledListResponse
	{
		$response = new GetFullFilledListResponse();

		$toGet->setSelectAllFields();
		$getListResult = $this->getList($toGet);
		$collection = $getListResult->getWorkflowStatesCollection();
		if (!$collection)
		{
			return $response->setWorkflowStatesList([]);
		}

		$fullFilledList = [];
		$userIds = [];
		foreach ($collection as $stateElement)
		{
			$tasksInfo = $stateElement->getTasksInfo();
			foreach ($tasksInfo as $task)
			{
				if (!empty($task['TASK_USERS']))
				{
					foreach ($task['TASK_USERS'] as $row)
					{
						$userIds[$row['USER_ID']] = true;
						$userIds[$row['ORIGINAL_USER_ID']] = true;
					}
				}
			}

			$fullFilledList[] = [
				'ID' => $stateElement->getId(),
				'STARTED' => $stateElement->getStarted(),
				'MODIFIED' => $stateElement->getModified(),
				'STATE_INFO' => $stateElement->getStateInfo(),
				'DOCUMENT_INFO' => $this->getDocumentInfo($stateElement->getComplexDocumentId()),
				'STARTED_USER_INFO' => [
					'ID' => $stateElement->getStartedBy(),
				],
				'TASKS_INFO' => $tasksInfo,
				'WORKFLOW_TEMPLATE_ID' => $stateElement->getWorkflowTemplateId(),
				'TEMPLATE_NAME' => $stateElement->getTemplate()?->getName(),
				'META' => $stateElement->getMeta()?->collectValues() ?? [],
			];

			$userIds[$stateElement->getStartedBy()] = true;
		}

		return (
			$response
				->setWorkflowStatesList($fullFilledList)
				->setMembersInfo($this->getMembersInfo(array_keys($userIds)))
		);
	}

	public function getTimeline(GetTimelineRequest $request): GetTimelineResponse
	{
		if (!$request->workflowId)
		{
			return GetTimelineResponse::createError(Error::fromCode(Error::WORKFLOW_NOT_FOUND));
		}

		$isAdmin = (new \CBPWorkflowTemplateUser($request->userId))->isAdmin();

		if (!$isAdmin)
		{
			$accessService = new WorkflowAccessService();
			$accessRequest = new CanViewTimelineRequest(workflowId: $request->workflowId, userId: $request->userId);

			$accessResponse = $accessService->canViewTimeline($accessRequest);
			if (!$accessResponse->isSuccess())
			{
				return (new GetTimelineResponse())->addErrors($accessResponse->getErrors());
			}
		}

		$timeline = Timeline::createByWorkflowId($request->workflowId);

		if (!$timeline)
		{
			return GetTimelineResponse::createError(Error::fromCode(Error::WORKFLOW_NOT_FOUND));
		}

		return GetTimelineResponse::createOk(['timeline' => $timeline->setUserId($request->userId)]);
	}

	private function getDocumentInfo(array $complexDocumentId): array
	{
		$documentService = \CBPRuntime::getRuntime()->getDocumentService();

		$key = null;
		try
		{
			$complexDocumentType = $documentService->getDocumentType($complexDocumentId);
			if ($complexDocumentType)
			{
				$key = $complexDocumentId[0] . '@' . $complexDocumentId[1] . '@' . $complexDocumentType[2];
			}
		}
		catch (SystemException | \Exception $exception)
		{
			$complexDocumentType = null;
		}

		static $cache = [];
		if ($key && !isset($cache[$key]))
		{
			$cache[$key] = $documentService->getDocumentTypeCaption($complexDocumentType);
		}
		$typeCaption = $key ? $cache[$key] : '';

		return [
			'COMPLEX_ID' => $complexDocumentId,
			'COMPLEX_TYPE' => $complexDocumentType,
			'NAME' => $documentService->getDocumentName($complexDocumentId),
			'TYPE_CAPTION' => $typeCaption,
		];
	}

	private function getMembersInfo(array $ids): array
	{
		if (empty($ids))
		{
			return [];
		}

		$userFields = ['ID', 'NAME', 'SECOND_NAME', 'LAST_NAME', 'LOGIN', 'TITLE', 'EMAIL', 'PERSONAL_PHOTO'];

		$users = \CUser::GetList(
			'id',
			'asc',
			['ID' => implode('|', $ids)],
			['FIELDS' => $userFields]
		);

		$info = [];
		while ($user = $users->Fetch())
		{
			$fullName = \CUser::FormatName(\CSite::GetNameFormat(false), $user, true, false);
			$personalPhoto = (int)$user['PERSONAL_PHOTO'];

			$info[] = [
				'ID' => (int)($user['ID'] ?? 0),
				'FULL_NAME' => $fullName,
				'PERSONAL_PHOTO' => $personalPhoto,
			];
		}

		return $info;
	}

	private function convertProcesses(int $userId)
	{
		if (empty($userId))
		{
			return;
		}

		$converterVersion = \CUserOptions::getOption(
			'bizproc',
			'processes_converted',
			0,
			$userId
		);

		if ($converterVersion === self::CONVERTER_VERSION)
		{
			return;
		}

		WorkflowUserTable::convertUserProcesses($userId);

		\CUserOptions::setOption(
			'bizproc',
			'processes_converted',
			self::CONVERTER_VERSION,
			false,
			$userId
		);
	}

	public function getAverageWorkflowDuration(
		GetAverageWorkflowDurationRequest $request
	): GetAverageWorkflowDurationResponse
	{
		// rights? canUserOperateDocumentType(CBPCanUserOperateOperation::ReadDocument, ...)

		$response = new GetAverageWorkflowDurationResponse();
		if ($request->templateId <= 0)
		{
			$response->addError(new Error('incorrect template id'));
		}

		if ($response->isSuccess())
		{
			$averageDuration = WorkflowDurationStatTable::getAverageDurationByTemplateId($request->templateId);
			if ($averageDuration !== null)
			{
				$response->setAverageDuration($averageDuration);
			}
		}

		return $response;
	}

	public function getExecutionTime(GetExecutionTimeRequest $request): GetExecutionTimeResponse
	{
		$response = new GetExecutionTimeResponse();

		if ($request->workflowStarted === null)
		{
			return $response->addError(new Error('incorrect workflowStarted'));
		}

		$startedTimestamp = $request->workflowStarted?->getTimestamp();
		if (WorkflowInstanceTable::exists($request->workflowId))
		{
			$currentTimestamp = (new DateTime())->getTimestamp();

			return $response->setExecutionTime($currentTimestamp - $startedTimestamp);
		}

		$modifiedTimestamp = $request->workflowModified->getTimestamp();

		return $response->setExecutionTime($modifiedTimestamp - $startedTimestamp);
	}

	public function getCompletedWorkflowEfficiency(string $workflowId): GetEfficiencyDataResponse
	{
		$workflow = WorkflowStateTable::query()
			->setSelect([
				'ID',
				'MODIFIED',
				'STARTED',
				'WORKFLOW_TEMPLATE_ID',
			])
			->setFilter([
				'=ID' => $workflowId,
				'=STATE' => 'Completed',
			])
			->exec()
			->fetchObject()
		;

		if ($workflow)
		{
			$executionTime = $this->getExecutionTime(
				new GetExecutionTimeRequest(
					workflowId: $workflow->getId(),
					workflowStarted: $workflow->getStarted(),
					workflowModified: $workflow->getModified()
				)
			)->getRoundedExecutionTime();
			$averageDuration = $this->getAverageWorkflowDuration(
				new GetAverageWorkflowDurationRequest(
					templateId: $workflow->getWorkflowTemplateId(),
				)
			)->getRoundedAverageDuration();
			$request = new GetEfficiencyDataRequest(
				executionTime: $executionTime,
				averageDuration: $averageDuration ?? $executionTime
			);

			return $this->getEfficiencyData($request);
		}

		$response = new GetEfficiencyDataResponse();
		$response->addError(new Error('WorkflowState not found'));

		return $response;
	}

	public function getEfficiencyData(GetEfficiencyDataRequest $request): GetEfficiencyDataResponse
	{
		$response = new GetEfficiencyDataResponse();

		$averageDuration = $request->averageDuration;
		$currentDuration = $request->executionTime;

		$efficiency = 'stopped';
		if (null === $averageDuration)
		{
			$efficiency = 'first';
		}
		elseif ($currentDuration <= $averageDuration)
		{
			$efficiency = 'fast';
		}
		elseif ($currentDuration <= ($averageDuration + self::THREE_DAYS_IN_SECONDS))
		{
			$efficiency = 'slow';
		}

		$response->setAverageDuration($request->averageDuration);
		$response->setExecutionTime($request->executionTime);
		$response->setEfficiency($efficiency);

		return $response;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit