/** * Create an admin rating for a worker on a completed task * * POST: { * TaskID: 123, * AdminUserID: 456, * onTime: true/false, * completedScope: true/false, * requiredFollowup: true/false, * continueAllow: true/false * } */ function readJsonBody() { var raw = getHttpRequestData().content; if (isNull(raw) || len(trim(raw)) == 0) return {}; try { var data = deserializeJSON(raw); return isStruct(data) ? data : {}; } catch (any e) { return {}; } } function generateToken() { return lcase(replace(createUUID(), "-", "", "all")); } try { data = readJsonBody(); taskID = val(structKeyExists(data, "TaskID") ? data.TaskID : 0); adminUserID = val(structKeyExists(data, "AdminUserID") ? data.AdminUserID : 0); if (taskID == 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "missing_task", "MESSAGE": "TaskID is required." })); abort; } if (adminUserID == 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "missing_admin", "MESSAGE": "AdminUserID is required." })); abort; } // Verify task exists and is completed qTask = queryExecute(" SELECT t.TaskID, t.TaskClaimedByUserID, t.TaskCompletedOn, t.TaskBusinessID FROM Tasks t WHERE t.TaskID = :taskID ", { taskID: taskID }); if (qTask.recordCount == 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "not_found", "MESSAGE": "Task not found." })); abort; } if (len(trim(qTask.TaskCompletedOn)) == 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "not_completed", "MESSAGE": "Task has not been completed yet." })); abort; } workerUserID = qTask.TaskClaimedByUserID; if (workerUserID == 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "no_worker", "MESSAGE": "No worker assigned to this task." })); abort; } // Check if admin rating already exists for this task qExisting = queryExecute(" SELECT TaskRatingID FROM TaskRatings WHERE TaskRatingTaskID = :taskID AND TaskRatingDirection = 'admin_rates_worker' LIMIT 1 ", { taskID: taskID }); if (qExisting.recordCount > 0) { writeOutput(serializeJSON({ "OK": false, "ERROR": "already_rated", "MESSAGE": "This task has already been rated by an admin." })); abort; } // Insert the admin rating (completed immediately since admin submits directly) token = generateToken(); queryExecute(" INSERT INTO TaskRatings ( TaskRatingTaskID, TaskRatingByUserID, TaskRatingForUserID, TaskRatingDirection, TaskRatingOnTime, TaskRatingCompletedScope, TaskRatingRequiredFollowup, TaskRatingContinueAllow, TaskRatingAccessToken, TaskRatingExpiresOn, TaskRatingCompletedOn ) VALUES ( :taskID, :adminUserID, :workerUserID, 'admin_rates_worker', :onTime, :completedScope, :requiredFollowup, :continueAllow, :token, DATE_ADD(NOW(), INTERVAL 24 HOUR), NOW() ) ", { taskID: taskID, adminUserID: adminUserID, workerUserID: workerUserID, onTime: { value: structKeyExists(data,"onTime") ? (data.onTime ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"onTime") }, completedScope: { value: structKeyExists(data,"completedScope") ? (data.completedScope ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"completedScope") }, requiredFollowup: { value: structKeyExists(data,"requiredFollowup") ? (data.requiredFollowup ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"requiredFollowup") }, continueAllow: { value: structKeyExists(data,"continueAllow") ? (data.continueAllow ? 1 : 0) : javaCast("null",""), cfsqltype: "cf_sql_tinyint", null: !structKeyExists(data,"continueAllow") }, token: token }); ratingID = queryExecute("SELECT LAST_INSERT_ID() AS id", {}).id; writeOutput(serializeJSON({ "OK": true, "MESSAGE": "Rating submitted successfully.", "RatingID": ratingID })); } catch (any e) { writeOutput(serializeJSON({ "OK": false, "ERROR": "server_error", "MESSAGE": "Error creating rating", "DETAIL": e.message })); }