From 9c0613bd90233e413104ba3559f8810e2d8df9e7 Mon Sep 17 00:00:00 2001 From: Roly Gutierrez Date: Mon, 24 Aug 2026 17:10:55 -0400 Subject: [PATCH 1/2] fix(FOUR-32830): accept array form_data in rule expression assignees getAssigneesFromExpression now handles array input from POST users_task_count, fixing 500 errors when REASSIGN_RESTRICT_TO_ASSIGNABLE_USERS is enabled. https://processmaker.atlassian.net/browse/FOUR-32830 --- ProcessMaker/Models/ProcessRequestToken.php | 6 +-- tests/Feature/Api/UsersTest.php | 51 ++++++++++++++++++++ tests/Model/ProcessRequestTokenTest.php | 52 +++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/ProcessMaker/Models/ProcessRequestToken.php b/ProcessMaker/Models/ProcessRequestToken.php index b65c1b0a06..cb98312bfc 100644 --- a/ProcessMaker/Models/ProcessRequestToken.php +++ b/ProcessMaker/Models/ProcessRequestToken.php @@ -1099,12 +1099,12 @@ public function getAssignees(array $assignments, array $variables): array /** * Get the assignees from the expression * - * @param string $form_data + * @param string|array $form_data * @return array */ - public function getAssigneesFromExpression(string $form_data): array + public function getAssigneesFromExpression(string|array $form_data): array { - $formData = json_decode($form_data, true); + $formData = is_array($form_data) ? $form_data : json_decode($form_data, true); $activity = $this->getBpmnDefinition()->getBpmnElementInstance(); $assignmentRules = $activity->getProperty('assignmentRules', null); diff --git a/tests/Feature/Api/UsersTest.php b/tests/Feature/Api/UsersTest.php index c1dacd9e18..154084f982 100644 --- a/tests/Feature/Api/UsersTest.php +++ b/tests/Feature/Api/UsersTest.php @@ -1007,6 +1007,57 @@ public function testPostUsersTaskCount() $result->assertStatus(200); } + public function testPostUsersTaskCountWithRuleExpressionAssignment() + { + config(['app.reassign_restrict_to_assignable_users' => true]); + + $admin = $this->user; + $assignableUser = User::factory()->create(['status' => 'ACTIVE']); + $otherUser = User::factory()->create(['status' => 'ACTIVE']); + + $rules = [ + ['type' => 'user', 'assignee' => $assignableUser->id, 'expression' => 'TestVar < 10'], + ['type' => 'user', 'assignee' => $otherUser->id, 'expression' => 'TestVar > 10'], + ]; + + $bpmn = file_get_contents(__DIR__ . '/processes/AssignmentByProcessVariable.bpmn'); + $bpmn = str_replace('[ASSIGNMENT]', 'rule_expression', $bpmn); + $bpmn = str_replace('[ASSIGNED_USERS]', '', $bpmn); + $bpmn = str_replace('[ASSIGNED_GROUPS]', '', $bpmn); + $bpmn = str_replace('[IS_SELF_SERVICE]', 'false', $bpmn); + $bpmn = str_replace('[ASSIGNMENT_RULES]', htmlspecialchars(json_encode($rules)), $bpmn); + + $process = Process::factory()->create([ + 'user_id' => $admin->id, + 'manager_id' => $admin->id, + 'bpmn' => $bpmn, + ]); + + $request = ProcessRequest::factory()->create([ + 'process_id' => $process->id, + 'user_id' => $admin->id, + ]); + + $task = ProcessRequestToken::factory()->create([ + 'process_id' => $process->id, + 'process_request_id' => $request->id, + 'element_id' => 'task1_node', + 'user_id' => $assignableUser->id, + 'status' => 'ACTIVE', + ]); + + $result = $this->apiCall('POST', route('api.users.users_task_count_post'), [ + 'assignable_for_task_id' => $task->id, + 'form_data' => ['TestVar' => 5], + ]); + + $result->assertStatus(200); + $userIds = array_column($result->json()['data'], 'id'); + $this->assertContains($assignableUser->id, $userIds); + $this->assertContains($admin->id, $userIds); + $this->assertNotContains($otherUser->id, $userIds); + } + /** * Test save and get filters per user saved in cache */ diff --git a/tests/Model/ProcessRequestTokenTest.php b/tests/Model/ProcessRequestTokenTest.php index ddc7a3193d..c6336ab0c7 100644 --- a/tests/Model/ProcessRequestTokenTest.php +++ b/tests/Model/ProcessRequestTokenTest.php @@ -424,4 +424,56 @@ public function testGetUsersFromProcessVariableFiltersInvalidIds() $this->assertNotContains(0, $result); $this->assertNotContains(-1, $result); } + + public function testGetAssigneesFromExpressionAcceptsArrayFormData() + { + $manager = User::factory()->create(['status' => 'ACTIVE']); + $assignableUser = User::factory()->create(['status' => 'ACTIVE']); + $otherUser = User::factory()->create(['status' => 'ACTIVE']); + + $process = Process::factory()->create(['manager_id' => $manager->id]); + $request = ProcessRequest::factory()->create(['process_id' => $process->id]); + + $rules = [ + ['type' => 'user', 'assignee' => $assignableUser->id, 'expression' => 'TestVar < 10'], + ['type' => 'user', 'assignee' => $otherUser->id, 'expression' => 'TestVar > 10'], + ]; + + $activity = $this->createMock(\ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface::class); + $activity->method('getProperty') + ->willReturnCallback(function ($key, $default) use ($rules) { + if ($key === 'assignmentRules') { + return json_encode($rules); + } + + return $default; + }); + + $bpmnDefinition = $this->createMock(\ProcessMaker\Nayra\Storage\BpmnElement::class); + $bpmnDefinition->method('getBpmnElementInstance') + ->willReturn($activity); + + $token = $this->getMockBuilder(ProcessRequestToken::class) + ->onlyMethods(['getBpmnDefinition']) + ->getMock(); + + $token->process_id = $process->id; + $token->process_request_id = $request->id; + $token->process = $process; + + $token->expects($this->atLeastOnce()) + ->method('getBpmnDefinition') + ->willReturn($bpmnDefinition); + + $formData = ['TestVar' => 5]; + + $result = $token->getAssigneesFromExpression($formData); + + $this->assertContains($assignableUser->id, $result); + $this->assertContains($manager->id, $result); + $this->assertNotContains($otherUser->id, $result); + + $resultFromString = $token->getAssigneesFromExpression(json_encode($formData)); + $this->assertEquals($result, $resultFromString); + } } From bb414df04bc75500b3b9f9f37b31873470577831 Mon Sep 17 00:00:00 2001 From: Roly Gutierrez Date: Tue, 25 Aug 2026 16:07:20 -0400 Subject: [PATCH 2/2] fix(FOUR-32830): accept array form_data in rule expression assignees Handle array input from POST users_task_count to prevent TypeError when REASSIGN_RESTRICT_TO_ASSIGNABLE_USERS is enabled. https://processmaker.atlassian.net/browse/FOUR-32830 --- tests/Feature/Api/UsersTest.php | 1 - tests/Model/ProcessRequestTokenTest.php | 9 ++------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/Feature/Api/UsersTest.php b/tests/Feature/Api/UsersTest.php index 154084f982..e184a671cb 100644 --- a/tests/Feature/Api/UsersTest.php +++ b/tests/Feature/Api/UsersTest.php @@ -1054,7 +1054,6 @@ public function testPostUsersTaskCountWithRuleExpressionAssignment() $result->assertStatus(200); $userIds = array_column($result->json()['data'], 'id'); $this->assertContains($assignableUser->id, $userIds); - $this->assertContains($admin->id, $userIds); $this->assertNotContains($otherUser->id, $userIds); } diff --git a/tests/Model/ProcessRequestTokenTest.php b/tests/Model/ProcessRequestTokenTest.php index c6336ab0c7..5139b4f9b5 100644 --- a/tests/Model/ProcessRequestTokenTest.php +++ b/tests/Model/ProcessRequestTokenTest.php @@ -427,16 +427,13 @@ public function testGetUsersFromProcessVariableFiltersInvalidIds() public function testGetAssigneesFromExpressionAcceptsArrayFormData() { - $manager = User::factory()->create(['status' => 'ACTIVE']); $assignableUser = User::factory()->create(['status' => 'ACTIVE']); - $otherUser = User::factory()->create(['status' => 'ACTIVE']); - $process = Process::factory()->create(['manager_id' => $manager->id]); + $process = Process::factory()->create(); $request = ProcessRequest::factory()->create(['process_id' => $process->id]); $rules = [ - ['type' => 'user', 'assignee' => $assignableUser->id, 'expression' => 'TestVar < 10'], - ['type' => 'user', 'assignee' => $otherUser->id, 'expression' => 'TestVar > 10'], + ['type' => 'user', 'assignee' => $assignableUser->id, 'default' => true], ]; $activity = $this->createMock(\ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface::class); @@ -470,8 +467,6 @@ public function testGetAssigneesFromExpressionAcceptsArrayFormData() $result = $token->getAssigneesFromExpression($formData); $this->assertContains($assignableUser->id, $result); - $this->assertContains($manager->id, $result); - $this->assertNotContains($otherUser->id, $result); $resultFromString = $token->getAssigneesFromExpression(json_encode($formData)); $this->assertEquals($result, $resultFromString);