false, 'ERROR' => 'missing_task', 'MESSAGE' => 'TaskID is required.']); if ($adminUserID === 0) apiAbort(['OK' => false, 'ERROR' => 'missing_admin', 'MESSAGE' => 'AdminUserID is required.']); $qTask = queryOne("SELECT ID, ClaimedByUserID, CompletedOn, BusinessID FROM Tasks WHERE ID = ?", [$taskID]); if (!$qTask) apiAbort(['OK' => false, 'ERROR' => 'not_found', 'MESSAGE' => 'Task not found.']); if (empty($qTask['CompletedOn'])) apiAbort(['OK' => false, 'ERROR' => 'not_completed', 'MESSAGE' => 'Task has not been completed yet.']); $workerUserID = (int) $qTask['ClaimedByUserID']; if ($workerUserID === 0) apiAbort(['OK' => false, 'ERROR' => 'no_worker', 'MESSAGE' => 'No worker assigned to this task.']); $qExisting = queryOne("SELECT ID FROM TaskRatings WHERE TaskID = ? AND Direction = 'admin_rates_worker' LIMIT 1", [$taskID]); if ($qExisting) apiAbort(['OK' => false, 'ERROR' => 'already_rated', 'MESSAGE' => 'This task has already been rated by an admin.']); $token = generateSecureToken(); queryTimed(" INSERT INTO TaskRatings (TaskID, ByUserID, ForUserID, Direction, OnTime, CompletedScope, RequiredFollowup, ContinueAllow, AccessToken, ExpiresOn, CompletedOn) VALUES (?, ?, ?, 'admin_rates_worker', ?, ?, ?, ?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR), NOW()) ", [ $taskID, $adminUserID, $workerUserID, isset($data['onTime']) ? ($data['onTime'] ? 1 : 0) : null, isset($data['completedScope']) ? ($data['completedScope'] ? 1 : 0) : null, isset($data['requiredFollowup']) ? ($data['requiredFollowup'] ? 1 : 0) : null, isset($data['continueAllow']) ? ($data['continueAllow'] ? 1 : 0) : null, $token, ]); $ratingID = (int) lastInsertId(); jsonResponse(['OK' => true, 'MESSAGE' => 'Rating submitted successfully.', 'RatingID' => $ratingID]); } catch (Exception $e) { jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => 'Error creating rating', 'DETAIL' => $e->getMessage()]); }