Categories Migration: - Add ItemCategoryID column to Items table (api/admin/addItemCategoryColumn.cfm) - Migration script to populate Categories from unified schema (api/admin/migrateToCategories.cfm) - Updated items.cfm and getForBuilder.cfm to use Categories table with fallback KDS Station Selection: - KDS now prompts for station selection on load (Kitchen, Bar, or All Stations) - Station filter persists in localStorage - Updated listForKDS.cfm to filter orders by station - Simplified KDS UI with station badge in header Portal Improvements: - Fixed drag-and-drop in station assignment (proper event propagation) - Fixed Back button links to use BASE_PATH for local development - Added console logging for debugging station assignment - Order detail API now calculates Subtotal, Tax, Tip, Total properly Admin Tools: - setupBigDeansStations.cfm - Create Kitchen and Bar stations for Big Dean's 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
134 lines
4.9 KiB
Text
134 lines
4.9 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>
|
|
/**
|
|
* 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));
|
|
</cfscript>
|