This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/orders/getDetail.cfm
John Mizerek 8acf2f3249 Complete DB column normalization: strip redundant table-name prefixes from all SQL queries
Updated 70 files to match the payfrit_dev schema where columns like
BusinessName→Name, UserFirstName→FirstName, AddressCity→City, etc.
PKs renamed to ID, FKs keep referenced table name (e.g. BusinessID).
SQL aliases preserve original JSON response keys for API compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 20:03:40 -08:00

249 lines
7.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>
/**
* Get Order Detail
* Returns full order info including line items, customer details, and staff who worked on the order
*
* GET: ?OrderID=123
* POST: { OrderID: 123 }
*/
response = { "OK": false };
try {
// Get OrderID from request
orderID = 0;
// Check URL params
if (structKeyExists(url, "OrderID")) {
orderID = val(url.OrderID);
}
// Check POST body
if (orderID == 0) {
requestBody = toString(getHttpRequestData().content);
if (len(requestBody)) {
requestData = deserializeJSON(requestBody);
if (structKeyExists(requestData, "OrderID")) {
orderID = val(requestData.OrderID);
}
}
}
if (orderID == 0) {
response["ERROR"] = "missing_order_id";
response["MESSAGE"] = "OrderID is required";
writeOutput(serializeJSON(response));
abort;
}
// Get order details
qOrder = queryExecute("
SELECT
o.ID,
o.BusinessID,
o.UserID,
o.ServicePointID,
o.StatusID,
o.OrderTypeID,
o.Remarks,
o.AddedOn,
o.LastEditedOn,
o.SubmittedOn,
o.TipAmount,
u.FirstName,
u.LastName,
u.ContactNumber,
u.EmailAddress,
sp.Name AS Name,
sp.TypeID AS TypeID,
b.Name AS BizName,
b.TaxRate
FROM Orders o
LEFT JOIN Users u ON u.ID = o.UserID
LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID
LEFT JOIN Businesses b ON b.ID = o.BusinessID
WHERE o.ID = :orderID
", { orderID: orderID });
if (qOrder.recordCount == 0) {
response["ERROR"] = "order_not_found";
response["MESSAGE"] = "Order not found";
writeOutput(serializeJSON(response));
abort;
}
// Get line items (excluding deleted items)
qItems = queryExecute("
SELECT
oli.ID,
oli.ItemID,
oli.ParentOrderLineItemID,
oli.Quantity,
oli.Price,
oli.Remark,
i.Name,
i.Price,
i.IsCheckedByDefault
FROM OrderLineItems oli
INNER JOIN Items i ON i.ID = oli.ItemID
WHERE oli.OrderID = :orderID
AND oli.IsDeleted = 0
ORDER BY oli.ID
", { orderID: orderID });
// Build line items array with parent-child structure
lineItems = [];
itemsById = {};
// First pass: create all items (use bracket notation to preserve key casing)
for (row in qItems) {
item = structNew("ordered");
item["LineItemID"] = val(row.ID);
item["ItemID"] = val(row.ID);
item["ParentLineItemID"] = val(row.ParentOrderLineItemID);
item["Name"] = row.Name ?: "";
item["Quantity"] = val(row.Quantity);
item["UnitPrice"] = val(row.Price);
item["Remarks"] = row.Remark ?: "";
item["IsDefault"] = (val(row.IsCheckedByDefault) == 1);
item["Modifiers"] = [];
itemsById[row.ID] = item;
}
// Second pass: build hierarchy
for (row in qItems) {
item = itemsById[row.ID];
parentID = row.ParentOrderLineItemID;
if (parentID > 0 && structKeyExists(itemsById, parentID)) {
// This is a modifier - add to parent (use bracket notation)
arrayAppend(itemsById[parentID]["Modifiers"], item);
} else {
// This is a top-level item
arrayAppend(lineItems, item);
}
}
// Calculate subtotal from root line items (use bracket notation)
subtotal = 0;
for (item in lineItems) {
itemTotal = item["UnitPrice"] * item["Quantity"];
// Add modifier prices
for (mod in item["Modifiers"]) {
itemTotal += mod["UnitPrice"] * mod["Quantity"];
}
subtotal += itemTotal;
}
// Calculate tax using business tax rate or default 8.25%
taxRate = isNumeric(qOrder.TaxRate) && qOrder.TaxRate > 0 ? qOrder.TaxRate : 0.0825;
tax = subtotal * taxRate;
// Get tip from order
tip = isNumeric(qOrder.TipAmount) ? qOrder.TipAmount : 0;
// Calculate total
total = subtotal + tax + tip;
// Get staff who worked on this order (from Tasks table) with pending rating tokens
qStaff = queryExecute("
SELECT DISTINCT u.ID, u.FirstName,
(SELECT r.AccessToken
FROM TaskRatings r
INNER JOIN Tasks t2 ON t2.ID = r.TaskID
WHERE t2.OrderID = :orderID
AND r.ForUserID = u.ID
AND r.Direction = 'customer_rates_worker'
AND r.CompletedOn IS NULL
AND r.ExpiresOn > NOW()
LIMIT 1) AS RatingToken
FROM Tasks t
INNER JOIN Users u ON u.ID = t.ClaimedByUserID
WHERE t.OrderID = :orderID
AND t.ClaimedByUserID > 0
", { orderID: orderID });
// Build staff array with avatar URLs and rating tokens (use ordered structs)
staff = [];
for (row in qStaff) {
staffMember = structNew("ordered");
staffMember["UserID"] = row.ID;
staffMember["FirstName"] = row.FirstName;
staffMember["AvatarUrl"] = "https://biz.payfrit.com/uploads/users/" & row.ID & ".jpg";
staffMember["RatingToken"] = row.RatingToken ?: "";
arrayAppend(staff, staffMember);
}
// Build response (use ordered structs to preserve key casing)
customer = structNew("ordered");
customer["UserID"] = qOrder.UserID;
customer["FirstName"] = qOrder.FirstName;
customer["LastName"] = qOrder.LastName;
customer["Phone"] = qOrder.ContactNumber;
customer["Email"] = qOrder.EmailAddress;
servicePoint = structNew("ordered");
servicePoint["ServicePointID"] = qOrder.ServicePointID;
servicePoint["Name"] = qOrder.Name;
servicePoint["TypeID"] = qOrder.TypeID;
order = structNew("ordered");
order["OrderID"] = qOrder.ID;
order["BusinessID"] = qOrder.BusinessID;
order["Name"] = qOrder.Name ?: "";
order["Status"] = qOrder.StatusID;
order["StatusText"] = getStatusText(qOrder.StatusID);
order["OrderTypeID"] = qOrder.OrderTypeID ?: 0;
order["OrderTypeName"] = getOrderTypeName(qOrder.OrderTypeID ?: 0);
order["Subtotal"] = subtotal;
order["Tax"] = tax;
order["Tip"] = tip;
order["Total"] = total;
order["Notes"] = qOrder.Remarks;
order["CreatedOn"] = dateTimeFormat(qOrder.AddedOn, "yyyy-mm-dd HH:nn:ss");
order["SubmittedOn"] = len(qOrder.SubmittedOn) ? dateTimeFormat(qOrder.SubmittedOn, "yyyy-mm-dd HH:nn:ss") : "";
order["UpdatedOn"] = len(qOrder.LastEditedOn) ? dateTimeFormat(qOrder.LastEditedOn, "yyyy-mm-dd HH:nn:ss") : "";
order["Customer"] = customer;
order["ServicePoint"] = servicePoint;
order["LineItems"] = lineItems;
order["Staff"] = staff;
response["OK"] = true;
response["ORDER"] = order;
} catch (any e) {
response["ERROR"] = "server_error";
response["MESSAGE"] = e.message;
}
writeOutput(serializeJSON(response));
// Helper functions
function getStatusText(status) {
switch (status) {
case 0: return "Cart";
case 1: return "Submitted";
case 2: return "In Progress";
case 3: return "Ready";
case 4: return "On the Way";
case 5: return "Complete";
case 6: return "Cancelled";
case 7: return "Deleted";
default: return "Unknown";
}
}
function getOrderTypeName(orderType) {
switch (orderType) {
case 1: return "Dine-in";
case 2: return "Takeaway";
case 3: return "Delivery";
default: return "Unknown";
}
}
</cfscript>