// 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));