Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ProcessMaker/Http/Controllers/Api/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ public function getUsersTaskCount(Request $request)
if ($assignmentRule === 'rule_expression' && $request->has('form_data')) {
$include_ids = $processRequestToken->getAssigneesFromExpression($request->input('form_data'));
}
if ($assignmentRule === 'process_variable' && $request->has('form_data')) {
$include_ids = $processRequestToken->getUsersFromProcessVariable($request->input('form_data'));
}
}

if (!empty($include_ids)) {
Expand Down
54 changes: 54 additions & 0 deletions ProcessMaker/Models/ProcessRequestToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,60 @@ public function getAssignmentRule()
return $assignment;
}

/**
* Get user IDs from process variables for task assignment.
*
* Extracts user IDs and group IDs from form data based on the activity's
* assignedUsers and assignedGroups properties. Retrieves all users from
* specified groups (including subgroups recursively) and combines them
* with directly assigned users.
*
* Used when assignment rule is 'process_variable'.
*
* @param array $form_data Form data containing process variable values.
* Keys must match activity's assignedUsers and
* assignedGroups properties. Values must be arrays.
*
* @return array Unique numeric user IDs (direct users + users from groups).
*/
public function getUsersFromProcessVariable(array $form_data)
{
$activity = $this->getBpmnDefinition()->getBpmnElementInstance();
$assignedUsers = $activity->getProperty('assignedUsers', null);
$assignedGroups = $activity->getProperty('assignedGroups', null);

$usersIds = [];
$groupsIds = [];

// Validate and get user IDs from form_data
if ($assignedUsers && isset($form_data[$assignedUsers]) && is_array($form_data[$assignedUsers])) {
$usersIds = $form_data[$assignedUsers];
}

// Validate and get group IDs from form_data
if ($assignedGroups && isset($form_data[$assignedGroups]) && is_array($form_data[$assignedGroups])) {
$groupsIds = $form_data[$assignedGroups];
}

// Get users from groups using the Process model method
$usersFromGroups = [];
if (!empty($groupsIds) && $this->process) {
// Use the getConsolidatedUsers method from the Process model
// This method gets users from groups including subgroups recursively
$this->process->getConsolidatedUsers($groupsIds, $usersFromGroups);
}

// Combine direct users with users from groups
$allUserIds = array_unique(array_merge($usersIds, $usersFromGroups));

// Convert to numeric array and filter valid values
$allUserIds = array_values(array_filter($allUserIds, function ($id) {
return !empty($id) && is_numeric($id) && $id > 0;
}));

return $allUserIds;
}

/**
* Get the assignees for the token.
*
Expand Down
44 changes: 21 additions & 23 deletions resources/js/common/reassignMixin.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getReassignUsers as getReassignUsersApi } from "../tasks/api";

export default {
data() {
return {
Expand All @@ -21,32 +23,28 @@ export default {
this.allowReassignment = response.data[this.task.id];
});
},
getReassignUsers(filter = null) {
const params = { };
if (filter) {
params.filter = filter;
}
if (this.task?.id) {
params.assignable_for_task_id = this.task.id;
// The variables are needed to calculate the rule expression.
if (this?.formData) {
params.form_data = this.formData;
delete params.form_data._user;
delete params.form_data._request;
delete params.form_data._process;
}
}
async getReassignUsers(filter = null) {
try {
const response = await getReassignUsersApi(
filter,
this.task?.id,
this.task?.request_data,
this.currentTaskUserId
);

ProcessMaker.apiClient.post('users_task_count', params ).then(response => {
this.reassignUsers = [];
response.data.data.forEach((user) => {
this.reassignUsers.push({
text: user.fullname,
value: user.id,
active_tasks_count: user.active_tasks_count
if (response?.data) {
response.data.forEach((user) => {
this.reassignUsers.push({
text: user.fullname,
value: user.id,
active_tasks_count: user.active_tasks_count
});
});
});
});
}
} catch (error) {
console.error('Error loading reassign users:', error);
}
},
onReassignInput: _.debounce(function (filter) {
this.getReassignUsers(filter);
Expand Down
41 changes: 39 additions & 2 deletions resources/js/tasks/api/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
import { getApi } from "../variables/index";

export const getReassignUsers = async (filter = null, taskId = null, currentTaskUserId = null) => {
/**
* Get reassign users using POST with form_data (for rule expression evaluation)
* This replaces the obsolete GET method with the advanced POST logic from reassignMixin
*
* @param {string|null} filter - Filter string to search users
* @param {number|null} taskId - Task ID to get assignable users for
* @param {Object|null} formData - Form data needed to calculate rule expressions
* @param {number|null} currentTaskUserId - User ID to exclude from results (matches: task?.user_id ?? task?.user?.id)
* @returns {Promise<Object>} Response data with users array
*/
export const getReassignUsers = async (
filter = null,
taskId = null,
formData = 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 params = {};

if (filter) {
params.filter = filter;
}

if (taskId) {
params.assignable_for_task_id = taskId;

// The variables are needed to calculate the rule expression.
if (formData) {
params.form_data = { ...formData };
// Remove internal variables
delete params.form_data._user;
delete params.form_data._request;
delete params.form_data._process;
}
}

const response = await api.post("users_task_count", params);
const data = response.data;

// Filter out current user to prevent self-reassignment (matches mixin logic)
if (currentTaskUserId && Array.isArray(data?.data)) {
data.data = data.data.filter((user) => user.id !== currentTaskUserId);
}

return data;
};

Expand Down
3 changes: 2 additions & 1 deletion resources/js/tasks/components/TasksPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@
<TaskPreviewAssignment
v-if="showReassignment"
:task="task"
:form-data="formData"
:current-task-user-id="currentTaskUserId"
@on-cancel-reassign="showReassignment = false"
@on-reassign-user="e=> reassignUser(e,false)"
/>
Expand Down Expand Up @@ -413,7 +415,6 @@ export default {
},
openReassignment() {
this.showReassignment = !this.showReassignment;
this.getReassignUsers();
},
getTaskDefinitionForReassignmentPermission() {
ProcessMaker.apiClient
Expand Down
41 changes: 30 additions & 11 deletions resources/js/tasks/components/taskPreview/TaskPreviewAssignment.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ const props = defineProps({
type: Object,
required: true,
},
formData: {
type: Object,
default: null,
},
currentTaskUserId: {
type: Number,
default: null,
},
});

const emit = defineEmits(["on-reassign-user"]);
Expand All @@ -80,18 +88,29 @@ const disabledAssign = ref(false);
// Computed properties
const disabled = computed(() => !selectedUser.value || !comments.value?.trim());

// Load the reassign users
// Load the reassign users using the centralized function with form_data
const loadReassignUsers = async (filter) => {
const response = await getReassignUsers(filter, props.task.id, props.task.user_id);

reassignUsers.value = [];
response.data.forEach((user) => {
reassignUsers.value.push({
text: user.fullname,
value: user.id,
active_tasks_count: user.active_tasks_count,
});
});
try {
const response = await getReassignUsers(
filter,
props.task?.id,
props.formData,
props.currentTaskUserId
);

reassignUsers.value = [];
if (response?.data) {
response.data.forEach((user) => {
reassignUsers.value.push({
text: user.fullname,
value: user.id,
active_tasks_count: user.active_tasks_count,
});
});
}
} catch (error) {
console.error('Error loading reassign users:', error);
}
};

/**
Expand Down
Loading
Loading