/** * 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 }; 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 ID, Name, Description, DaysActive, StartTime, EndTime, SortOrder, IsActive FROM Menus WHERE BusinessID = :businessID AND IsActive = 1 ORDER BY SortOrder, Name ", { 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 BusinessID = :businessID AND MenuID = :menuID ", { businessID: businessID, menuID: qMenus.ID[i] }, { datasource: "payfrit" }); arrayAppend(menus, { "MenuID": qMenus.ID[i], "Name": qMenus.Name[i], "Description": isNull(qMenus.Description[i]) ? "" : qMenus.Description[i], "DaysActive": qMenus.DaysActive[i], "StartTime": isNull(qMenus.StartTime[i]) ? "" : timeFormat(qMenus.StartTime[i], "HH:mm"), "EndTime": isNull(qMenus.EndTime[i]) ? "" : timeFormat(qMenus.EndTime[i], "HH:mm"), "SortOrder": qMenus.SortOrder[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 ID = :menuID AND BusinessID = :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.ID, "Name": qMenu.Name, "Description": isNull(qMenu.Description) ? "" : qMenu.Description, "DaysActive": qMenu.DaysActive, "StartTime": isNull(qMenu.StartTime) ? "" : timeFormat(qMenu.StartTime, "HH:mm"), "EndTime": isNull(qMenu.EndTime) ? "" : timeFormat(qMenu.EndTime, "HH:mm"), "SortOrder": qMenu.SortOrder, "IsActive": qMenu.IsActive } }; break; case "save": // Create or update a menu menuID = structKeyExists(requestData, "MenuID") ? val(requestData.MenuID) : 0; menuName = structKeyExists(requestData, "Name") ? trim(requestData.Name) : ""; menuDescription = structKeyExists(requestData, "Description") ? trim(requestData.Description) : ""; menuDaysActive = structKeyExists(requestData, "DaysActive") ? val(requestData.DaysActive) : 127; menuStartTime = structKeyExists(requestData, "StartTime") && len(trim(requestData.StartTime)) ? trim(requestData.StartTime) : ""; menuEndTime = structKeyExists(requestData, "EndTime") && len(trim(requestData.EndTime)) ? trim(requestData.EndTime) : ""; menuSortOrder = structKeyExists(requestData, "SortOrder") ? val(requestData.SortOrder) : 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 Name = :menuName, Description = :menuDescription, DaysActive = :menuDaysActive, StartTime = :menuStartTime, EndTime = :menuEndTime, SortOrder = :menuSortOrder WHERE ID = :menuID AND BusinessID = :businessID ", { menuID: { value: menuID, cfsqltype: "cf_sql_integer" }, businessID: { value: businessID, cfsqltype: "cf_sql_integer" }, menuName: { value: menuName, cfsqltype: "cf_sql_varchar" }, menuDescription: { value: menuDescription, cfsqltype: "cf_sql_varchar" }, menuDaysActive: { value: menuDaysActive, cfsqltype: "cf_sql_integer" }, menuStartTime: { value: menuStartTime, cfsqltype: "cf_sql_time", null: !len(menuStartTime) }, menuEndTime: { value: menuEndTime, cfsqltype: "cf_sql_time", null: !len(menuEndTime) }, menuSortOrder: { value: menuSortOrder, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); response = { "OK": true, "MenuID": menuID, "ACTION": "updated" }; } else { // Create new menu queryExecute(" INSERT INTO Menus ( BusinessID, Name, Description, DaysActive, StartTime, EndTime, SortOrder, IsActive, AddedOn ) VALUES ( :businessID, :menuName, :menuDescription, :menuDaysActive, :menuStartTime, :menuEndTime, :menuSortOrder, 1, NOW() ) ", { businessID: { value: businessID, cfsqltype: "cf_sql_integer" }, menuName: { value: menuName, cfsqltype: "cf_sql_varchar" }, menuDescription: { value: menuDescription, cfsqltype: "cf_sql_varchar" }, menuDaysActive: { value: menuDaysActive, cfsqltype: "cf_sql_integer" }, menuStartTime: { value: menuStartTime, cfsqltype: "cf_sql_time", null: !len(menuStartTime) }, menuEndTime: { value: menuEndTime, cfsqltype: "cf_sql_time", null: !len(menuEndTime) }, menuSortOrder: { value: menuSortOrder, cfsqltype: "cf_sql_integer" } }, { 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/items for warning qCatCheck = queryExecute(" SELECT COUNT(*) as cnt FROM Categories WHERE MenuID = :menuID AND BusinessID = :businessID ", { menuID: menuID, businessID: businessID }, { datasource: "payfrit" }); // Unassign categories from this menu if (qCatCheck.cnt > 0) { queryExecute(" UPDATE Categories SET MenuID = 0 WHERE MenuID = :menuID AND BusinessID = :businessID ", { menuID: menuID, businessID: businessID }, { datasource: "payfrit" }); } // Soft-delete the menu queryExecute(" UPDATE Menus SET IsActive = 0 WHERE ID = :menuID AND BusinessID = :businessID ", { menuID: menuID, businessID: businessID }, { datasource: "payfrit" }); response = { "OK": true, "MenuID": menuID, "ACTION": "deleted", "CategoriesUnassigned": qCatCheck.cnt }; 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 SortOrder = :sortOrder WHERE ID = :menuID AND BusinessID = :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 ?: ""; } try{logPerf(0);}catch(any e){} writeOutput(serializeJSON(response));