payfrit-works/api/admin/addDrinkModifiers.cfm
John Mizerek e757a4140b Add drink modifiers, unified schema improvements, and portal fixes
Menu System:
- Unified schema with Categories table integration
- Virtual category headers with proper parent ID remapping
- Filter out legacy category headers when using new schema
- Add drink modifier endpoints for Fountain Soda (Size/Flavor)

Admin Tools:
- addDrinkModifiers.cfm - Add size/flavor modifiers to drinks
- copyDrinksToBigDeans.cfm - Copy drink items between businesses
- debugDrinkStructure.cfm - Debug drink item hierarchy

Portal:
- Station assignment improvements with better drag-drop
- Enhanced debug task viewer

API Fixes:
- Application.cfm updated with new admin endpoint allowlist
- setLineItem.cfm formatting cleanup
- listMine.cfm task query fixes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 20:30:58 -08:00

176 lines
5.8 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">
<cfscript>
/**
* Add size and type modifiers to Big Dean's Fountain Soda
*/
response = { "OK": false, "SizesAdded": 0, "TypesAdded": 0 };
try {
bigDeansBusinessId = 27;
// Find the Fountain Soda item we created
qFountain = queryExecute("
SELECT ItemID, ItemName FROM Items
WHERE ItemBusinessID = :bizId AND ItemName = 'Fountain Soda'
", { bizId: bigDeansBusinessId }, { datasource: "payfrit" });
if (qFountain.recordCount == 0) {
response["ERROR"] = "Fountain Soda not found in Big Dean's menu";
writeOutput(serializeJSON(response));
abort;
}
fountainId = qFountain.ItemID;
response["FountainSodaID"] = fountainId;
// Update Fountain Soda to require child selection and be collapsible
queryExecute("
UPDATE Items
SET ItemRequiresChildSelection = 1, ItemIsCollapsible = 1
WHERE ItemID = :itemId
", { itemId: fountainId }, { datasource: "payfrit" });
// Check if modifiers already exist
qExisting = queryExecute("
SELECT COUNT(*) as cnt FROM Items WHERE ItemParentItemID = :parentId
", { parentId: fountainId }, { datasource: "payfrit" });
if (qExisting.cnt > 0) {
response["OK"] = true;
response["MESSAGE"] = "Modifiers already exist";
writeOutput(serializeJSON(response));
abort;
}
// Add Size group
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
sizeGroupId = qMaxItem.nextId;
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemRequiresChildSelection,
ItemMaxNumSelectionReq, ItemAddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
'Size', 'Choose your size', 0, 1,
0, 1, 1, 1, NOW()
)
", {
itemId: sizeGroupId,
bizId: bigDeansBusinessId,
parentId: fountainId
}, { datasource: "payfrit" });
// Add Size options
sizes = [
{ name: "Small", price: 0, isDefault: 0 },
{ name: "Medium", price: 0.50, isDefault: 1 },
{ name: "Large", price: 1.00, isDefault: 0 }
];
sizesAdded = 0;
for (size in sizes) {
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemIsCheckedByDefault,
ItemAddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
:name, '', :price, 1,
:sortOrder, 0, :isDefault,
NOW()
)
", {
itemId: qMaxItem.nextId,
bizId: bigDeansBusinessId,
parentId: sizeGroupId,
name: size.name,
price: size.price,
sortOrder: sizesAdded,
isDefault: size.isDefault
}, { datasource: "payfrit" });
sizesAdded++;
}
// Add Type/Flavor group
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
typeGroupId = qMaxItem.nextId;
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemRequiresChildSelection,
ItemMaxNumSelectionReq, ItemAddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
'Flavor', 'Choose your drink', 0, 1,
1, 1, 1, 1, NOW()
)
", {
itemId: typeGroupId,
bizId: bigDeansBusinessId,
parentId: fountainId
}, { datasource: "payfrit" });
// Add Type options
types = [
{ name: "Coca-Cola", isDefault: 1 },
{ name: "Diet Coke", isDefault: 0 },
{ name: "Sprite", isDefault: 0 },
{ name: "Fanta Orange", isDefault: 0 },
{ name: "Lemonade", isDefault: 0 },
{ name: "Root Beer", isDefault: 0 },
{ name: "Dr Pepper", isDefault: 0 }
];
typesAdded = 0;
for (type in types) {
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemIsCheckedByDefault,
ItemAddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
:name, '', 0, 1,
:sortOrder, 0, :isDefault,
NOW()
)
", {
itemId: qMaxItem.nextId,
bizId: bigDeansBusinessId,
parentId: typeGroupId,
name: type.name,
sortOrder: typesAdded,
isDefault: type.isDefault
}, { datasource: "payfrit" });
typesAdded++;
}
response["OK"] = true;
response["SizesAdded"] = sizesAdded;
response["TypesAdded"] = typesAdded;
response["SizeGroupID"] = sizeGroupId;
response["TypeGroupID"] = typeGroupId;
} catch (any e) {
response["ERROR"] = "server_error";
response["MESSAGE"] = e.message;
response["DETAIL"] = e.detail;
}
writeOutput(serializeJSON(response));
</cfscript>