/** * Menu CRUD API * * GET: List all menus for a business * POST: Create or update a menu * DELETE: Soft-delete a menu * * Input: BusinessID, and optionally Menu data for POST */ response = { "OK": false }; function apiAbort(payload) { writeOutput(serializeJSON(payload)); abort; } try { requestBody = toString(getHttpRequestData().content); requestData = {}; if (len(requestBody)) { requestData = deserializeJSON(requestBody); } businessID = 0; if (structKeyExists(requestData, "BusinessID")) { businessID = val(requestData.BusinessID); } if (businessID == 0) { apiAbort({ "OK": false, "ERROR": "missing_business_id", "MESSAGE": "BusinessID is required" }); } method = cgi.REQUEST_METHOD; action = structKeyExists(requestData, "action") ? lCase(requestData.action) : "list"; // Handle different actions switch (action) { case "list": // Get all active menus for this business qMenus = queryExecute(" SELECT MenuID, MenuName, MenuDescription, MenuDaysActive, MenuStartTime, MenuEndTime, MenuSortOrder, MenuIsActive FROM Menus WHERE MenuBusinessID = :businessID AND MenuIsActive = 1 ORDER BY MenuSortOrder, MenuName ", { businessID: businessID }, { datasource: "payfrit" }); menus = []; for (i = 1; i <= qMenus.recordCount; i++) { // Count categories in this menu qCatCount = queryExecute(" SELECT COUNT(*) as cnt FROM Categories WHERE CategoryBusinessID = :businessID AND CategoryMenuID = :menuID ", { businessID: businessID, menuID: qMenus.MenuID[i] }, { datasource: "payfrit" }); arrayAppend(menus, { "MenuID": qMenus.MenuID[i], "MenuName": qMenus.MenuName[i], "MenuDescription": isNull(qMenus.MenuDescription[i]) ? "" : qMenus.MenuDescription[i], "MenuDaysActive": qMenus.MenuDaysActive[i], "MenuStartTime": isNull(qMenus.MenuStartTime[i]) ? "" : timeFormat(qMenus.MenuStartTime[i], "HH:mm"), "MenuEndTime": isNull(qMenus.MenuEndTime[i]) ? "" : timeFormat(qMenus.MenuEndTime[i], "HH:mm"), "MenuSortOrder": qMenus.MenuSortOrder[i], "CategoryCount": qCatCount.cnt }); } response = { "OK": true, "MENUS": menus, "COUNT": arrayLen(menus) }; break; case "get": // Get a single menu by ID menuID = structKeyExists(requestData, "MenuID") ? val(requestData.MenuID) : 0; if (menuID == 0) { apiAbort({ "OK": false, "ERROR": "missing_menu_id", "MESSAGE": "MenuID is required" }); } qMenu = queryExecute(" SELECT * FROM Menus WHERE MenuID = :menuID AND MenuBusinessID = :businessID ", { menuID: menuID, businessID: businessID }, { datasource: "payfrit" }); if (qMenu.recordCount == 0) { apiAbort({ "OK": false, "ERROR": "menu_not_found", "MESSAGE": "Menu not found" }); } response = { "OK": true, "MENU": { "MenuID": qMenu.MenuID, "MenuName": qMenu.MenuName, "MenuDescription": isNull(qMenu.MenuDescription) ? "" : qMenu.MenuDescription, "MenuDaysActive": qMenu.MenuDaysActive, "MenuStartTime": isNull(qMenu.MenuStartTime) ? "" : timeFormat(qMenu.MenuStartTime, "HH:mm"), "MenuEndTime": isNull(qMenu.MenuEndTime) ? "" : timeFormat(qMenu.MenuEndTime, "HH:mm"), "MenuSortOrder": qMenu.MenuSortOrder, "MenuIsActive": qMenu.MenuIsActive } }; break; case "save": // Create or update a menu menuID = structKeyExists(requestData, "MenuID") ? val(requestData.MenuID) : 0; menuName = structKeyExists(requestData, "MenuName") ? trim(requestData.MenuName) : ""; menuDescription = structKeyExists(requestData, "MenuDescription") ? trim(requestData.MenuDescription) : ""; menuDaysActive = structKeyExists(requestData, "MenuDaysActive") ? val(requestData.MenuDaysActive) : 127; menuStartTime = structKeyExists(requestData, "MenuStartTime") && len(trim(requestData.MenuStartTime)) ? trim(requestData.MenuStartTime) : javaCast("null", ""); menuEndTime = structKeyExists(requestData, "MenuEndTime") && len(trim(requestData.MenuEndTime)) ? trim(requestData.MenuEndTime) : javaCast("null", ""); menuSortOrder = structKeyExists(requestData, "MenuSortOrder") ? val(requestData.MenuSortOrder) : 0; if (len(menuName) == 0) { apiAbort({ "OK": false, "ERROR": "missing_menu_name", "MESSAGE": "Menu name is required" }); } if (menuID > 0) { // Update existing menu queryExecute(" UPDATE Menus SET MenuName = :menuName, MenuDescription = :menuDescription, MenuDaysActive = :menuDaysActive, MenuStartTime = :menuStartTime, MenuEndTime = :menuEndTime, MenuSortOrder = :menuSortOrder WHERE MenuID = :menuID AND MenuBusinessID = :businessID ", { menuID: menuID, businessID: businessID, menuName: menuName, menuDescription: menuDescription, menuDaysActive: menuDaysActive, menuStartTime: menuStartTime, menuEndTime: menuEndTime, menuSortOrder: menuSortOrder }, { datasource: "payfrit" }); response = { "OK": true, "MenuID": menuID, "ACTION": "updated" }; } else { // Create new menu queryExecute(" INSERT INTO Menus ( MenuBusinessID, MenuName, MenuDescription, MenuDaysActive, MenuStartTime, MenuEndTime, MenuSortOrder, MenuIsActive, MenuAddedOn ) VALUES ( :businessID, :menuName, :menuDescription, :menuDaysActive, :menuStartTime, :menuEndTime, :menuSortOrder, 1, NOW() ) ", { businessID: businessID, menuName: menuName, menuDescription: menuDescription, menuDaysActive: menuDaysActive, menuStartTime: menuStartTime, menuEndTime: menuEndTime, menuSortOrder: menuSortOrder }, { datasource: "payfrit" }); result = queryExecute("SELECT LAST_INSERT_ID() as newID", {}, { datasource: "payfrit" }); response = { "OK": true, "MenuID": result.newID, "ACTION": "created" }; } break; case "delete": // Soft-delete a menu menuID = structKeyExists(requestData, "MenuID") ? val(requestData.MenuID) : 0; if (menuID == 0) { apiAbort({ "OK": false, "ERROR": "missing_menu_id", "MESSAGE": "MenuID is required" }); } // Check if menu has categories qCatCheck = queryExecute(" SELECT COUNT(*) as cnt FROM Categories WHERE CategoryMenuID = :menuID ", { menuID: menuID }, { datasource: "payfrit" }); if (qCatCheck.cnt > 0) { apiAbort({ "OK": false, "ERROR": "menu_has_categories", "MESSAGE": "Cannot delete menu with categories. Move or delete categories first.", "CATEGORY_COUNT": qCatCheck.cnt }); } queryExecute(" UPDATE Menus SET MenuIsActive = 0 WHERE MenuID = :menuID AND MenuBusinessID = :businessID ", { menuID: menuID, businessID: businessID }, { datasource: "payfrit" }); response = { "OK": true, "MenuID": menuID, "ACTION": "deleted" }; break; case "reorder": // Reorder menus menuOrder = structKeyExists(requestData, "MenuOrder") ? requestData.MenuOrder : []; if (!isArray(menuOrder) || arrayLen(menuOrder) == 0) { apiAbort({ "OK": false, "ERROR": "missing_menu_order", "MESSAGE": "MenuOrder array is required" }); } for (i = 1; i <= arrayLen(menuOrder); i++) { queryExecute(" UPDATE Menus SET MenuSortOrder = :sortOrder WHERE MenuID = :menuID AND MenuBusinessID = :businessID ", { menuID: val(menuOrder[i]), businessID: businessID, sortOrder: i - 1 }, { datasource: "payfrit" }); } response = { "OK": true, "ACTION": "reordered" }; break; default: apiAbort({ "OK": false, "ERROR": "invalid_action", "MESSAGE": "Unknown action: " & action }); } } catch (any e) { response["ERROR"] = "server_error"; response["MESSAGE"] = e.message; response["DETAIL"] = e.detail ?: ""; } writeOutput(serializeJSON(response));