#serializeJSON({"OK": false, "ERROR": "admin_only"})# /** * Migrate Modifier Templates for In and Out Burger (BusinessID=1) * * This script: * 1. Identifies unique modifier trees (children of parent items) * 2. Keeps ONE instance of each unique modifier name as the template * 3. Links all parent items to these templates * 4. Reports which duplicate items can be deleted */ response = { "OK": false, "steps": [], "templates": [], "links": [], "orphans": [] }; try { businessID = 17; // In and Out Burger // Step 1: Get all parent items (burgers, combos, etc.) qParentItems = queryExecute(" SELECT i.ItemID, i.ItemName FROM Items i INNER JOIN Categories c ON c.CategoryID = i.ItemCategoryID WHERE c.CategoryBusinessID = :businessID AND i.ItemParentItemID = 0 AND i.ItemIsActive = 1 ORDER BY i.ItemName ", { businessID: businessID }, { datasource: "payfrit" }); arrayAppend(response.steps, "Found " & qParentItems.recordCount & " parent items"); // Step 2: Get all direct children (level 1 modifiers) of parent items qModifiers = queryExecute(" SELECT m.ItemID, m.ItemName, m.ItemParentItemID, m.ItemPrice, m.ItemIsCheckedByDefault, m.ItemSortOrder, p.ItemName as ParentName FROM Items m INNER JOIN Items p ON p.ItemID = m.ItemParentItemID INNER JOIN Categories c ON c.CategoryID = p.ItemCategoryID WHERE c.CategoryBusinessID = :businessID AND m.ItemParentItemID > 0 AND m.ItemIsActive = 1 AND p.ItemParentItemID = 0 ORDER BY m.ItemName, m.ItemID ", { businessID: businessID }, { datasource: "payfrit" }); arrayAppend(response.steps, "Found " & qModifiers.recordCount & " level-1 modifiers"); // Step 3: Group modifiers by name to find duplicates modifiersByName = {}; for (mod in qModifiers) { modName = mod.ItemName; if (!structKeyExists(modifiersByName, modName)) { modifiersByName[modName] = []; } arrayAppend(modifiersByName[modName], { "ItemID": mod.ItemID, "ParentItemID": mod.ItemParentItemID, "ParentName": mod.ParentName, "Price": mod.ItemPrice, "IsDefault": mod.ItemIsCheckedByDefault, "SortOrder": mod.ItemSortOrder }); } // Step 4: For each unique modifier name, pick ONE as the template // Keep the first occurrence (usually oldest/original) templateMap = {}; // modifierName -> templateItemID orphanItems = []; // Items to delete later for (modName in modifiersByName) { instances = modifiersByName[modName]; if (arrayLen(instances) > 1) { // Multiple instances - first one becomes template templateItem = instances[1]; templateItemID = templateItem.ItemID; // Mark as template queryExecute(" UPDATE Items SET ItemIsModifierTemplate = 1 WHERE ItemID = :itemID ", { itemID: templateItemID }, { datasource: "payfrit" }); templateMap[modName] = templateItemID; arrayAppend(response.templates, { "name": modName, "templateItemID": templateItemID, "originalParent": templateItem.ParentName, "duplicateCount": arrayLen(instances) - 1 }); // Create links for ALL parent items that had this modifier for (i = 1; i <= arrayLen(instances); i++) { inst = instances[i]; parentItemID = inst.ParentItemID; // Insert link (ignore duplicates) try { queryExecute(" INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder) VALUES (:itemID, :templateID, :sortOrder) ON DUPLICATE KEY UPDATE SortOrder = :sortOrder ", { itemID: parentItemID, templateID: templateItemID, sortOrder: inst.SortOrder }, { datasource: "payfrit" }); arrayAppend(response.links, { "parentItemID": parentItemID, "parentName": inst.ParentName, "templateItemID": templateItemID, "templateName": modName }); } catch (any linkErr) { // Link already exists, ignore } // Mark duplicates (not the template) as orphans if (i > 1) { arrayAppend(orphanItems, { "ItemID": inst.ItemID, "ItemName": modName, "WasUnder": inst.ParentName }); } } } else { // Single instance - still mark as template for consistency singleItem = instances[1]; queryExecute(" UPDATE Items SET ItemIsModifierTemplate = 1 WHERE ItemID = :itemID ", { itemID: singleItem.ItemID }, { datasource: "payfrit" }); // Create link queryExecute(" INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder) VALUES (:itemID, :templateID, :sortOrder) ON DUPLICATE KEY UPDATE SortOrder = :sortOrder ", { itemID: singleItem.ParentItemID, templateID: singleItem.ItemID, sortOrder: singleItem.SortOrder }, { datasource: "payfrit" }); arrayAppend(response.templates, { "name": modName, "templateItemID": singleItem.ItemID, "originalParent": singleItem.ParentName, "duplicateCount": 0 }); } } response["orphans"] = orphanItems; response["orphanCount"] = arrayLen(orphanItems); response["templateCount"] = arrayLen(response.templates); response["linkCount"] = arrayLen(response.links); arrayAppend(response.steps, "Created " & arrayLen(response.templates) & " templates"); arrayAppend(response.steps, "Created " & arrayLen(response.links) & " links"); arrayAppend(response.steps, "Identified " & arrayLen(orphanItems) & " orphan items for deletion"); response["OK"] = true; } catch (any e) { response["ERROR"] = e.message; response["DETAIL"] = e.detail; } writeOutput(serializeJSON(response));