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>
108 lines
3.2 KiB
Text
108 lines
3.2 KiB
Text
<cfscript>
|
|
// Get menu data formatted for the builder UI
|
|
// Input: BusinessID
|
|
// Output: { OK: true, MENU: { categories: [...] } }
|
|
|
|
param name="form.BusinessID" default="0";
|
|
param name="url.BusinessID" default="#form.BusinessID#";
|
|
|
|
businessID = val(url.BusinessID);
|
|
|
|
response = { "OK": false };
|
|
|
|
try {
|
|
if (businessID == 0) {
|
|
// Try to get from request body
|
|
requestBody = toString(getHttpRequestData().content);
|
|
if (len(requestBody)) {
|
|
jsonData = deserializeJSON(requestBody);
|
|
businessID = val(jsonData.BusinessID ?: 0);
|
|
}
|
|
}
|
|
|
|
if (businessID == 0) {
|
|
throw("BusinessID is required");
|
|
}
|
|
|
|
// Get categories
|
|
categories = queryExecute("
|
|
SELECT CategoryID, CategoryName, CategoryDescription, CategorySortOrder
|
|
FROM Categories
|
|
WHERE CategoryBusinessID = :businessID
|
|
ORDER BY CategorySortOrder, CategoryName
|
|
", { businessID: businessID });
|
|
|
|
menuCategories = [];
|
|
|
|
for (cat in categories) {
|
|
// Get items for this category (without Tasks join which may not exist)
|
|
items = queryExecute("
|
|
SELECT i.ItemID, i.ItemName, i.ItemDescription, i.ItemPrice,
|
|
i.ItemIsActive, i.ItemSortOrder
|
|
FROM Items i
|
|
WHERE i.ItemCategoryID = :categoryID
|
|
AND i.ItemParentItemID = 0
|
|
ORDER BY i.ItemSortOrder, i.ItemName
|
|
", { categoryID: cat.CategoryID });
|
|
|
|
categoryItems = [];
|
|
|
|
for (item in items) {
|
|
// Get modifiers for this item
|
|
modifiers = queryExecute("
|
|
SELECT ItemID, ItemName, ItemPrice, ItemIsCheckedByDefault, ItemSortOrder
|
|
FROM Items
|
|
WHERE ItemParentItemID = :itemID
|
|
ORDER BY ItemSortOrder, ItemName
|
|
", { itemID: item.ItemID });
|
|
|
|
itemModifiers = [];
|
|
for (mod in modifiers) {
|
|
arrayAppend(itemModifiers, {
|
|
"id": mod.ItemID,
|
|
"name": mod.ItemName,
|
|
"price": mod.ItemPrice,
|
|
"isDefault": mod.ItemIsCheckedByDefault == 1,
|
|
"sortOrder": mod.ItemSortOrder
|
|
});
|
|
}
|
|
|
|
arrayAppend(categoryItems, {
|
|
"id": item.ItemID,
|
|
"name": item.ItemName,
|
|
"description": item.ItemDescription ?: "",
|
|
"price": item.ItemPrice,
|
|
"imageUrl": "",
|
|
"photoTaskId": "",
|
|
"modifiers": itemModifiers,
|
|
"sortOrder": item.ItemSortOrder
|
|
});
|
|
}
|
|
|
|
arrayAppend(menuCategories, {
|
|
"id": cat.CategoryID,
|
|
"name": cat.CategoryName,
|
|
"description": cat.CategoryDescription ?: "",
|
|
"sortOrder": cat.CategorySortOrder,
|
|
"items": categoryItems
|
|
});
|
|
}
|
|
|
|
response = {
|
|
"OK": true,
|
|
"MENU": {
|
|
"categories": menuCategories
|
|
}
|
|
};
|
|
|
|
} catch (any e) {
|
|
response = {
|
|
"OK": false,
|
|
"ERROR": e.message,
|
|
"DETAIL": e.detail ?: ""
|
|
};
|
|
}
|
|
|
|
cfheader(name="Content-Type", value="application/json");
|
|
writeOutput(serializeJSON(response));
|
|
</cfscript>
|