diff --git a/ProcessMaker/Traits/TaskControllerIndexMethods.php b/ProcessMaker/Traits/TaskControllerIndexMethods.php index be27fa8804..2ecafe85a8 100644 --- a/ProcessMaker/Traits/TaskControllerIndexMethods.php +++ b/ProcessMaker/Traits/TaskControllerIndexMethods.php @@ -295,6 +295,14 @@ private function applyPmql($query, $request, $user) { $pmql = $request->input('pmql', ''); if (!empty($pmql)) { + if ($this->advancedFilterHasStatus($request)) { + $pmql = $this->removeStatusFromPmql($pmql); + } + + if (empty($pmql)) { + return; + } + try { $query->pmql($pmql, null, $user); } catch (QueryException $e) { @@ -305,6 +313,36 @@ private function applyPmql($query, $request, $user) } } + private function advancedFilterHasStatus($request): bool + { + $advancedFilter = $request->input('advanced_filter', ''); + if (empty($advancedFilter)) { + return false; + } + + $filterArray = is_string($advancedFilter) ? json_decode($advancedFilter, true) : $advancedFilter; + if (!is_array($filterArray)) { + return false; + } + + foreach ($filterArray as $filter) { + if (isset($filter['subject']['type']) && $filter['subject']['type'] === 'Status') { + return true; + } + } + + return false; + } + + private function removeStatusFromPmql(string $pmql): string + { + $pmql = preg_replace('/\s+AND\s+\(status\s*=\s*"[^"]*"\)/i', '', $pmql); + $pmql = preg_replace('/\(status\s*=\s*"[^"]*"\)\s+AND\s+/i', '', $pmql); + $pmql = preg_replace('/\(status\s*=\s*"[^"]*"\)/i', '', $pmql); + + return trim($pmql); + } + private function applyAdvancedFilter($query, $request) { if ($advancedFilter = $request->input('advanced_filter', '')) { diff --git a/tests/Feature/Api/TasksTest.php b/tests/Feature/Api/TasksTest.php index 573b705d63..53386e1e91 100644 --- a/tests/Feature/Api/TasksTest.php +++ b/tests/Feature/Api/TasksTest.php @@ -830,6 +830,75 @@ public function testAdvancedFilterByProcessRequestName() $this->assertEquals($hitTask->id, $json['data'][0]['id']); } + public function testAdvancedStatusFilterOverridesPmqlStatus() + { + $user = User::factory()->create(['is_administrator' => true]); + + $activeTask = ProcessRequestToken::factory()->create([ + 'status' => 'ACTIVE', + 'element_type' => 'task', + 'user_id' => $user->id, + 'is_self_service' => 0, + ]); + + $completedTask = ProcessRequestToken::factory()->create([ + 'status' => 'CLOSED', + 'element_type' => 'task', + 'user_id' => $user->id, + 'is_self_service' => 0, + ]); + + $statusFilter = json_encode([ + [ + 'subject' => ['type' => 'Status'], + 'operator' => '=', + 'value' => 'Completed', + ], + ]); + + $response = $this->actingAs($user, 'api')->get(route('api.tasks.index', [ + 'pmql' => '(user_id = ' . $user->id . ') AND (status = "In Progress")', + 'advanced_filter' => $statusFilter, + ])); + + $response->assertStatus(200); + $data = $response->json('data'); + $returnedIds = collect($data)->pluck('id')->toArray(); + + $this->assertContains($completedTask->id, $returnedIds); + $this->assertNotContains($activeTask->id, $returnedIds); + } + + public function testPmqlStatusPreservedWhenNoAdvancedStatusFilter() + { + $user = User::factory()->create(['is_administrator' => true]); + + $activeTask = ProcessRequestToken::factory()->create([ + 'status' => 'ACTIVE', + 'element_type' => 'task', + 'user_id' => $user->id, + 'is_self_service' => 0, + ]); + + $completedTask = ProcessRequestToken::factory()->create([ + 'status' => 'CLOSED', + 'element_type' => 'task', + 'user_id' => $user->id, + 'is_self_service' => 0, + ]); + + $response = $this->actingAs($user, 'api')->get(route('api.tasks.index', [ + 'pmql' => '(user_id = ' . $user->id . ') AND (status = "In Progress")', + ])); + + $response->assertStatus(200); + $data = $response->json('data'); + $returnedIds = collect($data)->pluck('id')->toArray(); + + $this->assertContains($activeTask->id, $returnedIds); + $this->assertNotContains($completedTask->id, $returnedIds); + } + public function testGetScreenFields() { $this->be($this->user);