diff --git a/ProcessMaker/Http/Controllers/Api/TaskController.php b/ProcessMaker/Http/Controllers/Api/TaskController.php index 5c2613e293..2c2647cc40 100644 --- a/ProcessMaker/Http/Controllers/Api/TaskController.php +++ b/ProcessMaker/Http/Controllers/Api/TaskController.php @@ -341,7 +341,8 @@ public function update(Request $request, ProcessRequestToken $task) return new Resource($task->refresh()); } elseif (!empty($request->input('user_id'))) { $userToAssign = $request->input('user_id'); - $task->reassign($userToAssign, $request->user()); + $comments = $request->input('comments'); + $task->reassign($userToAssign, $request->user(), $comments); $taskRefreshed = $task->refresh(); @@ -426,7 +427,7 @@ public function setPriority(Request $request, ProcessRequestToken $task) } /** - * Only send data for a screen’s fields + * Only send data for a screen's fields * * @param ProcessRequestToken $task * diff --git a/ProcessMaker/Http/Controllers/Api/UserController.php b/ProcessMaker/Http/Controllers/Api/UserController.php index 070483b348..0ffb33450f 100644 --- a/ProcessMaker/Http/Controllers/Api/UserController.php +++ b/ProcessMaker/Http/Controllers/Api/UserController.php @@ -186,8 +186,7 @@ public function index(Request $request) */ public function getUsersTaskCount(Request $request) { - $query = User::nonSystem(); - $query->select('id', 'username', 'firstname', 'lastname'); + $query = User::select('id', 'username', 'firstname', 'lastname'); $filter = $request->input('filter', ''); if (!empty($filter)) { @@ -199,23 +198,18 @@ public function getUsersTaskCount(Request $request) }); } - $query->where('status', 'ACTIVE'); - - $query->withCount('activeTasks'); - $include_ids = []; $include_ids_string = $request->input('include_ids', ''); if (!empty($include_ids_string)) { $include_ids = explode(',', $include_ids_string); } elseif ($request->has('assignable_for_task_id')) { - $task = ProcessRequestToken::findOrFail($request->input('assignable_for_task_id')); - $assignmentRule = $task->getAssignmentRule(); - if ($assignmentRule === 'user_group') { - // Limit the list of users to those that can be assigned to the task - $include_ids = $task->process->getAssignableUsers($task->element_id); + $processRequestToken = ProcessRequestToken::findOrFail($request->input('assignable_for_task_id')); + $assignmentRule = $processRequestToken->getAssignmentRule(); + if (config('app.reassign_restrict_to_assignable_users')) { + $include_ids = $processRequestToken->process->getAssignableUsersByAssignmentType($processRequestToken); } if ($assignmentRule === 'rule_expression' && $request->has('form_data')) { - $include_ids = $task->getAssigneesFromExpression($request->input('form_data')); + $include_ids = $processRequestToken->getAssigneesFromExpression($request->input('form_data')); } } @@ -223,10 +217,13 @@ public function getUsersTaskCount(Request $request) $query->whereIn('id', $include_ids); } - $response = $query->orderBy( - $request->input('order_by', 'username'), - $request->input('order_direction', 'ASC') - ) + $response = $query + ->where('is_system', false) + ->where('status', 'ACTIVE') + ->withCount('activeTasks') + ->orderBy( + $request->input('order_by', 'username'), + $request->input('order_direction', 'ASC')) ->paginate(50); return new ApiCollection($response); diff --git a/ProcessMaker/Models/Process.php b/ProcessMaker/Models/Process.php index e7adbba6d8..b3598e1de8 100644 --- a/ProcessMaker/Models/Process.php +++ b/ProcessMaker/Models/Process.php @@ -1044,6 +1044,40 @@ public function getAssignableUsers($processTaskUuid) return array_values($users); } + /** + * This method is used to get the assignable users for a task based on the assignment rule. + * The assignment rule can be: + * - user_group: would assign it to those in the group or the Process Manager. + * - process_variable: would assign it to those in the group or the Process Manager. + * - rule_expression: would assign it to those in the group or the Process Manager. + * - previous_task_assignee: would assign it to the Process Manager. + * - requester: would assign it to the Process Manager. + * - process_manager: would assign it to the same Process Manager. + * + * @param ProcessRequestToken $processRequestToken + * @return array + */ + public function getAssignableUsersByAssignmentType(ProcessRequestToken $processRequestToken): array + { + $users = []; + switch ($processRequestToken->getAssignmentRule()) { + case 'user_group': + case 'process_variable': + case 'rule_expression': + $users = $this->getAssignableUsers($processRequestToken->element_id); + $users[] = $processRequestToken->process->properties["manager_id"]; + break; + case 'previous_task_assignee': + case 'requester': + $users[] = $processRequestToken->process->properties["manager_id"]; + break; + case 'process_manager': + $users[] = $processRequestToken->process->properties["manager_id"]; + break; + } + return $users; + } + /** * Get a consolidated list of users within groups. * diff --git a/ProcessMaker/Models/ProcessRequestToken.php b/ProcessMaker/Models/ProcessRequestToken.php index 87621f1f0a..97111108ce 100644 --- a/ProcessMaker/Models/ProcessRequestToken.php +++ b/ProcessMaker/Models/ProcessRequestToken.php @@ -1340,7 +1340,7 @@ public function sendActivityActivatedNotifications() * @param User $requestingUser * @return void */ - public function reassign($toUserId, User $requestingUser) + public function reassign($toUserId, User $requestingUser, $comments = '') { $sendActivityActivatedNotifications = false; $reassingAction = false; @@ -1365,6 +1365,9 @@ public function reassign($toUserId, User $requestingUser) $this->persistUserData($toUserId); $reassingAction = true; } + if ($comments != null && $comments !== '') { + $this->comments = $comments; + } $this->save(); if ($sendActivityActivatedNotifications) { diff --git a/config/app.php b/config/app.php index 4106d36af8..6745518567 100644 --- a/config/app.php +++ b/config/app.php @@ -300,5 +300,6 @@ 'multitenancy' => env('MULTITENANCY', false), + 'reassign_restrict_to_assignable_users' => env('REASSIGN_RESTRICT_TO_ASSIGNABLE_USERS', true), 'resources_core_path' => base_path('resources-core'), ]; diff --git a/database/migrations/2025_04_08_115507_add_the_comments_field_to_the_process_request_token_table.php b/database/migrations/2025_04_08_115507_add_the_comments_field_to_the_process_request_token_table.php new file mode 100644 index 0000000000..69811599b1 --- /dev/null +++ b/database/migrations/2025_04_08_115507_add_the_comments_field_to_the_process_request_token_table.php @@ -0,0 +1,28 @@ +longText('comments')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('process_request_tokens', function (Blueprint $table) { + $table->dropColumn('comments'); + }); + } +}; diff --git a/resources/js/tasks/api/index.js b/resources/js/tasks/api/index.js index a84ff22a5e..0b55e7e35f 100644 --- a/resources/js/tasks/api/index.js +++ b/resources/js/tasks/api/index.js @@ -1,11 +1,44 @@ -import { api } from "../variables/index"; +import { getApi } from "../variables/index"; -export const updateCollection = async ({ collectionId, recordId, data }) => { - const response = await api.put(`collections/${collectionId}/records/${recordId}`, data); +export const getReassignUsers = async (filter = null, taskId = null, currentTaskUserId = null) => { + const api = getApi(); + const response = await api.get("users_task_count", { params: { filter, assignable_for_task_id: taskId, include_current_user: true } }); + const data = response.data; + if (currentTaskUserId && Array.isArray(data?.data)) { + data.data = data.data.filter((user) => user.id !== currentTaskUserId); + } + return data; +}; + +export const updateReassignUser = async (taskId, userId, comments = null) => { + const api = getApi(); + const response = await api.put(`tasks/${taskId}`, { user_id: userId, comments }); + return response.data; +}; +export const updateComment = async ({ + body, + subject, + commentableId, + commentableType, + parentId = 0, + type = "COMMENT", +}) => { + const api = getApi(); + const response = await api.post("comments/comments", { + body, + subject, + commentable_id: commentableId, + commentable_type: commentableType, + type, + parent_id: parentId, + }); return response.data; }; -export default { - updateCollection, +export const updateCollection = async ({ collectionId, recordId, data }) => { + const api = getApi(); + const response = await api.put(`collections/${collectionId}/records/${recordId}`, data); + + return response.data; }; diff --git a/resources/js/tasks/components/PreviewMixin.js b/resources/js/tasks/components/PreviewMixin.js index c7a5886161..d004bc69d2 100644 --- a/resources/js/tasks/components/PreviewMixin.js +++ b/resources/js/tasks/components/PreviewMixin.js @@ -172,6 +172,7 @@ const PreviewMixin = { } this.prevTask = prevTask; this.nextTask = nextTask; + this.showReassignment = false; }, /** * Expand Open task diff --git a/resources/js/tasks/components/TasksPreview.vue b/resources/js/tasks/components/TasksPreview.vue index fd45f5817a..e9b27459df 100644 --- a/resources/js/tasks/components/TasksPreview.vue +++ b/resources/js/tasks/components/TasksPreview.vue @@ -1,41 +1,51 @@