/** * Save Wizard Data * * Takes the extracted menu data from the setup wizard and saves it to the database. * This transforms the wizard format into the format expected by the import system. * * POST JSON: * { * "businessId": "existing-business-id", * "data": { * "business": { "name": "...", "address": "...", "phone": "...", "hours": "..." }, * "categories": [ { "name": "...", "itemCount": 0 } ], * "modifiers": [ { "name": "...", "required": true, "options": [...] } ], * "items": [ { "name": "...", "price": 0, "category": "...", "modifiers": [...] } ] * } * } */ response = { "OK": false, "steps": [], "errors": [] }; try { requestBody = toString(getHttpRequestData().content); if (!len(requestBody)) { throw(message="No request body provided"); } data = deserializeJSON(requestBody); businessId = structKeyExists(data, "businessId") ? val(data.businessId) : 0; wizardData = structKeyExists(data, "data") ? data.data : {}; if (businessId == 0) { throw(message="businessId is required"); } // Verify business exists qBiz = queryExecute(" SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID = :id ", { id: businessId }, { datasource: "payfrit" }); if (qBiz.recordCount == 0) { throw(message="Business not found: " & businessId); } response.steps.append("Found business: " & qBiz.BusinessName); // Update business info if provided biz = structKeyExists(wizardData, "business") ? wizardData.business : {}; if (structKeyExists(biz, "name") && len(biz.name)) { // Optionally update business name and other info // For now we'll skip updating existing business - just add menu response.steps.append("Business info available (not updating existing)"); } // Build modifier template map // The wizard format has modifiers as simple objects, we need to create IDs modTemplates = structKeyExists(wizardData, "modifiers") ? wizardData.modifiers : []; templateMap = {}; // Maps modifier name to database ItemID response.steps.append("Processing " & arrayLen(modTemplates) & " modifier templates..."); for (i = 1; i <= arrayLen(modTemplates); i++) { tmpl = modTemplates[i]; tmplName = tmpl.name; required = structKeyExists(tmpl, "required") && tmpl.required == true; options = structKeyExists(tmpl, "options") ? tmpl.options : []; // Check if template already exists for this business qTmpl = queryExecute(" SELECT i.ItemID FROM Items i WHERE i.ItemBusinessID = :bizID AND i.ItemName = :name AND i.ItemParentItemID = 0 AND i.ItemIsCollapsible = 1 ", { bizID: businessId, name: tmplName }, { datasource: "payfrit" }); if (qTmpl.recordCount > 0) { templateItemID = qTmpl.ItemID; response.steps.append("Template exists: " & tmplName & " (ID: " & templateItemID & ")"); } else { // Create template as Item with ItemIsCollapsible=1 to mark as template queryExecute(" INSERT INTO Items ( ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, ItemIsActive, ItemRequiresChildSelection, ItemMaxNumSelectionReq, ItemSortOrder, ItemIsCollapsible ) VALUES ( :bizID, :name, 0, 0, 1, :required, 1, 0, 1 ) ", { bizID: businessId, name: tmplName, required: required ? 1 : 0 }, { datasource: "payfrit" }); qNewTmpl = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); templateItemID = qNewTmpl.id; response.steps.append("Created template: " & tmplName & " (ID: " & templateItemID & ")"); } templateMap[tmplName] = templateItemID; // Create/update template options optionOrder = 1; for (opt in options) { optName = opt.name; optPrice = structKeyExists(opt, "price") ? val(opt.price) : 0; qOpt = queryExecute(" SELECT ItemID FROM Items WHERE ItemBusinessID = :bizID AND ItemName = :name AND ItemParentItemID = :parentID ", { bizID: businessId, name: optName, parentID: templateItemID }, { datasource: "payfrit" }); if (qOpt.recordCount == 0) { queryExecute(" INSERT INTO Items ( ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, ItemIsActive, ItemSortOrder ) VALUES ( :bizID, :name, :parentID, :price, 1, :sortOrder ) ", { bizID: businessId, name: optName, parentID: templateItemID, price: optPrice, sortOrder: optionOrder }, { datasource: "payfrit" }); } optionOrder++; } } // Build category map categories = structKeyExists(wizardData, "categories") ? wizardData.categories : []; categoryMap = {}; // Maps category name to ItemID response.steps.append("Processing " & arrayLen(categories) & " categories..."); catOrder = 1; for (cat in categories) { catName = cat.name; // Check if category exists (Item at ParentID=0, not a template) qCat = queryExecute(" SELECT ItemID FROM Items WHERE ItemBusinessID = :bizID AND ItemName = :name AND ItemParentItemID = 0 AND (ItemIsCollapsible IS NULL OR ItemIsCollapsible = 0) ", { bizID: businessId, name: catName }, { datasource: "payfrit" }); if (qCat.recordCount > 0) { categoryItemID = qCat.ItemID; response.steps.append("Category exists: " & catName); } else { queryExecute(" INSERT INTO Items ( ItemBusinessID, ItemName, ItemParentItemID, ItemPrice, ItemIsActive, ItemSortOrder, ItemIsCollapsible ) VALUES ( :bizID, :name, 0, 0, 1, :sortOrder, 0 ) ", { bizID: businessId, name: catName, sortOrder: catOrder }, { datasource: "payfrit" }); qNewCat = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); categoryItemID = qNewCat.id; response.steps.append("Created category: " & catName & " (ID: " & categoryItemID & ")"); } categoryMap[catName] = categoryItemID; catOrder++; } // Create menu items items = structKeyExists(wizardData, "items") ? wizardData.items : []; response.steps.append("Processing " & arrayLen(items) & " menu items..."); totalItems = 0; totalLinks = 0; // Track item order within each category categoryItemOrder = {}; for (item in items) { itemName = item.name; itemDesc = structKeyExists(item, "description") ? item.description : ""; itemPrice = structKeyExists(item, "price") ? val(item.price) : 0; itemCategory = structKeyExists(item, "category") ? item.category : ""; itemModifiers = structKeyExists(item, "modifiers") ? item.modifiers : []; // Get category ID if (!len(itemCategory) || !structKeyExists(categoryMap, itemCategory)) { response.steps.append("Warning: Item '" & itemName & "' has unknown category '" & itemCategory & "' - skipping"); continue; } categoryItemID = categoryMap[itemCategory]; // Track sort order within category if (!structKeyExists(categoryItemOrder, itemCategory)) { categoryItemOrder[itemCategory] = 1; } itemOrder = categoryItemOrder[itemCategory]; categoryItemOrder[itemCategory]++; // Check if item exists qItem = queryExecute(" SELECT ItemID FROM Items WHERE ItemBusinessID = :bizID AND ItemName = :name AND ItemParentItemID = :parentID ", { bizID: businessId, name: itemName, parentID: categoryItemID }, { datasource: "payfrit" }); if (qItem.recordCount > 0) { menuItemID = qItem.ItemID; // Update existing item queryExecute(" UPDATE Items SET ItemDescription = :desc, ItemPrice = :price, ItemSortOrder = :sortOrder WHERE ItemID = :id ", { desc: itemDesc, price: itemPrice, sortOrder: itemOrder, id: menuItemID }, { datasource: "payfrit" }); } else { queryExecute(" INSERT INTO Items ( ItemBusinessID, ItemName, ItemDescription, ItemParentItemID, ItemPrice, ItemIsActive, ItemSortOrder ) VALUES ( :bizID, :name, :desc, :parentID, :price, 1, :sortOrder ) ", { bizID: businessId, name: itemName, desc: itemDesc, parentID: categoryItemID, price: itemPrice, sortOrder: itemOrder }, { datasource: "payfrit" }); qNewItem = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" }); menuItemID = qNewItem.id; } totalItems++; // Link modifier templates to this item modOrder = 1; for (modName in itemModifiers) { if (structKeyExists(templateMap, modName)) { templateItemID = templateMap[modName]; // Check if link exists qLink = queryExecute(" SELECT 1 FROM ItemTemplateLinks WHERE ItemID = :itemID AND TemplateItemID = :templateID ", { itemID: menuItemID, templateID: templateItemID }, { datasource: "payfrit" }); if (qLink.recordCount == 0) { queryExecute(" INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder) VALUES (:itemID, :templateID, :sortOrder) ", { itemID: menuItemID, templateID: templateItemID, sortOrder: modOrder }, { datasource: "payfrit" }); totalLinks++; } modOrder++; } } } response.steps.append("Created/updated " & totalItems & " items with " & totalLinks & " modifier links"); response.OK = true; response.summary = { "businessId": businessId, "categoriesProcessed": arrayLen(categories), "templatesProcessed": arrayLen(modTemplates), "itemsProcessed": totalItems, "linksCreated": totalLinks }; } catch (any e) { response.errors.append(e.message); if (len(e.detail)) { response.errors.append(e.detail); } } writeOutput(serializeJSON(response));