Portal local development: - Add BASE_PATH detection to all portal files (login, portal.js, menu-builder, station-assignment) - Allows portal to work at /biz.payfrit.com/ path locally Menu Builder fixes: - Fix duplicate template options in getForBuilder.cfm query - Filter template children by business ID with DISTINCT New APIs: - api/portal/myBusinesses.cfm - List businesses for logged-in user - api/stations/list.cfm - List KDS stations - api/menu/updateStations.cfm - Update item station assignments - api/setup/reimportBigDeans.cfm - Full Big Dean's menu import script Admin utilities: - Various debug and migration scripts for menu/template management - Beacon switching, category cleanup, modifier template setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
159 lines
4.9 KiB
Text
159 lines
4.9 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>
|
|
/**
|
|
* Fix Real Ice Cream Shake modifier structure
|
|
*
|
|
* Current (wrong):
|
|
* Real Ice Cream Shake (5808)
|
|
* ├── Chocolate (6294) - marked as template
|
|
* ├── Strawberry (6292) - marked as template
|
|
* └── Vanilla (6293) - marked as template
|
|
*
|
|
* Correct:
|
|
* Real Ice Cream Shake (5808)
|
|
* └── Choose Flavor (NEW - modifier group, REQUIRED)
|
|
* ├── Chocolate
|
|
* ├── Strawberry
|
|
* └── Vanilla
|
|
*
|
|
* Steps:
|
|
* 1. Create "Choose Flavor" modifier group under Shake (5808)
|
|
* 2. Re-parent the three flavors under the new group
|
|
* 3. Remove template flag from flavors
|
|
* 4. Mark "Choose Flavor" as the template
|
|
* 5. Create ItemTemplateLinks entry for Shake -> Choose Flavor
|
|
* 6. Set ItemRequiresChildSelection=1 on the shake item
|
|
*/
|
|
|
|
response = { "OK": false, "steps": [] };
|
|
|
|
try {
|
|
shakeItemID = 5808;
|
|
chocolateID = 6294;
|
|
strawberryID = 6292;
|
|
vanillaID = 6293;
|
|
categoryID = 46; // Fries and Shakes
|
|
|
|
// Step 1: Create "Choose Flavor" modifier group under Shake
|
|
queryExecute("
|
|
INSERT INTO Items (
|
|
ItemCategoryID,
|
|
ItemName,
|
|
ItemDescription,
|
|
ItemParentItemID,
|
|
ItemPrice,
|
|
ItemIsActive,
|
|
ItemIsCheckedByDefault,
|
|
ItemRequiresChildSelection,
|
|
ItemMaxNumSelectionReq,
|
|
ItemIsCollapsible,
|
|
ItemSortOrder,
|
|
ItemIsModifierTemplate,
|
|
ItemAddedOn
|
|
) VALUES (
|
|
:categoryID,
|
|
'Choose Flavor',
|
|
'',
|
|
:shakeItemID,
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
1,
|
|
1,
|
|
0,
|
|
1,
|
|
NOW()
|
|
)
|
|
", {
|
|
categoryID: categoryID,
|
|
shakeItemID: shakeItemID
|
|
}, { datasource: "payfrit" });
|
|
|
|
// Get the new Choose Flavor ID
|
|
qNewGroup = queryExecute("
|
|
SELECT ItemID FROM Items
|
|
WHERE ItemName = 'Choose Flavor'
|
|
AND ItemParentItemID = :shakeItemID
|
|
ORDER BY ItemID DESC
|
|
LIMIT 1
|
|
", { shakeItemID: shakeItemID }, { datasource: "payfrit" });
|
|
|
|
chooseFlavorID = qNewGroup.ItemID;
|
|
arrayAppend(response.steps, "Created 'Choose Flavor' group with ID: " & chooseFlavorID);
|
|
|
|
// Step 2: Re-parent the three flavors under Choose Flavor
|
|
queryExecute("
|
|
UPDATE Items
|
|
SET ItemParentItemID = :chooseFlavorID,
|
|
ItemIsModifierTemplate = 0,
|
|
ItemIsCheckedByDefault = 0
|
|
WHERE ItemID IN (:chocolateID, :strawberryID, :vanillaID)
|
|
", {
|
|
chooseFlavorID: chooseFlavorID,
|
|
chocolateID: chocolateID,
|
|
strawberryID: strawberryID,
|
|
vanillaID: vanillaID
|
|
}, { datasource: "payfrit" });
|
|
|
|
arrayAppend(response.steps, "Re-parented Chocolate, Strawberry, Vanilla under Choose Flavor");
|
|
|
|
// Step 3: Set Vanilla as default (common choice)
|
|
queryExecute("
|
|
UPDATE Items SET ItemIsCheckedByDefault = 1 WHERE ItemID = :vanillaID
|
|
", { vanillaID: vanillaID }, { datasource: "payfrit" });
|
|
|
|
arrayAppend(response.steps, "Set Vanilla as default flavor");
|
|
|
|
// Step 4: Remove old template links for the flavors
|
|
queryExecute("
|
|
DELETE FROM ItemTemplateLinks
|
|
WHERE TemplateItemID IN (:chocolateID, :strawberryID, :vanillaID)
|
|
", {
|
|
chocolateID: chocolateID,
|
|
strawberryID: strawberryID,
|
|
vanillaID: vanillaID
|
|
}, { datasource: "payfrit" });
|
|
|
|
arrayAppend(response.steps, "Removed old template links for flavor items");
|
|
|
|
// Step 5: Create ItemTemplateLinks for Shake -> Choose Flavor
|
|
queryExecute("
|
|
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder)
|
|
VALUES (:shakeItemID, :chooseFlavorID, 0)
|
|
ON DUPLICATE KEY UPDATE SortOrder = 0
|
|
", {
|
|
shakeItemID: shakeItemID,
|
|
chooseFlavorID: chooseFlavorID
|
|
}, { datasource: "payfrit" });
|
|
|
|
arrayAppend(response.steps, "Created template link: Shake -> Choose Flavor");
|
|
|
|
// Step 6: Set ItemRequiresChildSelection on shake
|
|
queryExecute("
|
|
UPDATE Items
|
|
SET ItemRequiresChildSelection = 1
|
|
WHERE ItemID = :shakeItemID
|
|
", { shakeItemID: shakeItemID }, { datasource: "payfrit" });
|
|
|
|
arrayAppend(response.steps, "Set ItemRequiresChildSelection=1 on Shake item");
|
|
|
|
response["OK"] = true;
|
|
response["chooseFlavorID"] = chooseFlavorID;
|
|
|
|
} catch (any e) {
|
|
response["ERROR"] = e.message;
|
|
response["DETAIL"] = e.detail;
|
|
}
|
|
|
|
writeOutput(serializeJSON(response));
|
|
</cfscript>
|