Only quickTasks/ and scheduledTasks/ subdirectories remain tracked since those are actively used by the portal. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
186 lines
6.8 KiB
Text
186 lines
6.8 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
|
|
<!--- Only allow from localhost --->
|
|
<cfif NOT (cgi.remote_addr EQ "127.0.0.1" OR cgi.remote_addr EQ "::1" OR findNoCase("localhost", cgi.server_name))>
|
|
<cfoutput>#serializeJSON({"OK": false, "ERROR": "admin_only"})#</cfoutput>
|
|
<cfabort>
|
|
</cfif>
|
|
|
|
<cfscript>
|
|
/**
|
|
* 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.ID, i.Name
|
|
FROM Items i
|
|
INNER JOIN Categories c ON c.ID = i.CategoryID
|
|
WHERE c.BusinessID = :businessID
|
|
AND i.ParentItemID = 0
|
|
AND i.IsActive = 1
|
|
ORDER BY i.Name
|
|
", { 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.Name,
|
|
m.ParentItemID,
|
|
m.Price,
|
|
m.IsCheckedByDefault,
|
|
m.SortOrder,
|
|
p.Name as ParentName
|
|
FROM Items m
|
|
INNER JOIN Items p ON p.ItemID = m.ParentItemID
|
|
INNER JOIN Categories c ON c.ID = p.CategoryID
|
|
WHERE c.BusinessID = :businessID
|
|
AND m.ParentItemID > 0
|
|
AND m.IsActive = 1
|
|
AND p.ParentItemID = 0
|
|
ORDER BY m.Name, 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.Name;
|
|
if (!structKeyExists(modifiersByName, modName)) {
|
|
modifiersByName[modName] = [];
|
|
}
|
|
arrayAppend(modifiersByName[modName], {
|
|
"ItemID": mod.ItemID,
|
|
"ParentItemID": mod.ParentItemID,
|
|
"ParentName": mod.ParentName,
|
|
"Price": mod.Price,
|
|
"IsDefault": mod.IsCheckedByDefault,
|
|
"SortOrder": mod.SortOrder
|
|
});
|
|
}
|
|
|
|
// 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 IsModifierTemplate = 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 lt_ItemID_TemplateItemID (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,
|
|
"Name": modName,
|
|
"WasUnder": inst.ParentName
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
// Single instance - still mark as template for consistency
|
|
singleItem = instances[1];
|
|
queryExecute("
|
|
UPDATE Items SET IsModifierTemplate = 1 WHERE ItemID = :itemID
|
|
", { itemID: singleItem.ItemID }, { datasource: "payfrit" });
|
|
|
|
// Create link
|
|
queryExecute("
|
|
INSERT INTO lt_ItemID_TemplateItemID (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));
|
|
</cfscript>
|