1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
WorkflowFacesService.php
См. документацию.
1<?php
2
3namespace Bitrix\Bizproc\Api\Service;
4
5use Bitrix\Bizproc\Api\Data\WorkflowFacesService\ProgressBox;
6use Bitrix\Bizproc\Api\Data\WorkflowFacesService\Step;
7use Bitrix\Bizproc\Api\Data\WorkflowFacesService\StepDurations;
8use Bitrix\Bizproc\Api\Enum\WorkflowFacesService\WorkflowFacesStep;
9use Bitrix\Bizproc\Api\Request\WorkflowAccessService\CanViewFacesRequest;
10use Bitrix\Bizproc\Api\Request\WorkflowFacesService\GetDataRequest;
11use Bitrix\Bizproc\Api\Response\Error;
12use Bitrix\Bizproc\Api\Response\WorkflowFacesService\GetDataResponse;
13use Bitrix\Bizproc\Api\Response\WorkflowFacesService\GetDataByStepsResponse;
14use Bitrix\Bizproc\UI\Helpers\DurationFormatter;
15use Bitrix\Bizproc\Workflow\Task\TaskTable;
16use Bitrix\Bizproc\Workflow\WorkflowState;
17use Bitrix\Bizproc\WorkflowStateTable;
18use Bitrix\Main\Type\Collection;
19use Bitrix\Main\Type\DateTime;
20
22{
23 public const WORKFLOW_DOES_NOT_EXIST_ERROR_CODE = 'WORKFLOW_DOES_NOT_EXIST';
24 private const WORKFLOW_FIELDS = ['ID', 'STARTED', 'STARTED_BY', 'MODIFIED', 'META.START_DURATION'];
25 private const TASK_FIELDS = ['ID', 'WORKFLOW_ID', 'MODIFIED', 'STATUS', 'CREATED_DATE'];
26 private const COMPLETED_TASK_LIMIT = 2;
27 private const RUNNING_TASK_LIMIT = 3;
28
29 private WorkflowAccessService $accessService;
30
31 public function __construct(
32 WorkflowAccessService $accessService
33 )
34 {
35 $this->accessService = $accessService;
36 }
37
39 {
40 if (empty($request->workflowId))
41 {
42 return GetDataResponse::createError(new Error('empty workflowId')); // todo: Loc
43 }
44
45 if (!$request->skipAccessCheck)
46 {
47 $canViewResponse = $this->accessService->canViewFaces(
49 $request->workflowId,
50 max($request->accessUserId ?? 0, 0),
51 max($request->currentUserId ?? 0, 0),
52 )
53 );
54 if (!$canViewResponse->isSuccess())
55 {
56 return GetDataResponse::createError($this->accessService::getViewAccessDeniedError());
57 }
58 }
59
60 $workflow =
61 WorkflowStateTable::query()
62 ->setSelect(self::WORKFLOW_FIELDS)
63 ->where('ID', $request->workflowId)
64 ->exec()
65 ->fetchObject()
66 ;
67 if (!$workflow)
68 {
69 return GetDataResponse::createError(
70 new Error(
71 'workflow does not exist', // todo: Loc
72 self::WORKFLOW_DOES_NOT_EXIST_ERROR_CODE
73 )
74 );
75 }
76
79 ->setAuthorId($workflow->getStartedBy() ?? 0)
80 ->setWorkflowStarted($workflow->getStarted())
81 ;
82
83 $response->setWorkflowIsFinished(\CBPHelper::isWorkflowFinished($request->workflowId));
84
85 $response->setCompletedTasksCount(
86 TaskTable::getCount([
87 '=WORKFLOW_ID' => $request->workflowId,
88 '!=STATUS' => \CBPTaskStatus::Running,
89 ])
90 );
91
92 $completedTasks =
93 $response->getCompletedTasksCount() > 0
94 ? $this->getCompletedTasks($request->workflowId)
95 : []
96 ;
97
98 $runningTasks =
99 $response->getWorkflowIsFinished()
100 ? []
101 : $this->getRunningTasks($request->workflowId, $request->runningTaskId)
102 ;
103
104 $response->setTasksUserIds($this->getTasksUserIds($completedTasks, $runningTasks, $request->taskUsersLimit));
105
106 $runningTask = current($runningTasks);
107 if ($runningTask)
108 {
109 $response->setRunningTask($runningTask);
110 }
111
112 $completedTask = current($completedTasks);
113 $doneTask = false;
114 if ($response->getWorkflowIsFinished() && count($completedTasks) > 1)
115 {
116 $doneTask = $completedTask;
117 $completedTask = next($completedTasks);
118 }
119 if ($doneTask)
120 {
121 $response->setDoneTask($doneTask);
122 }
123 if ($completedTask)
124 {
125 $response->setCompletedTask($completedTask);
126 }
127
128 $authorDuration = $workflow->getMeta()?->getStartDuration() ?? 0;
129 $runningDuration = (
130 $response->getWorkflowIsFinished()
131 ? 0
132 : $this->getRunningDuration($workflow, $runningTask ?: null, $completedTask ?: null)
133 );
134 $completedDuration = $this->getCompletedDuration($workflow, $completedTask ?: null);
135 $doneDuration = (
136 $response->getWorkflowIsFinished()
137 ? $this->getDoneDuration($workflow, $completedTask ?: null)
138 : 0
139 );
140
141 $response->setDurations(new StepDurations($authorDuration, $runningDuration, $completedDuration, $doneDuration));
142
143 return $response;
144 }
145
146 private function getCompletedTasks(string $workflowId): array
147 {
148 $completedTasksIterator = \CBPTaskService::getList(
149 ['MODIFIED' => 'DESC'],
150 ['WORKFLOW_ID' => $workflowId, '!STATUS' => \CBPTaskStatus::Running],
151 false,
152 ['nTopCount' => self::COMPLETED_TASK_LIMIT],
153 self::TASK_FIELDS,
154 );
155 $completedTasks = [];
156 while ($task = $completedTasksIterator->getNext())
157 {
158 $completedTasks[$task['ID']] = $task;
159 }
160
161 return $completedTasks;
162 }
163
164 private function getRunningTasks(string $workflowId, ?int $runningTaskId = null): array
165 {
166 $isTaskIdCorrect = $runningTaskId && $runningTaskId > 0;
167
168 $runningTask = null;
169 $hasTasks = false;
170
171 $runningTasksIterator = \CBPTaskService::getList(
172 ['ID' => 'ASC'],
173 ['WORKFLOW_ID' => $workflowId, 'STATUS' => \CBPTaskStatus::Running],
174 false,
175 ['nTopCount' => self::RUNNING_TASK_LIMIT],
176 self::TASK_FIELDS,
177 );
178
179 $runningTasks = [];
180 while ($task = $runningTasksIterator->getNext())
181 {
182 $hasTasks = true;
183 if ($isTaskIdCorrect && ($runningTaskId === (int)$task['ID']))
184 {
185 $runningTask = $task;
186
187 continue;
188 }
189
190 $runningTasks[$task['ID']] = $task;
191 }
192
193 // parallel or already completed taskId
194 if ($hasTasks && $isTaskIdCorrect && !$runningTask)
195 {
197 [], ['ID' => $runningTaskId], false, false, self::TASK_FIELDS
198 );
199 $task = $iterator->getNext();
200 if ($task && (int)$task['STATUS'] === \CBPTaskStatus::Running)
201 {
202 $runningTask = $task;
203 }
204 }
205
206 if ($runningTask)
207 {
208 $runningTasks = [$runningTask['ID'] => $runningTask] + $runningTasks; // merge, where $runningTaskId is first task
209 }
210
211 return $runningTasks;
212 }
213
214 private function getTasksUserIds(array $completedTasks, array $runningTasks, int $usersLimit): array
215 {
216 $taskIds = array_merge(array_keys($runningTasks), array_keys($completedTasks));
217 $taskUsers = $taskIds ? \CBPTaskService::getTaskUsers($taskIds) : [];
218
219 $taskUserIdsMap = [];
220 foreach ($taskUsers as $taskId => $users)
221 {
222 $ids = array_slice(array_column($users, 'USER_ID'), 0, $usersLimit);
223 Collection::normalizeArrayValuesByInt($ids, false);
224 $taskUserIdsMap[$taskId] = $ids;
225 }
226
227 return $taskUserIdsMap;
228 }
229
230 private function getRunningDuration(WorkflowState $workflow, ?array $runningTask, ?array $completedTask): int
231 {
232 $currentTimestamp = time();
233 if ($runningTask)
234 {
235 $startTaskTimestamp = $this->getDateTimeTimestamp($runningTask['CREATED_DATE'] ?? null);
236 if (!$startTaskTimestamp)
237 {
238 $startTaskTimestamp = $this->getDateTimeTimestamp($runningTask['MODIFIED'] ?? null);
239 }
240
241 return $startTaskTimestamp ? $currentTimestamp - $startTaskTimestamp : 0;
242 }
243
244 if ($completedTask)
245 {
246 $finishTaskTimestamp = $this->getDateTimeTimestamp($completedTask['MODIFIED'] ?? null);
247
248 return $finishTaskTimestamp ? $currentTimestamp - $finishTaskTimestamp : 0;
249 }
250
251 $startWorkflowTimestamp = $this->getDateTimeTimestamp($workflow->getStarted());
252
253 return $startWorkflowTimestamp ? ($currentTimestamp - $startWorkflowTimestamp) : 0;
254 }
255
256 private function getCompletedDuration(WorkflowState $workflow, ?array $completedTask): int
257 {
258 $startWorkflowTimestamp = $this->getDateTimeTimestamp($workflow->getStarted());
259
260 $finishTaskTimestamp = (
261 $completedTask ? $this->getDateTimeTimestamp($completedTask['MODIFIED'] ?? null) : null
262 );
263
264 return $startWorkflowTimestamp && $finishTaskTimestamp ? ($finishTaskTimestamp - $startWorkflowTimestamp) : 0;
265 }
266
267 private function getDoneDuration(WorkflowState $workflow, ?array $completedTask): int
268 {
269 $finishWorkflowTimestamp = $this->getDateTimeTimestamp($workflow->getModified());
270 if ($completedTask)
271 {
272 $finishTaskTimestamp = $this->getDateTimeTimestamp($completedTask['MODIFIED'] ?? null);
273
274 return (
275 $finishWorkflowTimestamp && $finishTaskTimestamp && ($finishWorkflowTimestamp > $finishTaskTimestamp)
276 ? $finishWorkflowTimestamp - $finishTaskTimestamp
277 : 0
278 );
279 }
280
281 $startWorkflowTimestamp = $this->getDateTimeTimestamp($workflow->getStarted());
282
283 return (
284 $finishWorkflowTimestamp && $startWorkflowTimestamp
285 ? ($finishWorkflowTimestamp - $startWorkflowTimestamp)
286 : 0
287 );
288 }
289
290 private function getDateTimeTimestamp($datetime): ?int
291 {
292 if ($datetime instanceof DateTime)
293 {
294 return $datetime->getTimestamp();
295 }
296
297 if (is_string($datetime) && DateTime::isCorrect($datetime))
298 {
299 return DateTime::createFromUserTime($datetime)->getTimestamp();
300 }
301
302 return null;
303 }
304
305 public function getDataBySteps(GetDataRequest $request): GetDataByStepsResponse
306 {
307 $response = new GetDataByStepsResponse();
308
309 $data = $this->getData($request);
310 if (!$data->isSuccess())
311 {
312 return $response->addErrors($data->getErrors());
313 }
314
316 ->setIsWorkflowFinished($data->getWorkflowIsFinished())
317 ->setAuthorStep($this->createAuthorStep($data))
318 ->setFirstStep($response->getAuthorStep())
319 ;
320
321 $completedTasksCount = $data->getCompletedTasksCount();
322 if ($completedTasksCount > 0)
323 {
324 $response->setProgressBox(
325 new ProgressBox(
326 ProgressBox::calculateProgressTasksCount($completedTasksCount, $data->getWorkflowIsFinished())
327 )
328 );
329 }
330
331 if ($data->getCompletedTask())
332 {
334 ->setCompletedStep($this->createCompletedStep($data))
335 ->setSecondStep($response->getCompletedStep())
336 ;
337 }
338
339 if ($data->getWorkflowIsFinished())
340 {
341 $response->setDoneStep($this->createDoneStep($data));
342 }
343 else
344 {
345 $response->setRunningStep($this->createRunningStep($data));
346 }
347
349 $finalStep = $response->getDoneStep() ?? $response->getRunningStep();
350 if ($response->getSecondStep())
351 {
352 $response->setThirdStep($finalStep);
353 }
354 else
355 {
356 $response->setSecondStep($finalStep);
357 }
358
359 $response->setTimeStep($this->createTimeStep($data));
360
361 return $response;
362 }
363
364 private function createAuthorStep(GetDataResponse $data): Step
365 {
366 $authorId = $data->getAuthorId();
367 $duration = $data->getDurations();
368
369 return (
370 (new Step(WorkflowFacesStep::Author))
371 ->setAvatars($authorId > 0 ? [$authorId] : [])
372 ->setDuration((int)($duration?->getRoundedAuthorDuration()))
373 );
374 }
375
376 private function createCompletedStep(GetDataResponse $data): Step
377 {
378 $completedTask = $data->getCompletedTask();
379 $completedTaskId = (int)$completedTask['ID'];
380 $duration = $data->getDurations();
381
382 return (
383 (new Step(WorkflowFacesStep::Completed))
384 ->setAvatars($data->getTaskUserIds($completedTaskId))
385 ->setDuration((int)($duration?->getRoundedCompletedDuration()))
386 ->setSuccess($data->isCompletedTaskStatusSuccess())
387 ->setTaskId($completedTaskId)
388 );
389 }
390
391 private function createDoneStep(GetDataResponse $data): Step
392 {
393 $doneTask = $data->getDoneTask();
394 $doneTaskId = $doneTask ? (int)$doneTask['ID'] : null;
395 $duration = $data->getDurations();
396
397 return (
398 (new Step(WorkflowFacesStep::Done))
399 ->setAvatars($doneTaskId ? $data->getTaskUserIds($doneTaskId) : [])
400 ->setDuration((int)($duration?->getRoundedDoneDuration()))
401 ->setSuccess(!$doneTask || $data->isDoneTaskStatusSuccess())
402 ->setTaskId($doneTaskId ?: 0)
403 );
404 }
405
406 private function createRunningStep(GetDataResponse $data): Step
407 {
408 $runningTask = $data->getRunningTask();
409 $runningTaskId = $runningTask ? $runningTask['ID'] : null;
410 $duration = $data->getDurations();
411
412 return (
413 (new Step(WorkflowFacesStep::Running))
414 ->setAvatars($runningTaskId ? $data->getTaskUserIds($runningTaskId) : [])
415 ->setDuration((int)($duration?->getRoundedRunningDuration()))
416 ->setTaskId($runningTaskId ? : 0)
417 );
418 }
419
420 private function createTimeStep(GetDataResponse $data): Step
421 {
422 if ($data->getWorkflowIsFinished())
423 {
424 $durations = $data->getDurations();
425 $totalDuration = (
426 ($durations?->getRoundedAuthorDuration() ?? 0)
427 + ($durations?->getRoundedCompletedDuration() ?? 0)
428 + ($durations?->getRoundedDoneDuration() ?? 0)
429 );
430
431 return (
432 (new Step(WorkflowFacesStep::TimeFinal))
433 ->setDuration(
434 DurationFormatter::roundTimeInSeconds($totalDuration)
435 )
436 );
437 }
438
439 $timestamp = $data->getWorkflowStarted()?->getTimestamp();
440
441 return (
442 (new Step(WorkflowFacesStep::TimeInWork))
443 ->setDuration(
444 DurationFormatter::roundTimeInSeconds($timestamp ? time() - $timestamp : 0)
445 )
446 );
447 }
448
449 public static function getStepById(string $id): ?Step
450 {
451 $step = WorkflowFacesStep::tryFrom($id);
452
453 return $step ? new Step($step) : null;
454 }
455}
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
__construct(WorkflowAccessService $accessService)
Определения WorkflowFacesService.php:31
getData(GetDataRequest $request)
Определения WorkflowFacesService.php:38
Определения error.php:15
static getList($arOrder=array("ID"=> "DESC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения taskservice.php:831
const Running
Определения constants.php:258
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$response
Определения result.php:21
$iterator
Определения yandex_run.php:610