/** * Migrate Unified Schema to Categories * * This script: * 1. Finds all "category" Items (ParentID=0, not templates, not collapsible) * 2. Creates corresponding entries in Categories table * 3. Updates child Items with the new CategoryID * * GET: ?BusinessID=27 (or all if not specified) */ response = { "OK": false, "BusinessesProcessed": [], "Errors": [] }; try { // Get BusinessID from URL if specified businessFilter = ""; if (structKeyExists(url, "BusinessID") && val(url.BusinessID) > 0) { businessFilter = val(url.BusinessID); } // Find all businesses with items in unified schema if (len(businessFilter)) { qBusinesses = queryExecute(" SELECT DISTINCT ItemBusinessID as BusinessID FROM Items WHERE ItemBusinessID = :bid AND ItemBusinessID > 0 ", { bid: businessFilter }, { datasource: "payfrit" }); } else { qBusinesses = queryExecute(" SELECT DISTINCT ItemBusinessID as BusinessID FROM Items WHERE ItemBusinessID > 0 ", {}, { datasource: "payfrit" }); } for (biz in qBusinesses) { bizId = biz.BusinessID; bizResult = { "BusinessID": bizId, "CategoriesCreated": 0, "ItemsUpdated": 0 }; try { // Find category-like Items (parent=0, not collapsible, has children, not in template links) qCategoryItems = queryExecute(" SELECT DISTINCT p.ItemID, p.ItemName, p.ItemSortOrder FROM Items p INNER JOIN Items c ON c.ItemParentItemID = p.ItemID WHERE p.ItemBusinessID = :bizId AND p.ItemParentItemID = 0 AND (p.ItemIsCollapsible = 0 OR p.ItemIsCollapsible IS NULL) AND NOT EXISTS ( SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = p.ItemID ) ORDER BY p.ItemSortOrder, p.ItemName ", { bizId: bizId }, { datasource: "payfrit" }); sortOrder = 0; for (catItem in qCategoryItems) { // Check if category already exists for this business with same name qExisting = queryExecute(" SELECT CategoryID FROM Categories WHERE CategoryBusinessID = :bizId AND CategoryName = :name ", { bizId: bizId, name: left(catItem.ItemName, 30) }, { datasource: "payfrit" }); if (qExisting.recordCount == 0) { // Get next CategoryID qMaxId = queryExecute(" SELECT COALESCE(MAX(CategoryID), 0) + 1 as nextId FROM Categories ", {}, { datasource: "payfrit" }); newCatId = qMaxId.nextId; // Create new category with explicit ID queryExecute(" INSERT INTO Categories (CategoryID, CategoryBusinessID, CategoryParentCategoryID, CategoryName, CategorySortOrder, CategoryAddedOn) VALUES (:catId, :bizId, 0, :name, :sortOrder, NOW()) ", { catId: newCatId, bizId: bizId, name: left(catItem.ItemName, 30), sortOrder: sortOrder }, { datasource: "payfrit" }); bizResult.CategoriesCreated++; } else { newCatId = qExisting.CategoryID; } // Update all children of this category Item to have the new CategoryID queryExecute(" UPDATE Items SET ItemCategoryID = :catId WHERE ItemParentItemID = :parentId AND ItemBusinessID = :bizId ", { catId: newCatId, parentId: catItem.ItemID, bizId: bizId }, { datasource: "payfrit" }); qUpdated = queryExecute("SELECT ROW_COUNT() as cnt", {}, { datasource: "payfrit" }); bizResult.ItemsUpdated += qUpdated.cnt; sortOrder++; } arrayAppend(response.BusinessesProcessed, bizResult); } catch (any bizErr) { arrayAppend(response.Errors, { "BusinessID": bizId, "Error": bizErr.message }); } } response["OK"] = true; response["TotalBusinesses"] = qBusinesses.recordCount; } catch (any e) { response["ERROR"] = "server_error"; response["MESSAGE"] = e.message; response["DETAIL"] = e.detail; } writeOutput(serializeJSON(response));