diff --git a/database/factories/SubApplicationFactory.php b/database/factories/SubApplicationFactory.php new file mode 100644 index 000000000..e21488705 --- /dev/null +++ b/database/factories/SubApplicationFactory.php @@ -0,0 +1,17 @@ +define(\ProcessMaker\Model\SubApplication::class, function (Faker $faker) { + return [ + 'APP_UID' => G::generateUniqueID(), + 'APP_PARENT' => G::generateUniqueID(), + 'DEL_INDEX_PARENT' => 2, + 'DEL_THREAD_PARENT' => 1, + 'SA_STATUS' => 'ACTIVE', + 'SA_VALUES_OUT' => 'a:0:{}', + 'SA_VALUES_IN' => 'a:0:{}', + 'SA_INIT_DATE' => $faker->dateTime(), + 'SA_FINISH_DATE' => $faker->dateTime(), + ]; +}); diff --git a/tests/unit/workflow/engine/classes/DerivationTest.php b/tests/unit/workflow/engine/classes/DerivationTest.php index c2c1d8959..17fe95730 100644 --- a/tests/unit/workflow/engine/classes/DerivationTest.php +++ b/tests/unit/workflow/engine/classes/DerivationTest.php @@ -10,6 +10,7 @@ use ProcessMaker\Model\Application; use ProcessMaker\Model\Delegation; use ProcessMaker\Model\Process; use ProcessMaker\Model\Route; +use ProcessMaker\Model\SubApplication; use ProcessMaker\Model\Task; use ProcessMaker\Model\TaskUser; use ProcessMaker\Model\User; @@ -111,6 +112,8 @@ class DerivationTest extends TestCase * It tests the doDerivation method sending variables synchronously * * @covers Derivation::doDerivation() + * @covers Derivation:: + * * @test */ public function it_should_test_the_do_derivation_method_sending_variables_synchronously() @@ -151,7 +154,7 @@ class DerivationTest extends TestCase $sp = [ 'SP_VARIABLES_OUT' => 'a:1:{s:6:"@&var1";s:6:"@&var2";}', 'SP_VARIABLES_IN' => 'a:1:{s:6:"@&var2";s:6:"@&var3";}', - 'SP_SYNCHRONOUS' => '1', + 'SP_SYNCHRONOUS' => 1, 'SP_TYPE' => '', 'TAS_UID' => $task->TAS_UID, 'USR_UID' => $user->USR_UID, @@ -166,12 +169,22 @@ class DerivationTest extends TestCase // Assert the new delegation index is 1 $this->assertEquals(1, $res); + + // Review the subprocess synchronously + $query = SubApplication::query()->select(); + $query->where('APP_PARENT', $application->APP_UID); + $query->where('DEL_INDEX_PARENT', $appDelegation->DEL_INDEX); + $results = $query->get()->toArray(); + $this->assertNotEmpty($results); + $this->assertEquals($results[0]['SA_STATUS'], 'ACTIVE'); } /** * It tests the doDerivation method sending variables asynchronously * * @covers Derivation::doDerivation() + * @covers Derivation:: + * * @test */ public function it_should_test_the_do_derivation_method_sending_variables_asynchronously() @@ -225,7 +238,7 @@ class DerivationTest extends TestCase $sp = [ 'SP_VARIABLES_OUT' => 'a:1:{s:6:"@&var1";s:6:"@&var2";}', 'SP_VARIABLES_IN' => 'a:1:{s:6:"@&var2";s:6:"@&var3";}', - 'SP_SYNCHRONOUS' => '0', + 'SP_SYNCHRONOUS' => 0, 'SP_TYPE' => '', 'TAS_UID' => $task->TAS_UID, 'USR_UID' => $user->USR_UID, @@ -240,5 +253,13 @@ class DerivationTest extends TestCase // Assert the new delegation index is 1 $this->assertEquals(1, $res); + + // Review the subprocess asynchronously + $query = SubApplication::query()->select(); + $query->where('APP_PARENT', $application->APP_UID); + $query->where('DEL_INDEX_PARENT', $appDelegation->DEL_INDEX); + $results = $query->get()->toArray(); + $this->assertNotEmpty($results); + $this->assertEquals($results[0]['SA_STATUS'], 'FINISHED'); } } \ No newline at end of file diff --git a/workflow/engine/classes/Derivation.php b/workflow/engine/classes/Derivation.php index 49302cd53..b50a41ecc 100644 --- a/workflow/engine/classes/Derivation.php +++ b/workflow/engine/classes/Derivation.php @@ -5,6 +5,7 @@ */ use ProcessMaker\Model\Application as ModelApplication; +use ProcessMaker\Model\SubApplication as ModelSubApplication; class Derivation { @@ -1163,7 +1164,6 @@ class Derivation $this->verifyIsCaseChild($currentDelegation["APP_UID"], $currentDelegation["DEL_INDEX"]); } $flagUpdateCase = true; - } //The variable $iNewDelIndex will be true if we created a new index the variable @@ -1204,7 +1204,7 @@ class Derivation * @param array $appFields * @param array $aSP * - * @return integer $iNewDelIndex + * @return integer * @throws /Exception */ function doDerivation ($currentDelegation, $nextDel, $appFields, $aSP = null) @@ -1338,84 +1338,10 @@ class Derivation //if there are SubProcess to create if (isset($aSP)) { - //Check if is SelfService the task in the SubProcess - $isSelfService = (empty($aSP['USR_UID'])) ? true : false; + // Create case in the subprocess + $this->subProcessCreation($aSP, $appFields, $currentDelegation, $iNewDelIndex, $iAppThreadIndex); - //Create the new case in the sub-process - //Set the initial date to null the time its created - $aNewCase = $this->case->startCase( $aSP['TAS_UID'], $aSP['USR_UID'], true, $appFields, $isSelfService); - - //Load the TAS_UID related to the SubProcess - $taskNextDel = TaskPeer::retrieveByPK($aSP["TAS_UID"]); //Sub-Process - - //Copy case variables to sub-process case - $aFields = unserialize( $aSP['SP_VARIABLES_OUT'] ); - $aNewFields = array (); - $aOldFields = $this->case->loadCase( $aNewCase['APPLICATION'] ); - - foreach ($aFields as $sOriginField => $sTargetField) { - $sOriginField = trim($sOriginField, " @#%?$=&"); - $sTargetField = trim($sTargetField, " @#%?$=&"); - - $aNewFields[$sTargetField] = isset( $appFields['APP_DATA'][$sOriginField] ) ? $appFields['APP_DATA'][$sOriginField] : ''; - - if (array_key_exists($sOriginField . '_label', $appFields['APP_DATA'])) { - $aNewFields[$sTargetField . '_label'] = $appFields['APP_DATA'][$sOriginField . '_label']; - } - } - - //We will to update the new case - $aOldFields['APP_DATA'] = array_merge( $aOldFields['APP_DATA'], $aNewFields ); - $aOldFields['APP_STATUS'] = 'TO_DO'; - $this->case->updateCase( - $aNewCase['APPLICATION'], - $aOldFields - ); - - //Create a registry in SUB_APPLICATION table - $aSubApplication = array ( - 'APP_UID' => $aNewCase['APPLICATION'], - 'APP_PARENT' => $currentDelegation['APP_UID'], - 'DEL_INDEX_PARENT' => $iNewDelIndex, - 'DEL_THREAD_PARENT' => $iAppThreadIndex, - 'SA_STATUS' => 'ACTIVE', - 'SA_VALUES_OUT' => serialize($aNewFields), - 'SA_INIT_DATE' => date('Y-m-d H:i:s') - ); - if ($aSP['SP_SYNCHRONOUS'] == 0) { - $aSubApplication['SA_STATUS'] = 'FINISHED'; - $aSubApplication['SA_FINISH_DATE'] = $aSubApplication['SA_INIT_DATE']; - } - $oSubApplication = new SubApplication(); - $oSubApplication->create( $aSubApplication ); - - //Update the AppDelegation to execute the update trigger - $AppDelegation = AppDelegationPeer::retrieveByPK( $aNewCase['APPLICATION'], $aNewCase['INDEX'] ); - $AppDelegation->save(); - - //Create record in table APP_ASSIGN_SELF_SERVICE_VALUE - if ($taskNextDel->getTasAssignType() == "SELF_SERVICE" && trim($taskNextDel->getTasGroupVariable()) != "") { - $nextTaskGroupVariable = trim($taskNextDel->getTasGroupVariable(), " @#"); - - if (isset($aOldFields["APP_DATA"][$nextTaskGroupVariable])) { - $dataVariable = $aOldFields["APP_DATA"][$nextTaskGroupVariable]; - $dataVariable = (is_array($dataVariable))? $dataVariable : trim($dataVariable); - - if (!empty($dataVariable)) { - $appAssignSelfServiceValue = new AppAssignSelfServiceValue(); - - $appAssignSelfServiceValue->create($aNewCase["APPLICATION"], $aNewCase["INDEX"], array("PRO_UID" => $aNewCase["PROCESS"], "TAS_UID" => $aSP["TAS_UID"], "GRP_UID" => ""), $dataVariable); - } - } - } - - //We will to send the notifications - $sendNotificationsMobile = $this->sendNotificationsMobile($aOldFields, $aSP, $aNewCase['INDEX']); - $nextTaskData = $taskNextDel->toArray(BasePeer::TYPE_FIELDNAME); - $nextTaskData['USR_UID'] = $aSP['USR_UID']; - $sendNotifications = $this->notifyAssignedUser($appFields, $nextTaskData, $aNewCase['INDEX']); - - //If is ASYNCHRONOUS we will to route the case master + // If is ASYNCHRONOUS we will to route the case master if ($aSP['SP_SYNCHRONOUS'] == 0) { $this->case->setDelInitDate( $currentDelegation['APP_UID'], $iNewDelIndex ); $aDeriveTasks = $this->prepareInformation( @@ -1476,6 +1402,102 @@ class Derivation return $iNewDelIndex; } + /** + * Create the sub-process + * + * @param array $subProcessInfo + * @param array $appFields + * @param array $currentDelegation + * @param int $delIndex + * @param int $threadIndex + * + * @return void + */ + protected function subProcessCreation(array $subProcessInfo, array $appFields, array $currentDelegation, $delIndex, $threadIndex) + { + // Check if is SelfService the task in the SubProcess + $isSelfService = (empty($subProcessInfo['USR_UID'])) ? true : false; + + // Create the new case in the sub-process + // Set the initial date to null the time its created + // The DelThreadStatus will create with CLOSED value for avoid to open the case without all the execution + $newCase = $this->case->startCase($subProcessInfo['TAS_UID'], $subProcessInfo['USR_UID'], true, $appFields, $isSelfService); + + // Load the TAS_UID related to the SubProcess + $taskNextDel = TaskPeer::retrieveByPK($subProcessInfo["TAS_UID"]); //Sub-Process + + // Copy case variables to sub-process case + $fields = unserialize($subProcessInfo['SP_VARIABLES_OUT']); + $newFields = []; + $oldFields = $this->case->loadCase($newCase['APPLICATION']); + + foreach ($fields as $originField => $targetField) { + $originField = trim($originField, " @#%?$=&"); + $targetField = trim($targetField, " @#%?$=&"); + $newFields[$targetField] = isset($appFields['APP_DATA'][$originField]) ? $appFields['APP_DATA'][$originField] : ''; + + if (array_key_exists($originField . '_label', $appFields['APP_DATA'])) { + $newFields[$targetField . '_label'] = $appFields['APP_DATA'][$originField . '_label']; + } + } + + // We will to update the new case + $oldFields['APP_DATA'] = array_merge($oldFields['APP_DATA'], $newFields); + $oldFields['APP_STATUS'] = 'TO_DO'; + $this->case->updateCase( + $newCase['APPLICATION'], + $oldFields + ); + + // Create a registry in SUB_APPLICATION table + $attributes = [ + 'APP_UID' => $newCase['APPLICATION'], + 'APP_PARENT' => $currentDelegation['APP_UID'], + 'DEL_INDEX_PARENT' => $delIndex, + 'DEL_THREAD_PARENT' => $threadIndex, + 'SA_STATUS' => 'ACTIVE', + 'SA_VALUES_OUT' => serialize($newFields), + 'SA_INIT_DATE' => date('Y-m-d H:i:s') + ]; + if ($subProcessInfo['SP_SYNCHRONOUS'] == 0) { + $attributes['SA_STATUS'] = 'FINISHED'; + $attributes['SA_FINISH_DATE'] = $attributes['SA_INIT_DATE']; + } + $subprocess = ModelSubApplication::create($attributes); + + // Update the AppDelegation to execute the update trigger + // Update the DelThreadStatus, the thread is ready for continue + $appDelegation = AppDelegationPeer::retrieveByPK($newCase['APPLICATION'], $newCase['INDEX']); + $appDelegation->setDelThreadStatus('OPEN'); + $appDelegation->save(); + + // Create record in table APP_ASSIGN_SELF_SERVICE_VALUE + $tasGroupVariable = $taskNextDel->getTasGroupVariable(); + if ($taskNextDel->getTasAssignType() == "SELF_SERVICE" && !empty(trim($tasGroupVariable))) { + $nextTaskGroupVariable = trim($tasGroupVariable, " @#"); + + if (isset($oldFields["APP_DATA"][$nextTaskGroupVariable])) { + $dataVariable = $oldFields["APP_DATA"][$nextTaskGroupVariable]; + $dataVariable = (is_array($dataVariable))? $dataVariable : trim($dataVariable); + + if (!empty($dataVariable)) { + $appAssignSelfServiceValue = new AppAssignSelfServiceValue(); + $appAssignSelfServiceValue->create( + $newCase["APPLICATION"], + $newCase["INDEX"], + ["PRO_UID" => $newCase["PROCESS"], "TAS_UID" => $subProcessInfo["TAS_UID"], "GRP_UID" => ""], + $dataVariable + ); + } + } + } + // We will to send the notifications + $sendNotificationsMobile = $this->sendNotificationsMobile($oldFields, $subProcessInfo, $newCase['INDEX']); + $nextTaskData = $taskNextDel->toArray(BasePeer::TYPE_FIELDNAME); + $nextTaskData['USR_UID'] = $subProcessInfo['USR_UID']; + $sendNotifications = $this->notifyAssignedUser($appFields, $nextTaskData, $newCase['INDEX']); + } + /** * This function returns the current user Checking cases where USR_ID exists or checking a subprocess (SYNCHRONOUS) * @param $nextDel diff --git a/workflow/engine/classes/model/AppDelegation.php b/workflow/engine/classes/model/AppDelegation.php index f6c0a5d47..e0d1a39f3 100644 --- a/workflow/engine/classes/model/AppDelegation.php +++ b/workflow/engine/classes/model/AppDelegation.php @@ -181,6 +181,9 @@ class AppDelegation extends BaseAppDelegation $criteriaUpdate->add(AppDelegationPeer::DEL_LAST_INDEX, 0); BasePeer::doUpdate($criteria, $criteriaUpdate, Propel::getConnection('workflow')); + // Define the status of the thread, if is subprocess we need to CLOSED the thread + $theadStatus = !$isSubprocess ? 'OPEN' : 'CLOSED'; + $this->setAppUid($sAppUid); $this->setProUid($sProUid); $this->setTasUid($sTasUid); @@ -191,7 +194,7 @@ class AppDelegation extends BaseAppDelegation $this->setDelType('NORMAL'); $this->setDelPriority(($iPriority != '' ? $iPriority : '3')); $this->setDelThread($sAppThread); - $this->setDelThreadStatus('OPEN'); + $this->setDelThreadStatus($theadStatus); $this->setDelDelegateDate('now'); $this->setAppNumber($appNumber); $this->setTasId($taskId); diff --git a/workflow/engine/src/ProcessMaker/Model/SubApplication.php b/workflow/engine/src/ProcessMaker/Model/SubApplication.php new file mode 100644 index 000000000..615fd093e --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Model/SubApplication.php @@ -0,0 +1,51 @@ + '', + 'SA_VALUES_OUT' => '', + 'SA_VALUES_IN' => '', + 'SA_INIT_DATE' => '', + 'SA_FINISH_DATE' => '' + ]; + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'APP_UID', + 'APP_PARENT', + 'DEL_INDEX_PARENT', + 'DEL_THREAD_PARENT', + 'SA_STATUS', + 'SA_VALUES_OUT', + 'SA_VALUES_IN', + 'SA_INIT_DATE', + 'SA_FINISH_DATE' + ]; +} \ No newline at end of file