Task System: - Tasks auto-created when KDS marks order Ready (status 3) - Duplicate task prevention via TaskOrderID check - Task completion now marks associated order as Completed (status 4) - Fixed isNull() check for TaskCompletedOn (use len() instead) - Added TaskOrderID to task queries for order linking Worker APIs: - api/workers/myBusinesses.cfm with GROUP BY to prevent duplicates - api/tasks/listMine.cfm for worker's claimed tasks with filters - api/tasks/complete.cfm updates both task and order status - api/tasks/accept.cfm for claiming tasks KDS/Portal: - KDS only shows orders with status < 4 - Portal dashboard improvements Admin/Debug: - Debug endpoints for tasks and businesses - Test data reset endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
158 lines
6.2 KiB
Text
158 lines
6.2 KiB
Text
<cfscript>
|
|
// Save menu data from the builder UI
|
|
// Input: BusinessID, Menu (JSON structure)
|
|
// Output: { OK: true }
|
|
|
|
response = { "OK": false };
|
|
|
|
try {
|
|
requestBody = toString(getHttpRequestData().content);
|
|
if (!len(requestBody)) {
|
|
throw("Request body is required");
|
|
}
|
|
|
|
jsonData = deserializeJSON(requestBody);
|
|
businessID = val(jsonData.BusinessID ?: 0);
|
|
menu = jsonData.Menu ?: {};
|
|
|
|
if (businessID == 0) {
|
|
throw("BusinessID is required");
|
|
}
|
|
|
|
if (!structKeyExists(menu, "categories") || !isArray(menu.categories)) {
|
|
throw("Menu categories are required");
|
|
}
|
|
|
|
// Process each category
|
|
for (cat in menu.categories) {
|
|
categoryID = 0;
|
|
|
|
// Check if it's an existing category (numeric ID) or new (temp_ prefix)
|
|
if (isNumeric(cat.id)) {
|
|
categoryID = val(cat.id);
|
|
// Update existing category
|
|
queryExecute("
|
|
UPDATE Categories
|
|
SET CategoryName = :name,
|
|
CategoryDescription = :description,
|
|
CategorySortOrder = :sortOrder
|
|
WHERE CategoryID = :categoryID
|
|
", {
|
|
categoryID: categoryID,
|
|
name: cat.name,
|
|
description: cat.description ?: "",
|
|
sortOrder: val(cat.sortOrder ?: 0)
|
|
});
|
|
} else {
|
|
// Insert new category
|
|
queryExecute("
|
|
INSERT INTO Categories (CategoryBusinessID, CategoryName, CategoryDescription, CategorySortOrder)
|
|
VALUES (:businessID, :name, :description, :sortOrder)
|
|
", {
|
|
businessID: businessID,
|
|
name: cat.name,
|
|
description: cat.description ?: "",
|
|
sortOrder: val(cat.sortOrder ?: 0)
|
|
});
|
|
|
|
// Get the new category ID
|
|
result = queryExecute("SELECT LAST_INSERT_ID() as newID");
|
|
categoryID = result.newID;
|
|
}
|
|
|
|
// Process items in this category
|
|
if (structKeyExists(cat, "items") && isArray(cat.items)) {
|
|
for (item in cat.items) {
|
|
itemID = 0;
|
|
|
|
if (isNumeric(item.id)) {
|
|
itemID = val(item.id);
|
|
// Update existing item (without ImageURL which may not exist)
|
|
queryExecute("
|
|
UPDATE Items
|
|
SET ItemName = :name,
|
|
ItemDescription = :description,
|
|
ItemPrice = :price,
|
|
ItemCategoryID = :categoryID,
|
|
ItemSortOrder = :sortOrder
|
|
WHERE ItemID = :itemID
|
|
", {
|
|
itemID: itemID,
|
|
name: item.name,
|
|
description: item.description ?: "",
|
|
price: val(item.price ?: 0),
|
|
categoryID: categoryID,
|
|
sortOrder: val(item.sortOrder ?: 0)
|
|
});
|
|
} else {
|
|
// Insert new item (without ImageURL which may not exist)
|
|
queryExecute("
|
|
INSERT INTO Items (ItemBusinessID, ItemCategoryID, ItemName, ItemDescription, ItemPrice, ItemSortOrder, ItemIsActive, ItemParentItemID)
|
|
VALUES (:businessID, :categoryID, :name, :description, :price, :sortOrder, 1, 0)
|
|
", {
|
|
businessID: businessID,
|
|
categoryID: categoryID,
|
|
name: item.name,
|
|
description: item.description ?: "",
|
|
price: val(item.price ?: 0),
|
|
sortOrder: val(item.sortOrder ?: 0)
|
|
});
|
|
|
|
result = queryExecute("SELECT LAST_INSERT_ID() as newID");
|
|
itemID = result.newID;
|
|
}
|
|
|
|
// Process modifiers for this item
|
|
if (structKeyExists(item, "modifiers") && isArray(item.modifiers)) {
|
|
for (mod in item.modifiers) {
|
|
if (isNumeric(mod.id)) {
|
|
// Update existing modifier
|
|
queryExecute("
|
|
UPDATE Items
|
|
SET ItemName = :name,
|
|
ItemPrice = :price,
|
|
ItemIsCheckedByDefault = :isDefault,
|
|
ItemSortOrder = :sortOrder
|
|
WHERE ItemID = :modID
|
|
", {
|
|
modID: val(mod.id),
|
|
name: mod.name,
|
|
price: val(mod.price ?: 0),
|
|
isDefault: (mod.isDefault ?: false) ? 1 : 0,
|
|
sortOrder: val(mod.sortOrder ?: 0)
|
|
});
|
|
} else {
|
|
// Insert new modifier
|
|
queryExecute("
|
|
INSERT INTO Items (ItemBusinessID, ItemCategoryID, ItemParentItemID, ItemName, ItemPrice, ItemIsCheckedByDefault, ItemSortOrder, ItemIsActive)
|
|
VALUES (:businessID, :categoryID, :parentID, :name, :price, :isDefault, :sortOrder, 1)
|
|
", {
|
|
businessID: businessID,
|
|
categoryID: categoryID,
|
|
parentID: itemID,
|
|
name: mod.name,
|
|
price: val(mod.price ?: 0),
|
|
isDefault: (mod.isDefault ?: false) ? 1 : 0,
|
|
sortOrder: val(mod.sortOrder ?: 0)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
response = { "OK": true };
|
|
|
|
} catch (any e) {
|
|
response = {
|
|
"OK": false,
|
|
"ERROR": e.message,
|
|
"DETAIL": e.detail ?: "",
|
|
"TYPE": e.type ?: ""
|
|
};
|
|
}
|
|
|
|
cfheader(name="Content-Type", value="application/json");
|
|
writeOutput(serializeJSON(response));
|
|
</cfscript>
|