1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
workflow.php
См. документацию.
1<?php
2
4
9{
10 private bool $isNew = false;
11 private bool $isAbandoned = false;
12 private string $instanceId;
13
16
18
19 protected array $activitiesQueue = [];
20 protected array $eventsQueue = [];
21
22 private array $activitiesNamesMap = [];
23
24 /************************ PROPERTIES *******************************/
25
26 public function getInstanceId()
27 {
28 return $this->instanceId;
29 }
30
34 public function getRuntime()
35 {
36 return $this->runtime;
37 }
38
40 {
42 }
43
44 private function getWorkflowStatus()
45 {
46 return $this->rootActivity->getWorkflowStatus();
47 }
48
49 protected function setWorkflowStatus($newStatus)
50 {
51 $this->rootActivity->setWorkflowStatus($newStatus);
52 $this->getRuntime()->onWorkflowStatusChanged($this->getInstanceId(), $newStatus);
53 $this->syncStatus($newStatus);
54 }
55
56 public function getService($name)
57 {
58 return $this->runtime->getService($name);
59 }
60
61 public function getDocumentId()
62 {
63 return $this->rootActivity->getDocumentId();
64 }
65
66 public function getDocumentType()
67 {
68 return $this->rootActivity->getDocumentType();
69 }
70
71 public function getTemplateId(): int
72 {
73 return (int)$this->rootActivity->getWorkflowTemplateId();
74 }
75
76 public function getStartedBy(): ?int
77 {
78 $startedBy = (int)CBPHelper::stripUserPrefix($this->rootActivity->{\CBPDocument::PARAM_TAGRET_USER});
79
80 return $startedBy ?: null;
81 }
82
84 {
85 return $this->persister;
86 }
87
88 /************************ CONSTRUCTORS ****************************************************/
89
96 public function __construct($instanceId, CBPRuntime $runtime)
97 {
98 if (!$instanceId)
99 {
100 throw new Exception("instanceId");
101 }
102
103 $this->instanceId = $instanceId;
104 $this->runtime = $runtime;
105 $this->persister = CBPWorkflowPersister::GetPersister();
106 }
107
112 public function __sleep()
113 {
114 return [];
115 }
116
117 /************************ CREATE / LOAD WORKFLOW ****************************************/
118
119 public function initialize(
121 $documentId,
122 $workflowParameters = [],
123 $workflowVariablesTypes = [],
124 $workflowParametersTypes = [],
125 $workflowTemplateId = 0
126 )
127 {
128 $this->rootActivity = $rootActivity;
129 $rootActivity->setWorkflow($this);
130 if (method_exists($rootActivity, 'setWorkflowTemplateId'))
131 {
132 $rootActivity->setWorkflowTemplateId($workflowTemplateId);
133 }
134
135 if (method_exists($rootActivity, 'setTemplateUserId'))
136 {
137 $rootActivity->setTemplateUserId(
138 CBPWorkflowTemplateLoader::getTemplateUserId($workflowTemplateId)
139 );
140 }
141
142 $arDocumentId = CBPHelper::parseDocumentId($documentId);
143
144 $rootActivity->setDocumentId($arDocumentId);
145
146 $documentService = $this->getService("DocumentService");
147 $documentType = $workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]
148 ?? $documentService->getDocumentType($arDocumentId)
149 ;
150
151 unset($workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]);
152
153 if ($documentType !== null)
154 {
155 $rootActivity->setDocumentType($documentType);
156 $rootActivity->setFieldTypes($documentService->getDocumentFieldTypes($documentType));
157 }
158
159 $rootActivity->setProperties($workflowParameters);
160
161 $rootActivity->setVariablesTypes($workflowVariablesTypes);
162 if (is_array($workflowVariablesTypes))
163 {
164 foreach ($workflowVariablesTypes as $k => $v)
165 {
166 $variableValue = $v["Default"] ?? null;
167 if ($documentType && $fieldTypeObject = $documentService->getFieldTypeObject($documentType, $v))
168 {
169 $fieldTypeObject->setDocumentId($arDocumentId);
170 $variableValue = $fieldTypeObject->internalizeValue('Variable', $variableValue);
171 }
172
173 //set defaults on start
174 $rootActivity->setVariable($k, $variableValue);
175 }
176 }
177
178 $rootActivity->setPropertiesTypes($workflowParametersTypes);
179 }
180
182 {
183 $this->rootActivity = $rootActivity;
184 $rootActivity->setWorkflow($this);
185
186 switch ($this->getWorkflowStatus())
187 {
190 throw new Exception("InvalidAttemptToLoad");
191 }
192 }
193
194 /************************ EXECUTE WORKFLOW ************************************************/
195
200 public function start(): void
201 {
202 if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Created)
203 {
204 throw new Exception("CanNotStartInstanceTwice");
205 }
206
207 $this->isNew = true;
208 $this->run();
209 }
210
215 public function resume(): void
216 {
217 if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Suspended)
218 {
219 throw new Exception("CanNotResumeInstance");
220 }
221
222 $this->run();
223 }
224
225 private function run(): void
226 {
227 try
228 {
229 $this->setWorkflowStatus(CBPWorkflowStatus::Running);
230 if ($this->isNew)
231 {
232 $this->rootActivity->setReadOnlyData(
233 $this->rootActivity->pullProperties()
234 );
235
236 $this->initializeActivity($this->rootActivity);
237 $this->executeActivity($this->rootActivity);
238 }
239
240 $this->runQueue();
241 }
242 catch (Exception $e)
243 {
244 $this->terminate($e);
245
246 throw $e;
247 }
248
249 if ($this->rootActivity->executionStatus === CBPActivityExecutionStatus::Closed)
250 {
251 $this->setWorkflowStatus(CBPWorkflowStatus::Completed);
252 }
253 elseif ($this->getWorkflowStatus() === CBPWorkflowStatus::Running)
254 {
255 $this->setWorkflowStatus(CBPWorkflowStatus::Suspended);
256 }
257
258 $this->persister->saveWorkflow($this->rootActivity, true);
259 }
260
261 public function isNew()
262 {
263 return $this->isNew;
264 }
265
266 public function abandon(): void
267 {
268 $this->isAbandoned = true;
269 }
270
271 public function isAbandoned(): bool
272 {
273 return $this->isAbandoned;
274 }
275
276 public function isFinished(): bool
277 {
278 if ($this->isAbandoned())
279 {
280 return true;
281 }
282
283 return CBPWorkflowStatus::isFinished((int)$this->getWorkflowStatus());
284 }
285
286 /********************** EXTERNAL EVENTS **************************************************************/
287
294 public function sendExternalEvent(string $eventName, array $eventParameters = []): void
295 {
296 $this->addEventToQueue($eventName, $eventParameters);
297
298 if ($this->getWorkflowStatus() !== CBPWorkflowStatus::Running)
299 {
300 $this->resume();
301 }
302 }
303
304 /*********************** SEARCH ACTIVITY BY NAME ****************************************************/
305
306 private function fillNameActivityMapInternal(CBPActivity $activity)
307 {
308 $this->activitiesNamesMap[$activity->getName()] = $activity;
309
310 if ($activity instanceof \CBPCompositeActivity)
311 {
312 $arSubActivities = $activity->collectNestedActivities();
313 foreach ($arSubActivities as $subActivity)
314 {
315 $this->fillNameActivityMapInternal($subActivity);
316 }
317 }
318 }
319
320 private function fillNameActivityMap()
321 {
322 if (!is_array($this->activitiesNamesMap))
323 {
324 $this->activitiesNamesMap = [];
325 }
326
327 if (count($this->activitiesNamesMap) > 0)
328 {
329 return;
330 }
331
332 $this->fillNameActivityMapInternal($this->rootActivity);
333 }
334
341 public function getActivityByName($activityName)
342 {
343 if ($activityName == '')
344 {
345 throw new Exception('activityName');
346 }
347
348 $activity = null;
349
350 $this->fillNameActivityMap();
351
352 if (array_key_exists($activityName, $this->activitiesNamesMap))
353 {
354 $activity = $this->activitiesNamesMap[$activityName];
355 }
356
357 return $activity;
358 }
359
360 /************************ ACTIVITY EXECUTION *************************************************/
361
368 {
369 if ($activity->executionStatus !== CBPActivityExecutionStatus::Initialized)
370 {
371 throw new Exception("InvalidInitializingState");
372 }
373
374 $activity->initialize();
375 }
376
383 public function executeActivity(CBPActivity $activity, array $eventParameters = [])
384 {
385 if ($activity->executionStatus !== CBPActivityExecutionStatus::Initialized)
386 {
387 throw new Exception("InvalidExecutionState");
388 }
389
390 $activity->setStatus(CBPActivityExecutionStatus::Executing, $eventParameters);
391 $this->addItemToQueue([$activity, CBPActivityExecutorOperationType::Execute]);
392 }
393
400 public function closeActivity(CBPActivity $activity, $arEventParameters = [])
401 {
402 switch ($activity->executionStatus)
403 {
405 $activity->markCompleted($arEventParameters);
406 return;
407
409 $activity->markCanceled($arEventParameters);
410 return;
411
413 return;
414
416 $activity->markFaulted($arEventParameters);
417 return;
418 }
419
420 throw new Exception("InvalidClosingState");
421 }
422
429 public function cancelActivity(CBPActivity $activity, $arEventParameters = [])
430 {
431 if ($activity->executionStatus !== CBPActivityExecutionStatus::Executing)
432 {
433 throw new Exception("InvalidCancelingState");
434 }
435
436 $activity->setStatus(CBPActivityExecutionStatus::Canceling, $arEventParameters);
437 $this->addItemToQueue(array($activity, CBPActivityExecutorOperationType::Cancel));
438 }
439
440 public function faultActivity(CBPActivity $activity, Exception $e, $arEventParameters = [])
441 {
442 if ($activity->executionStatus === CBPActivityExecutionStatus::Closed)
443 {
444 if ($activity->parent === null)
445 {
446 $this->Terminate($e);
447 }
448 else
449 {
450 $this->FaultActivity($activity->parent, $e, $arEventParameters);
451 }
452 }
453 else
454 {
456 $this->addItemToQueue(array($activity, CBPActivityExecutorOperationType::HandleFault, $e));
457 }
458 }
459
460 /************************ ACTIVITIES QUEUE ***********************************************/
461
462 private function addItemToQueue($item)
463 {
464 $this->activitiesQueue[] = $item;
465 }
466
467 protected function runQueue()
468 {
469 $canRun = $this->runStep();
470
471 while ($canRun)
472 {
473 $canRun = $this->runStep();
474 }
475 }
476
477 protected function runStep(): bool
478 {
479 if (empty($this->activitiesQueue))
480 {
481 $this->ProcessQueuedEvents();
482 }
483
484 $item = array_shift($this->activitiesQueue);
485 if ($item === null)
486 {
487 return false;
488 }
489
490 try
491 {
492 $this->runQueuedItem($item[0], $item[1], (count($item) > 2 ? $item[2] : null));
493 }
494 catch (Exception $e)
495 {
496 $this->faultActivity($item[0], $e);
497
498 if ($this->getWorkflowStatus() === CBPWorkflowStatus::Terminated)
499 {
500 return false;
501 }
502 }
503
504 return true;
505 }
506
510 private function runQueuedItem(CBPActivity $activity, $activityOperation, Exception $exception = null): void
511 {
512 match ($activityOperation)
513 {
514 CBPActivityExecutorOperationType::Execute => $this->runExecuteActivityOperation($activity),
515 CBPActivityExecutorOperationType::Cancel => $this->runCancelActivityOperation($activity),
516 CBPActivityExecutorOperationType::HandleFault => $this->runHandleFaultActivityOperation($activity, $exception),
517 };
518 }
519
525 private function runExecuteActivityOperation(CBPActivity $activity): void
526 {
527 if ($activity->executionStatus !== CBPActivityExecutionStatus::Executing)
528 {
529 return;
530 }
531
533 if ($activity->isActivated())
534 {
536 $trackingService = $this->getService('TrackingService');
537 $trackingService->write(
538 $this->getInstanceId(),
540 $activity->getName(),
541 $activity->executionStatus,
542 $activity->executionResult,
543 $activity->getTitle(),
544 ''
545 );
546 $newStatus = $activity->execute();
547 }
548
549 if ($newStatus === CBPActivityExecutionStatus::Closed)
550 {
551 $this->closeActivity($activity);
552 }
554 {
555 throw new Exception('InvalidExecutionStatus');
556 }
557 }
558
564 private function runCancelActivityOperation(CBPActivity $activity): void
565 {
566 if ($activity->executionStatus !== CBPActivityExecutionStatus::Canceling)
567 {
568 return;
569 }
570
572 $trackingService = $this->getService("TrackingService");
573 $trackingService->write(
574 $this->getInstanceId(),
576 $activity->getName(),
577 $activity->executionStatus,
578 $activity->executionResult,
579 $activity->getTitle()
580 );
581
582 $newStatus = $activity->cancel();
583
584 if ($newStatus === CBPActivityExecutionStatus::Closed)
585 {
586 $this->closeActivity($activity);
587 }
589 {
590 throw new Exception("InvalidExecutionStatus");
591 }
592 }
593
600 private function runHandleFaultActivityOperation(CBPActivity $activity, ?Exception $exception): void
601 {
602 if ($activity->executionStatus !== CBPActivityExecutionStatus::Faulting)
603 {
604 return;
605 }
606
608 $trackingService = $this->getService("TrackingService");
609 $trackingService->write(
610 $this->getInstanceId(),
612 $activity->getName(),
613 $activity->executionStatus,
614 $activity->executionResult,
615 $activity->getTitle(),
616 ($exception ? ($exception->getCode() ? "[" . $exception->getCode() . "] " : '') . $exception->getMessage() : "")
617 );
618
619 $newStatus = $activity->handleFault($exception);
620
621 if ($newStatus === CBPActivityExecutionStatus::Closed)
622 {
623 $this->closeActivity($activity);
624 }
626 {
627 throw new Exception("InvalidExecutionStatus");
628 }
629 }
630
631 public function terminate(Exception $e = null, $stateTitle = '')
632 {
634
635 $this->setWorkflowStatus(CBPWorkflowStatus::Terminated);
636
637 $this->persister->SaveWorkflow($this->rootActivity, true);
638
640 $stateService = $this->GetService("StateService");
641 $stateService->SetState(
642 $this->instanceId,
643 [
644 "STATE" => "Terminated",
645 "TITLE" => $stateTitle ?: GetMessage("BPCGWF_TERMINATED_MSGVER_1"),
646 "PARAMETERS" => [],
647 ],
648 false
649 );
650
651 if ($e)
652 {
654 $trackingService = $this->getService("TrackingService");
655 $trackingService->write(
656 $this->instanceId,
658 "none",
661 GetMessage('BPCGWF_EXCEPTION_TITLE'),
662 ($e->getCode() ? "[" . $e->getCode() . "] " : '') . $e->getMessage()
663 );
664 }
665 }
666
673 {
674 $activity->finalize();
675 }
676
677 /************************ EVENTS QUEUE ********************************************************/
678
679 private function addEventToQueue($eventName, $arEventParameters = array())
680 {
681 $this->eventsQueue[] = [$eventName, $arEventParameters];
682 }
683
684 private function processQueuedEvents()
685 {
686 while (true)
687 {
688 $event = array_shift($this->eventsQueue);
689 if ($event === null)
690 {
691 return;
692 }
693
694 [$eventName, $eventParameters] = $event;
695
696 $this->processQueuedEvent($eventName, $eventParameters);
697 }
698 }
699
700 private function processQueuedEvent($eventName, $eventParameters = [])
701 {
702 if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
703 {
704 return;
705 }
706
707 foreach ($this->rootActivity->arEventsMap[$eventName] as $eventHandler)
708 {
709 if (!empty($eventParameters['DebugEvent']) && $eventHandler instanceof IBPActivityDebugEventListener)
710 {
711 $eventHandler->onDebugEvent($eventParameters);
712
713 continue;
714 }
715
716 if ($eventHandler instanceof IBPActivityExternalEventListener)
717 {
718 $eventHandler->onExternalEvent($eventParameters);
719 }
720 }
721 }
722
723 private function syncStatus(int $status): void
724 {
725 if ($status < CBPWorkflowStatus::Completed) // skip Created and Running
726 {
727 return;
728 }
729
730 WorkflowUserTable::syncOnWorkflowUpdated($this, $status);
731 }
732
739 public function addEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
740 {
741 if (!is_array($this->rootActivity->arEventsMap))
742 {
743 $this->rootActivity->arEventsMap = [];
744 }
745
746 if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
747 {
748 $this->rootActivity->arEventsMap[$eventName] = [];
749 }
750
751 $this->rootActivity->arEventsMap[$eventName][] = $eventHandler;
752 }
753
754 public function getEventsMap(): array
755 {
756 return is_array($this->rootActivity->arEventsMap) ? $this->rootActivity->arEventsMap : [];
757 }
758
765 public function removeEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
766 {
767 if (!is_array($this->rootActivity->arEventsMap))
768 {
769 $this->rootActivity->arEventsMap = [];
770 }
771
772 if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
773 {
774 $this->rootActivity->arEventsMap[$eventName] = [];
775 }
776
777 $idx = array_search($eventHandler, $this->rootActivity->arEventsMap[$eventName], true);
778 if ($idx !== false)
779 {
780 unset($this->rootActivity->arEventsMap[$eventName][$idx]);
781 }
782
783 if (count($this->rootActivity->arEventsMap[$eventName]) <= 0)
784 {
785 unset($this->rootActivity->arEventsMap[$eventName]);
786 }
787 }
788
789 public function isDebug(): bool
790 {
791 return false;
792 }
793}
const Faulted
Определения constants.php:47
const Executing
Определения constants.php:8
const Canceling
Определения constants.php:9
const Faulting
Определения constants.php:11
const Initialized
Определения constants.php:7
const Closed
Определения constants.php:10
Определения activity.php:8
getWorkflowStatus()
Определения activity.php:97
Определения runtime.php:22
static deleteByWorkflow($workflowId, $taskStatus=null)
Определения taskservice.php:340
const Running
Определения constants.php:258
const CancelActivity
Определения trackingservice.php:700
const FaultActivity
Определения trackingservice.php:701
const ExecuteActivity
Определения trackingservice.php:698
Определения workflow.php:9
isDebug()
Определения workflow.php:789
resume()
Определения workflow.php:215
cancelActivity(CBPActivity $activity, $arEventParameters=[])
Определения workflow.php:429
isAbandoned()
Определения workflow.php:271
CBPCompositeActivity $rootActivity
Определения workflow.php:17
getService($name)
Определения workflow.php:56
sendExternalEvent(string $eventName, array $eventParameters=[])
Определения workflow.php:294
array $activitiesQueue
Определения workflow.php:19
getDocumentId()
Определения workflow.php:61
initializeActivity(CBPActivity $activity)
Определения workflow.php:367
addEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
Определения workflow.php:739
runQueue()
Определения workflow.php:467
getEventsMap()
Определения workflow.php:754
runStep()
Определения workflow.php:477
getPersister()
Определения workflow.php:83
getStartedBy()
Определения workflow.php:76
closeActivity(CBPActivity $activity, $arEventParameters=[])
Определения workflow.php:400
reload(CBPActivity $rootActivity)
Определения workflow.php:181
getTemplateId()
Определения workflow.php:71
faultActivity(CBPActivity $activity, Exception $e, $arEventParameters=[])
Определения workflow.php:440
array $eventsQueue
Определения workflow.php:20
getRootActivity()
Определения workflow.php:39
__sleep()
Определения workflow.php:112
executeActivity(CBPActivity $activity, array $eventParameters=[])
Определения workflow.php:383
__construct($instanceId, CBPRuntime $runtime)
Определения workflow.php:96
finalizeActivity(CBPActivity $activity)
Определения workflow.php:672
setWorkflowStatus($newStatus)
Определения workflow.php:49
getRuntime()
Определения workflow.php:34
removeEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
Определения workflow.php:765
getDocumentType()
Определения workflow.php:66
isNew()
Определения workflow.php:261
isFinished()
Определения workflow.php:276
initialize(CBPActivity $rootActivity, $documentId, $workflowParameters=[], $workflowVariablesTypes=[], $workflowParametersTypes=[], $workflowTemplateId=0)
Определения workflow.php:119
CBPWorkflowPersister $persister
Определения workflow.php:15
getInstanceId()
Определения workflow.php:26
abandon()
Определения workflow.php:266
start()
Определения workflow.php:200
getActivityByName($activityName)
Определения workflow.php:341
CBPRuntime $runtime
Определения workflow.php:14
static GetPersister()
Определения workflowpersister.php:21
const Suspended
Определения constants.php:84
const Running
Определения constants.php:82
static isFinished(int $status)
Определения constants.php:115
const Completed
Определения constants.php:83
const Terminated
Определения constants.php:85
const Created
Определения constants.php:81
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$activity
Определения options.php:214
$status
Определения session.php:10
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$name
Определения menu_edit.php:35
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
</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
$k
Определения template_pdf.php:567