get('/department/selfserve/tasks', function () { global $response; $this->requirePermission('list_department_selfserve_tasks'); $user = (new authentication())->get_user(); if ($user) { (new logs_o())->add('department_selfserve_tasks', 'global', 1, $user->id, 'LIST_TASKS', 'User listed department self-serve tasks'); $tasks_o = new department_selfserve_tasks_o(); $authorized_department_ids = $user->getGroup()->getDepartments(); $has_view_all_permission = $this->hasPermission('view_all_department_selfserve_tasks'); // If an ID is provided, return that specific task if (self::isParametersSet(['id'])) { $tasks_o->select((int)self::getParameter('id')); if ($tasks_o->exists()) { if (!$this->canAccessDepartment($authorized_department_ids, (int)$tasks_o->department->value())) { if (!$has_view_all_permission) { $this->forbidDepartmentAccess((int)$tasks_o->department->value(), ['view_all_department_selfserve_tasks']); } } $response->success($tasks_o->asArray()); } else { $response->error('Task not found', 404); } } $filters = []; if (self::isParametersSet(['department'])) { $requested_department = (int)self::getParameter('department'); if (!$this->canAccessDepartment($authorized_department_ids, $requested_department) && !$has_view_all_permission) { $this->forbidDepartmentAccess($requested_department, ['view_all_department_selfserve_tasks']); } $filters['department'] = $requested_department; } else { if (!$has_view_all_permission) { if (empty($authorized_department_ids)) { $authorized_department_ids = [0]; } else { $authorized_department_ids[] = 0; } $filters['department'] = $authorized_department_ids; } } if (self::isParametersSet(['lane'])) { $filters['lane'] = (int)self::getParameter('lane'); } if (self::isParametersSet(['product'])) { $filters['product'] = (int)self::getParameter('product'); } if (self::isParametersSet(['condition_id'])) { $filters['condition_id'] = (int)self::getParameter('condition_id'); } if (self::isParametersSet(['gate_type'])) { $filters['gate_type'] = (string)self::getParameter('gate_type'); } if (self::isParametersSet(['gate_ref_id'])) { $filters['gate_ref_id'] = (int)self::getParameter('gate_ref_id'); } if (self::isParametersSet(['machine_type_id'])) { $filters['machine_type_id'] = (int)self::getParameter('machine_type_id'); } $response->success( $tasks_o->setSearchableFields(['id', 'department', 'lane', 'product', 'machine_type_id', 'condition_id', 'gate_type', 'gate_ref_id', 'task', 'description', 'deleted_at']) ->listObjectsWithPaginationIfSet(function ($task) { $t = new department_selfserve_tasks_o(); $t->select((int)$task['id']); return $t->asArray(); }, $tasks_o->forceRestrictFilters($filters)) ); } else { $response->error('Invalid session', 400); } }, [ 'list_department_selfserve_tasks' => 'List all department self-serve tasks', 'view_all_department_selfserve_tasks' => 'View all department self-serve tasks' ]); /** * Add a department self-serve task */ $this->post('/department/selfserve/tasks', function () { global $response; $this->requirePermission('add_department_selfserve_tasks'); $user = (new authentication())->get_user(); if ($user) { $department = $response->isRequestParameterSet('department') ? (int)$response->getRequestParameter('department') : 0; $lane = $response->isRequestParameterSet('lane') ? (int)$response->getRequestParameter('lane') : 0; $product = $response->isRequestParameterSet('product') ? (int)$response->getRequestParameter('product') : 0; $machine_type_id = null; if ($response->isRequestParameterSet('machine_type_id')) { $machine_type_param = $response->getRequestParameter('machine_type_id'); if (!is_null($machine_type_param) && $machine_type_param !== '' && $machine_type_param !== 'null') { $machine_type_id = (int)$machine_type_param; } } $question_id = null; if ($response->isRequestParameterSet('condition_id')) { $condition_id_param = $response->getRequestParameter('condition_id'); if (!is_null($condition_id_param) && $condition_id_param !== '' && $condition_id_param !== 'null') { $question_id = (int)$condition_id_param; } } $gate_type = null; if ($response->isRequestParameterSet('gate_type')) { try { $gate_type = department_selfserve_tasks_o::normalizeGateTypeInput($response->getRequestParameter('gate_type')); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } $gate_ref_id = null; if ($response->isRequestParameterSet('gate_ref_id')) { $gate_ref_param = $response->getRequestParameter('gate_ref_id'); if (!is_null($gate_ref_param) && $gate_ref_param !== '' && $gate_ref_param !== 'null') { $gate_ref_id = (int)$gate_ref_param; } } if ($gate_type !== null) { if ($gate_type === selfserve_task_gate_type::ALWAYS) { $gate_ref_id = null; $question_id = null; } else { if ($gate_ref_id === null) { $gate_ref_id = $question_id; } if ($gate_ref_id === null || $gate_ref_id <= 0) { $response->error('gate_ref_id is required when gate_type is CONDITION or QUESTION', 400); } // Keep legacy contract field in sync. $question_id = $gate_ref_id; } } elseif ($gate_ref_id !== null && $gate_ref_id > 0) { $question_id = $gate_ref_id; } $task = (string)$response->getRequestParameter('task'); $description = (string)$response->getRequestParameter('description'); $order_priority = (int)($response->getRequestParameter('order_priority') ?? 0); // Optional services $services_param = $response->isRequestParameterSet('services') ? $response->getRequestParameter('services') : null; $services_enums = null; if (!is_null($services_param)) { // Accept array, JSON string, or comma-separated string $raw = $services_param; if (is_string($raw)) { $decoded = json_decode($raw, true); if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $raw = $decoded; } else { $raw = array_filter(array_map(function ($s) { return trim($s); }, explode(',', $services_param)), fn($s) => $s !== ''); } } if (!is_array($raw)) { $response->error('Invalid format for services. Expected array, JSON array, or comma-separated string of service names.', 400); } // Map to enum cases by name (case-insensitive) $services_enums = []; foreach ($raw as $srv) { $name = strtoupper(is_string($srv) ? trim($srv) : (string)$srv); $matched = null; foreach (selfserve_lane_services::cases() as $case) { if ($case->name === $name) { $matched = $case; break; } } if (!$matched) { $response->error('Invalid service: ' . (string)$srv, 400); } $services_enums[] = $matched; } } // Optional buttons $buttons_param = $response->isRequestParameterSet('buttons') ? $response->getRequestParameter('buttons') : null; $buttons_ids = null; if (!is_null($buttons_param)) { try { $buttons_ids = department_selfserve_tasks_o::normalizeButtonsInput($buttons_param); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } // Optional dynamic_images_vehicle_type $vehicle_type_param = $response->isRequestParameterSet('dynamic_images_vehicle_type') ? $response->getRequestParameter('dynamic_images_vehicle_type') : null; $vehicle_type = null; if ($vehicle_type_param !== null) { try { $vehicle_type = department_selfserve_tasks_o::normalizeVehicleTypeInput($vehicle_type_param); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } if (!$task || !$description) { $response->error('Missing required fields: task and description', 400); } if ($machine_type_id === null && (!$department || !$lane || !$product)) { $response->error('Missing required fields: either machine_type_id or department, lane, and product', 400); } $authorized_department_ids = $user->getGroup()->getDepartments(); if (!$this->canAccessDepartment($authorized_department_ids, $department)) { $this->forbidDepartmentAccess($department); } try { $task_o = (new department_selfserve_tasks_o())->add( $department, $lane, $product, $question_id, $task, $description, $order_priority, $services_enums, $buttons_ids, $vehicle_type, $machine_type_id, $gate_type, $gate_ref_id, ); (new selfserve_config_versioning())->syncDraftFromLegacyForDepartment($department); (new logs_o())->add('department_selfserve_tasks', 'global', 1, $user->id, 'ADD_TASK', 'User added a department self-serve task: ' . $task); $response->success($task_o->asArray()); } catch (\Exception $e) { $response->error($e->getMessage(), 500); } } else { $response->error('Invalid session', 400); } }, [ 'add_department_selfserve_tasks' => 'Add a department self-serve task' ]); /** * Update a department self-serve task */ $this->put('/department/selfserve/tasks', function () { global $response; $this->requirePermission('edit_department_selfserve_tasks'); $user = (new authentication())->get_user(); if ($user) { self::requireParameters(['id']); $id = (int)self::getParameter('id'); $task_o = new department_selfserve_tasks_o(); $task_o->select($id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $originalDepartment = (int)$task_o->department->value(); $authorized_department_ids = $user->getGroup()->getDepartments(); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { $this->forbidDepartmentAccess((int)$task_o->department->value()); } if (self::isParametersSet(['department'])) { $new_department = (int)self::getParameter('department'); if (!$this->canAccessDepartment($authorized_department_ids, $new_department)) { $this->forbidDepartmentAccess($new_department); } $task_o->department->set($new_department); } if (self::isParametersSet(['lane'])) { $task_o->lane->set((int)self::getParameter('lane')); } if (self::isParametersSet(['product'])) { $task_o->product->set((int)self::getParameter('product')); } if (self::isParametersSet(['machine_type_id'])) { $param = self::getParameter('machine_type_id'); $task_o->machine_type_id->set($param === null || $param === '' || $param === 'null' ? null : (int)$param); } if (self::isParametersSet(['condition_id'])) { $param = self::getParameter('condition_id'); $task_o->setQuestionId($param === null || $param === '' || $param === 'null' ? null : (int)$param); } if (self::isParametersSet(['gate_type']) || self::isParametersSet(['gate_ref_id'])) { try { $effectiveGateType = self::isParametersSet(['gate_type']) ? department_selfserve_tasks_o::normalizeGateTypeInput(self::getParameter('gate_type')) : department_selfserve_tasks_o::normalizeGateTypeInput((string)($task_o->gate_type->value() ?? selfserve_task_gate_type::ALWAYS->value)); $effectiveGateRefId = self::isParametersSet(['gate_ref_id']) ? ( (self::getParameter('gate_ref_id') === null || self::getParameter('gate_ref_id') === '' || self::getParameter('gate_ref_id') === 'null') ? null : (int)self::getParameter('gate_ref_id') ) : ($task_o->gate_ref_id->value() === null ? null : (int)$task_o->gate_ref_id->value()); if (!self::isParametersSet(['gate_ref_id']) && self::isParametersSet(['condition_id']) && $effectiveGateType !== selfserve_task_gate_type::ALWAYS) { $conditionParam = self::getParameter('condition_id'); $effectiveGateRefId = ($conditionParam === null || $conditionParam === '' || $conditionParam === 'null') ? null : (int)$conditionParam; } if ($effectiveGateType === selfserve_task_gate_type::ALWAYS) { $effectiveGateRefId = null; } elseif ($effectiveGateRefId === null || $effectiveGateRefId <= 0) { $response->error('gate_ref_id is required when gate_type is CONDITION or QUESTION', 400); } $task_o->gate_type->set($effectiveGateType->value); $task_o->gate_ref_id->set($effectiveGateRefId); // Preserve legacy contract field. $task_o->condition_id->set($effectiveGateRefId); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } if (self::isParametersSet(['task'])) { $task_o->task->set((string)self::getParameter('task')); } if (self::isParametersSet(['description'])) { $task_o->description->set((string)self::getParameter('description')); } if (self::isParametersSet(['order_priority'])) { $task_o->order_priority->set((int)self::getParameter('order_priority')); } if (self::isParametersSet(['services'])) { $param = self::getParameter('services'); if ($param === null) { // Explicitly clear services $task_o->services->set(null); } else { $raw = $param; if (is_string($raw)) { $decoded = json_decode($raw, true); if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $raw = $decoded; } else { $raw = array_filter(array_map(function ($s) { return trim($s); }, explode(',', $param)), fn($s) => $s !== ''); } } if (!is_array($raw)) { $response->error('Invalid format for services. Expected array, JSON array, or comma-separated string of service names.', 400); } // Validate and store names $names = []; foreach ($raw as $srv) { $name = strtoupper(is_string($srv) ? trim($srv) : (string)$srv); $valid = false; foreach (selfserve_lane_services::cases() as $case) { if ($case->name === $name) { $valid = true; break; } } if (!$valid) { $response->error('Invalid service: ' . (string)$srv, 400); } $names[] = $name; } $task_o->services->set($names); } } if (self::isParametersSet(['buttons'])) { $param = self::getParameter('buttons'); if ($param === null) { // Explicitly clear buttons $task_o->buttons->set(null); } else { try { $ids = department_selfserve_tasks_o::normalizeButtonsInput($param); $task_o->buttons->set($ids); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } } // Optional dynamic_images_vehicle_type update if (self::isParametersSet(['dynamic_images_vehicle_type'])) { $param = self::getParameter('dynamic_images_vehicle_type'); if ($param === null) { $task_o->dynamic_images_vehicle_type->set(null); } else { try { $val = department_selfserve_tasks_o::normalizeVehicleTypeInput($param); $task_o->dynamic_images_vehicle_type->set($val); } catch (\Exception $e) { $response->error($e->getMessage(), 400); } } } (new selfserve_config_versioning())->syncDraftFromLegacyForDepartment($originalDepartment); (new selfserve_config_versioning())->syncDraftFromLegacyForDepartment((int)$task_o->department->value()); (new logs_o())->add('department_selfserve_tasks', 'global', 1, $user->id, 'EDIT_TASK', 'User updated department self-serve task ID: ' . $id); $response->success($task_o->asArray()); } else { $response->error('Invalid session', 400); } }, [ 'edit_department_selfserve_tasks' => 'Edit a department self-serve task' ]); /** * Delete a department self-serve task */ $this->delete('/department/selfserve/tasks', function () { global $response; $this->requirePermission('delete_department_selfserve_tasks'); $user = (new authentication())->get_user(); if ($user) { self::requireParameters(['id']); $id = (int)self::getParameter('id'); $task_o = new department_selfserve_tasks_o(); $task_o->select($id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $authorized_department_ids = $user->getGroup()->getDepartments(); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { $this->forbidDepartmentAccess((int)$task_o->department->value()); } $task_o->delete(); (new selfserve_config_versioning())->syncDraftFromLegacyForDepartment((int)$task_o->department->value()); (new logs_o())->add('department_selfserve_tasks', 'global', 1, $user->id, 'DELETE_TASK', 'User deleted department self-serve task ID: ' . $id); $response->success('Task deleted'); } else { $response->error('Invalid session', 400); } }, [ 'delete_department_selfserve_tasks' => 'Delete a department self-serve task' ]); /** * Download an attachment for a department self-serve task */ $this->get('/department/selfserve/tasks/attachments/download', function () { global $response; $this->requirePermission('download_department_selfserve_task_attachments'); $user = (new authentication())->get_user(); if ($user) { self::requireParameters(['task_id', 'attachment_id']); $task_id = (int)self::getParameter('task_id'); $attachment_id = (int)self::getParameter('attachment_id'); $task_o = new department_selfserve_tasks_o(); $task_o->select($task_id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $authorized_department_ids = $user->getGroup()->getDepartments(); $has_view_all_permission = $this->hasPermission('view_all_department_selfserve_tasks'); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { if (!$has_view_all_permission) { $this->forbidDepartmentAccess((int)$task_o->department->value(), ['view_all_department_selfserve_tasks']); } } $task_attachments = $task_o->listAttachments(); $task_attachment_ids = array_map(static fn($attachment) => (int)$attachment->id, $task_attachments); if (!in_array($attachment_id, $task_attachment_ids, true)) { $response->error('Attachment not found', 404); } $attachment = $task_o->getAttachment($attachment_id); $attachment_store = new attachment_store(); $attachments = new attachments(); $attachment_formatted = $attachments->format($attachment); $download_link = $attachment_store->generateDirectDownloadUrl($attachment_formatted->content->document); (new logs_o())->add('department_selfserve_tasks', (int)$task_o->department->value(), 1, $user->id, 'DOWNLOAD_TASK_ATTACHMENT', 'User downloaded attachment ID: ' . $attachment_id . ' for task ID: ' . $task_id); $response->success(['download_link' => $download_link]); } else { $response->error('Invalid session', 400); } }, [ 'download_department_selfserve_task_attachments' => 'Download attachments for a department self-serve task', 'view_all_department_selfserve_tasks' => 'View all department self-serve tasks' ]); /** * List attachments for a department self-serve task */ $this->get('/department/selfserve/tasks/attachments', function () { global $response; $this->requirePermission('list_department_selfserve_task_attachments'); $user = (new authentication())->get_user(); if ($user) { self::requireParameters(['id']); $task_id = (int)self::getParameter('id'); $task_o = new department_selfserve_tasks_o(); $task_o->select($task_id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $authorized_department_ids = $user->getGroup()->getDepartments(); $has_view_all_permission = $this->hasPermission('view_all_department_selfserve_tasks'); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { if (!$has_view_all_permission) { $this->forbidDepartmentAccess((int)$task_o->department->value(), ['view_all_department_selfserve_tasks']); } } $attachments = $task_o->listAttachments(); (new logs_o())->add('department_selfserve_tasks', (int)$task_o->department->value(), 1, $user->id, 'LIST_TASK_ATTACHMENTS', 'User listed attachments for task ID: ' . $task_id); $response->success($attachments); } else { $response->error('Invalid session', 400); } }, [ 'list_department_selfserve_task_attachments' => 'List attachments for a department self-serve task', 'view_all_department_selfserve_tasks' => 'View all department self-serve tasks' ]); /** * Upload an attachment for a department self-serve task */ $this->post('/department/selfserve/tasks/attachments/upload', function () { global $response; $this->requirePermission('add_department_selfserve_task_attachments'); $user = (new authentication())->get_user(); if ($user) { $this->requireParameters(['task_id', 'base64_file', 'file_name']); $task_id = (int)$this->getParameter('task_id'); $base64_file = (string)$this->getParameter('base64_file'); $file_name = (string)$this->getParameter('file_name'); $task_o = new department_selfserve_tasks_o(); $task_o->select($task_id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $authorized_department_ids = $user->getGroup()->getDepartments(); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { $this->forbidDepartmentAccess((int)$task_o->department->value()); } $attachment_store = new attachment_store(); $extension = pathinfo($file_name, PATHINFO_EXTENSION); $object_name = $attachment_store->storeTempFileFromBase64($base64_file, $extension); if ($object_name === false) { $response->error('Failed to store the attachment file.', 500); } $object_attachment = $task_o->addAttachment((new attachment_content())->setDocument($object_name)->setOther($file_name)); (new logs_o())->add('department_selfserve_tasks', (int)$task_o->department->value(), 1, $user->id, 'ADD_TASK_ATTACHMENT', 'User added an attachment for task ID: ' . $task_id); $attachments = new attachments(); $response->success($attachments->format($attachments->get($object_attachment->id))); } else { $response->error('Invalid session', 400); } }, [ 'add_department_selfserve_task_attachments' => 'Add attachments for a department self-serve task' ]); /** * Delete an attachment for a department self-serve task */ $this->delete('/department/selfserve/tasks/attachments', function () { global $response; $this->requirePermission('delete_department_selfserve_task_attachments'); $user = (new authentication())->get_user(); if ($user) { self::requireParameters(['task_id', 'attachment_id']); $task_id = (int)self::getParameter('task_id'); $attachment_id = (int)self::getParameter('attachment_id'); $task_o = new department_selfserve_tasks_o(); $task_o->select($task_id); if (!$task_o->exists()) { $response->error('Task not found', 404); } $authorized_department_ids = $user->getGroup()->getDepartments(); if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) { $this->forbidDepartmentAccess((int)$task_o->department->value()); } $task_attachments = $task_o->listAttachments(); $task_attachment_ids = array_map(static fn($attachment) => (int)$attachment->id, $task_attachments); if (!in_array($attachment_id, $task_attachment_ids, true)) { $response->error('Attachment not found', 404); } $task_o->removeAttachment($attachment_id); (new logs_o())->add('department_selfserve_tasks', (int)$task_o->department->value(), 1, $user->id, 'DELETE_TASK_ATTACHMENT', 'User deleted attachment ID: ' . $attachment_id . ' for task ID: ' . $task_id); $response->success(['message' => 'Attachment deleted successfully']); } else { $response->error('Invalid session', 400); } }, [ 'delete_department_selfserve_task_attachments' => 'Delete attachments for a department self-serve task' ]); } private function canAccessDepartment(array $authorizedDepartmentIds, int $departmentId): bool { return $departmentId === 0 || in_array($departmentId, $authorizedDepartmentIds, true); } }