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 39448c5d91 Fix prefixed column names in auth, orders, portal team, users search, workers APIs
Updated Users (UserID, UserFirstName, UserLastName, UserEmailAddress, UserContactNumber),
ServicePoints (ServicePointID, ServicePointName, ServicePointTypeID), and Businesses
(BusinessID, BusinessName, BusinessTaxRate, BusinessPhone) column references with proper
prefixed names and AS aliases for API compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 17:43:33 -08:00

249 lines
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.UserFirstName AS FirstName,
u.UserLastName AS LastName,
u.UserContactNumber AS ContactNumber,
u.UserEmailAddress AS EmailAddress,
sp.ServicePointName AS Name,
sp.ServicePointTypeID AS TypeID,
b.BusinessName AS BizName,
b.BusinessTaxRate AS TaxRate
FROM Orders o
LEFT JOIN Users u ON u.UserID = o.UserID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = o.ServicePointID
LEFT JOIN Businesses b ON b.BusinessID = 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.UserID AS ID, u.UserFirstName AS FirstName,
(SELECT r.AccessToken
FROM TaskRatings r
INNER JOIN Tasks t2 ON t2.ID = r.TaskID
WHERE t2.OrderID = :orderID
AND r.ForUserID = u.UserID
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.UserID = 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>