// Enable or disable a scheduled task // Input: BusinessID (required), ScheduledTaskID (required), IsActive (required) // Output: { OK: true } function apiAbort(required struct payload) { writeOutput(serializeJSON(payload)); abort; } function readJsonBody() { var raw = getHttpRequestData().content; if (isNull(raw)) raw = ""; if (!len(trim(raw))) return {}; try { var data = deserializeJSON(raw); if (isStruct(data)) return data; } catch (any e) {} return {}; } // Calculate next run time from cron expression function calculateNextRun(required string cronExpression) { var parts = listToArray(cronExpression, " "); if (arrayLen(parts) != 5) { return dateAdd("d", 1, now()); } var cronMinute = parts[1]; var cronHour = parts[2]; var cronDay = parts[3]; var cronMonth = parts[4]; var cronWeekday = parts[5]; var checkDate = dateAdd("n", 1, now()); checkDate = createDateTime(year(checkDate), month(checkDate), day(checkDate), hour(checkDate), minute(checkDate), 0); var maxIterations = 400 * 24 * 60; var iterations = 0; while (iterations < maxIterations) { var matchMinute = (cronMinute == "*" || (isNumeric(cronMinute) && minute(checkDate) == int(cronMinute))); var matchHour = (cronHour == "*" || (isNumeric(cronHour) && hour(checkDate) == int(cronHour))); var matchDay = (cronDay == "*" || (isNumeric(cronDay) && day(checkDate) == int(cronDay))); var matchMonth = (cronMonth == "*" || (isNumeric(cronMonth) && month(checkDate) == int(cronMonth))); var dow = dayOfWeek(checkDate) - 1; var matchWeekday = (cronWeekday == "*"); if (!matchWeekday) { if (find("-", cronWeekday)) { var range = listToArray(cronWeekday, "-"); if (arrayLen(range) == 2 && isNumeric(range[1]) && isNumeric(range[2])) { matchWeekday = (dow >= int(range[1]) && dow <= int(range[2])); } } else if (isNumeric(cronWeekday)) { matchWeekday = (dow == int(cronWeekday)); } } if (matchMinute && matchHour && matchDay && matchMonth && matchWeekday) { return checkDate; } checkDate = dateAdd("n", 1, checkDate); iterations++; } return dateAdd("d", 1, now()); } try { data = readJsonBody(); // Get BusinessID businessID = 0; httpHeaders = getHttpRequestData().headers; if (structKeyExists(data, "BusinessID") && isNumeric(data.BusinessID)) { businessID = int(data.BusinessID); } else if (structKeyExists(httpHeaders, "X-Business-ID") && isNumeric(httpHeaders["X-Business-ID"])) { businessID = int(httpHeaders["X-Business-ID"]); } if (businessID == 0) { apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "BusinessID is required" }); } // Get task ID and new state taskID = structKeyExists(data, "ScheduledTaskID") && isNumeric(data.ScheduledTaskID) ? int(data.ScheduledTaskID) : 0; isActive = structKeyExists(data, "IsActive") ? (data.IsActive ? 1 : 0) : 0; if (taskID == 0) { apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "ScheduledTaskID is required" }); } // Verify exists and get cron expression qCheck = queryExecute(" SELECT ScheduledTaskID, ScheduledTaskCronExpression as CronExpression FROM ScheduledTaskDefinitions WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID ", { id: { value: taskID, cfsqltype: "cf_sql_integer" }, businessID: { value: businessID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); if (qCheck.recordCount == 0) { apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Scheduled task not found" }); } // If enabling, recalculate next run time nextRunUpdate = ""; if (isActive) { nextRunOn = calculateNextRun(qCheck.CronExpression); nextRunUpdate = ", ScheduledTaskNextRunOn = :nextRun"; } // Update status if (isActive) { queryExecute(" UPDATE ScheduledTaskDefinitions SET ScheduledTaskIsActive = :isActive, ScheduledTaskNextRunOn = :nextRun WHERE ScheduledTaskID = :id ", { isActive: { value: isActive, cfsqltype: "cf_sql_bit" }, nextRun: { value: nextRunOn, cfsqltype: "cf_sql_timestamp" }, id: { value: taskID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); } else { queryExecute(" UPDATE ScheduledTaskDefinitions SET ScheduledTaskIsActive = :isActive WHERE ScheduledTaskID = :id ", { isActive: { value: isActive, cfsqltype: "cf_sql_bit" }, id: { value: taskID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); } apiAbort({ "OK": true, "MESSAGE": isActive ? "Scheduled task enabled" : "Scheduled task disabled" }); } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": e.message }); }