The inverted group header item isn't always an order line item itself, so RemovedDefaults was never computed. Now detects inverted groups via children's ParentIsInvertedGroup flag and attaches RemovedDefaults to the first child as a proxy. KDS JS handles both patterns. Also skips showing default modifiers from inverted groups since those are represented by "NO removed-item" instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
236 lines
No EOL
8.2 KiB
Text
236 lines
No EOL
8.2 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
|
|
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
|
<cfargument name="payload" type="struct" required="true">
|
|
<cfcontent type="application/json; charset=utf-8">
|
|
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
|
<cfabort>
|
|
</cffunction>
|
|
|
|
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
|
<cfset var raw = getHttpRequestData().content>
|
|
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
|
<cfreturn {}>
|
|
</cfif>
|
|
<cftry>
|
|
<cfset var data = deserializeJSON(raw)>
|
|
<cfif isStruct(data)>
|
|
<cfreturn data>
|
|
<cfelse>
|
|
<cfreturn {}>
|
|
</cfif>
|
|
<cfcatch>
|
|
<cfreturn {}>
|
|
</cfcatch>
|
|
</cftry>
|
|
</cffunction>
|
|
|
|
<cfset data = readJsonBody()>
|
|
<cfset BusinessID = val( structKeyExists(data,"BusinessID") ? data.BusinessID : 0 )>
|
|
<cfset ServicePointID = val( structKeyExists(data,"ServicePointID") ? data.ServicePointID : 0 )>
|
|
<cfset StationID = val( structKeyExists(data,"StationID") ? data.StationID : 0 )>
|
|
|
|
<cfif BusinessID LTE 0>
|
|
<cfset apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "BusinessID is required.", "DETAIL": "" })>
|
|
</cfif>
|
|
|
|
<cftry>
|
|
<!--- Build WHERE clause for filtering --->
|
|
<cfset whereClauses = ["o.BusinessID = ?"]>
|
|
<cfset params = [ { value = BusinessID, cfsqltype = "cf_sql_integer" } ]>
|
|
|
|
<!--- Filter by service point if provided --->
|
|
<cfif ServicePointID GT 0>
|
|
<cfset arrayAppend(whereClauses, "o.ServicePointID = ?")>
|
|
<cfset arrayAppend(params, { value = ServicePointID, cfsqltype = "cf_sql_integer" })>
|
|
</cfif>
|
|
|
|
<!--- Only show submitted orders (StatusID >= 1) --->
|
|
<cfset arrayAppend(whereClauses, "o.StatusID >= 1")>
|
|
|
|
<!--- Don't show completed orders (assuming StatusID 4 is completed) --->
|
|
<cfset arrayAppend(whereClauses, "o.StatusID < 4")>
|
|
|
|
<cfset whereSQL = arrayToList(whereClauses, " AND ")>
|
|
|
|
<!--- If filtering by station, only get orders that have items for that station --->
|
|
<cfif StationID GT 0>
|
|
<cfset stationParams = duplicate(params)>
|
|
<cfset arrayAppend(stationParams, { value = StationID, cfsqltype = "cf_sql_integer" })>
|
|
<cfset qOrders = queryTimed("
|
|
SELECT DISTINCT
|
|
o.ID,
|
|
o.UUID,
|
|
o.UserID,
|
|
o.BusinessID,
|
|
o.OrderTypeID,
|
|
o.StatusID,
|
|
o.ServicePointID,
|
|
o.Remarks,
|
|
DATE_FORMAT(o.SubmittedOn, '%Y-%m-%dT%H:%i:%sZ') AS SubmittedOn,
|
|
DATE_FORMAT(o.LastEditedOn, '%Y-%m-%dT%H:%i:%sZ') AS LastEditedOn,
|
|
sp.Name AS Name,
|
|
u.FirstName,
|
|
u.LastName
|
|
FROM Orders o
|
|
LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID
|
|
LEFT JOIN Users u ON u.ID = o.UserID
|
|
INNER JOIN OrderLineItems oli ON oli.OrderID = o.ID
|
|
INNER JOIN Items i ON i.ID = oli.ItemID
|
|
WHERE #whereSQL#
|
|
AND (i.StationID = ? OR i.StationID IS NULL)
|
|
AND oli.IsDeleted = b'0'
|
|
ORDER BY SubmittedOn ASC, o.ID ASC
|
|
", stationParams, { datasource = "payfrit" })>
|
|
<cfelse>
|
|
<cfset qOrders = queryTimed("
|
|
SELECT
|
|
o.ID,
|
|
o.UUID,
|
|
o.UserID,
|
|
o.BusinessID,
|
|
o.OrderTypeID,
|
|
o.StatusID,
|
|
o.ServicePointID,
|
|
o.Remarks,
|
|
DATE_FORMAT(o.SubmittedOn, '%Y-%m-%dT%H:%i:%sZ') AS SubmittedOn,
|
|
DATE_FORMAT(o.LastEditedOn, '%Y-%m-%dT%H:%i:%sZ') AS LastEditedOn,
|
|
sp.Name AS Name,
|
|
u.FirstName,
|
|
u.LastName
|
|
FROM Orders o
|
|
LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID
|
|
LEFT JOIN Users u ON u.ID = o.UserID
|
|
WHERE #whereSQL#
|
|
ORDER BY o.SubmittedOn ASC, o.ID ASC
|
|
", params, { datasource = "payfrit" })>
|
|
</cfif>
|
|
|
|
<cfset orders = []>
|
|
|
|
<cfloop query="qOrders">
|
|
<!--- Get all line items for this order (frontend handles station filtering) --->
|
|
<cfset qLineItems = queryTimed("
|
|
SELECT
|
|
oli.ID,
|
|
oli.ParentOrderLineItemID,
|
|
oli.ItemID,
|
|
oli.Price,
|
|
oli.Quantity,
|
|
oli.Remark,
|
|
oli.IsDeleted,
|
|
oli.StatusID,
|
|
i.Name,
|
|
i.ParentItemID,
|
|
i.IsCheckedByDefault,
|
|
i.IsInvertedGroup,
|
|
i.StationID,
|
|
parent.Name AS ItemParentName,
|
|
COALESCE(parent.IsInvertedGroup, 0) AS ParentIsInvertedGroup
|
|
FROM OrderLineItems oli
|
|
INNER JOIN Items i ON i.ID = oli.ItemID
|
|
LEFT JOIN Items parent ON parent.ID = i.ParentItemID
|
|
WHERE oli.OrderID = ?
|
|
AND oli.IsDeleted = b'0'
|
|
ORDER BY oli.ID
|
|
", [ { value = qOrders.ID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
|
|
|
|
<cfset lineItems = []>
|
|
<cfloop query="qLineItems">
|
|
<cfset arrayAppend(lineItems, {
|
|
"OrderLineItemID": qLineItems.ID,
|
|
"ParentOrderLineItemID": qLineItems.ParentOrderLineItemID,
|
|
"ItemID": qLineItems.ItemID,
|
|
"Price": qLineItems.Price,
|
|
"Quantity": qLineItems.Quantity,
|
|
"Remark": qLineItems.Remark,
|
|
"Name": qLineItems.Name,
|
|
"ParentItemID": qLineItems.ParentItemID,
|
|
"ItemParentName": qLineItems.ItemParentName,
|
|
"IsCheckedByDefault": qLineItems.IsCheckedByDefault,
|
|
"IsInvertedGroup": qLineItems.IsInvertedGroup,
|
|
"ParentIsInvertedGroup": qLineItems.ParentIsInvertedGroup,
|
|
"StationID": qLineItems.StationID,
|
|
"StatusID": val(qLineItems.StatusID)
|
|
})>
|
|
</cfloop>
|
|
|
|
<!--- For inverted modifier groups, compute removed defaults --->
|
|
<!--- The inverted group header may not be an order line item itself,
|
|
so we detect inverted groups by finding children whose parent item is inverted --->
|
|
<cfset invertedGroupsSeen = {}>
|
|
<cfloop array="#lineItems#" index="li">
|
|
<cfif li.ParentIsInvertedGroup AND NOT structKeyExists(invertedGroupsSeen, li.ParentItemID)>
|
|
<!--- First child of this inverted group — compute removed defaults once --->
|
|
<cfset invertedGroupsSeen[li.ParentItemID] = true>
|
|
<cfset qRemovedDefaults = queryTimed("
|
|
SELECT i.Name
|
|
FROM Items i
|
|
WHERE i.ParentItemID = ?
|
|
AND i.IsActive = 1
|
|
AND i.IsCheckedByDefault = b'1'
|
|
AND i.ID NOT IN (
|
|
SELECT oli2.ItemID FROM OrderLineItems oli2
|
|
WHERE oli2.OrderID = ? AND oli2.ParentOrderLineItemID = ? AND oli2.IsDeleted = b'0'
|
|
)
|
|
ORDER BY i.SortOrder
|
|
", [
|
|
{ value = li.ParentItemID, cfsqltype = "cf_sql_integer" },
|
|
{ value = qOrders.ID, cfsqltype = "cf_sql_integer" },
|
|
{ value = li.ParentOrderLineItemID, cfsqltype = "cf_sql_integer" }
|
|
], { datasource = "payfrit" })>
|
|
<cfset removedNames = []>
|
|
<cfloop query="qRemovedDefaults">
|
|
<cfset arrayAppend(removedNames, qRemovedDefaults.Name)>
|
|
</cfloop>
|
|
<!--- Attach RemovedDefaults to the first child so the JS can find it --->
|
|
<cfset li["RemovedDefaults"] = removedNames>
|
|
<cfset li["IsInvertedGroupProxy"] = true>
|
|
</cfif>
|
|
</cfloop>
|
|
|
|
<!--- Determine order type name --->
|
|
<cfset orderTypeName = "">
|
|
<cfswitch expression="#qOrders.OrderTypeID#">
|
|
<cfcase value="1"><cfset orderTypeName = "Dine-In"></cfcase>
|
|
<cfcase value="2"><cfset orderTypeName = "Takeaway"></cfcase>
|
|
<cfcase value="3"><cfset orderTypeName = "Delivery"></cfcase>
|
|
<cfdefaultcase><cfset orderTypeName = ""></cfdefaultcase>
|
|
</cfswitch>
|
|
|
|
<cfset arrayAppend(orders, {
|
|
"OrderID": qOrders.ID,
|
|
"UUID": qOrders.UUID,
|
|
"UserID": qOrders.UserID,
|
|
"BusinessID": qOrders.BusinessID,
|
|
"OrderTypeID": qOrders.OrderTypeID,
|
|
"OrderTypeName": orderTypeName,
|
|
"StatusID": qOrders.StatusID,
|
|
"ServicePointID": qOrders.ServicePointID,
|
|
"Remarks": qOrders.Remarks,
|
|
"SubmittedOn": qOrders.SubmittedOn,
|
|
"LastEditedOn": qOrders.LastEditedOn,
|
|
"Name": qOrders.Name,
|
|
"FirstName": qOrders.FirstName,
|
|
"LastName": qOrders.LastName,
|
|
"LineItems": lineItems
|
|
})>
|
|
</cfloop>
|
|
|
|
<cfset apiAbort({
|
|
"OK": true,
|
|
"ERROR": "",
|
|
"ORDERS": orders,
|
|
"STATION_FILTER": StationID
|
|
})>
|
|
|
|
<cfcatch>
|
|
<cfset apiAbort({
|
|
"OK": false,
|
|
"ERROR": "server_error",
|
|
"MESSAGE": "DB error loading orders for KDS",
|
|
"DETAIL": cfcatch.message
|
|
})>
|
|
</cfcatch>
|
|
</cftry> |