/** * 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.OrderID, o.OrderBusinessID, o.OrderUserID, o.OrderServicePointID, o.OrderStatusID, o.OrderTypeID, o.OrderRemarks, o.OrderAddedOn, o.OrderLastEditedOn, o.OrderSubmittedOn, o.OrderTipAmount, u.UserFirstName, u.UserLastName, u.UserContactNumber, u.UserEmailAddress, sp.ServicePointName, sp.ServicePointTypeID, b.BusinessName, b.BusinessTaxRate FROM Orders o LEFT JOIN Users u ON u.UserID = o.OrderUserID LEFT JOIN ServicePoints sp ON sp.ServicePointID = o.OrderServicePointID LEFT JOIN Businesses b ON b.BusinessID = o.OrderBusinessID WHERE o.OrderID = :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.OrderLineItemID, oli.OrderLineItemItemID, oli.OrderLineItemParentOrderLineItemID, oli.OrderLineItemQuantity, oli.OrderLineItemPrice, oli.OrderLineItemRemark, i.ItemName, i.ItemPrice, i.ItemIsCheckedByDefault FROM OrderLineItems oli INNER JOIN Items i ON i.ItemID = oli.OrderLineItemItemID WHERE oli.OrderLineItemOrderID = :orderID AND oli.OrderLineItemIsDeleted = 0 ORDER BY oli.OrderLineItemID ", { orderID: orderID }); // Build line items array with parent-child structure lineItems = []; itemsById = {}; // First pass: create all items for (row in qItems) { item = { "LineItemID": val(row.OrderLineItemID), "ItemID": val(row.OrderLineItemItemID), "ParentLineItemID": val(row.OrderLineItemParentOrderLineItemID), "ItemName": row.ItemName ?: "", "Quantity": val(row.OrderLineItemQuantity), "UnitPrice": val(row.OrderLineItemPrice), "Remarks": row.OrderLineItemRemark ?: "", "IsDefault": (val(row.ItemIsCheckedByDefault) == 1), "Modifiers": [] }; itemsById[row.OrderLineItemID] = item; } // Second pass: build hierarchy for (row in qItems) { item = itemsById[row.OrderLineItemID]; parentID = row.OrderLineItemParentOrderLineItemID; if (parentID > 0 && structKeyExists(itemsById, parentID)) { // This is a modifier - add to parent arrayAppend(itemsById[parentID].Modifiers, item); } else { // This is a top-level item arrayAppend(lineItems, item); } } // Calculate subtotal from root line items 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.BusinessTaxRate) && qOrder.BusinessTaxRate > 0 ? qOrder.BusinessTaxRate : 0.0825; tax = subtotal * taxRate; // Get tip from order tip = isNumeric(qOrder.OrderTipAmount) ? qOrder.OrderTipAmount : 0; // Calculate total total = subtotal + tax + tip; // Get staff who worked on this order (from Tasks table) qStaff = queryExecute(" SELECT DISTINCT u.UserID, u.UserFirstName FROM Tasks t INNER JOIN Users u ON u.UserID = t.TaskClaimedByUserID WHERE t.TaskOrderID = :orderID AND t.TaskClaimedByUserID > 0 ", { orderID: orderID }); // Build staff array with avatar URLs staff = []; for (row in qStaff) { arrayAppend(staff, { "UserID": row.UserID, "FirstName": row.UserFirstName, "AvatarUrl": "https://biz.payfrit.com/uploads/users/" & row.UserID & ".jpg" }); } // Build response order = { "OrderID": qOrder.OrderID, "BusinessID": qOrder.OrderBusinessID, "BusinessName": qOrder.BusinessName ?: "", "Status": qOrder.OrderStatusID, "StatusText": getStatusText(qOrder.OrderStatusID), "OrderTypeID": qOrder.OrderTypeID ?: 0, "OrderTypeName": getOrderTypeName(qOrder.OrderTypeID ?: 0), "Subtotal": subtotal, "Tax": tax, "Tip": tip, "Total": total, "Notes": qOrder.OrderRemarks, "CreatedOn": dateTimeFormat(qOrder.OrderAddedOn, "yyyy-mm-dd HH:nn:ss"), "SubmittedOn": len(qOrder.OrderSubmittedOn) ? dateTimeFormat(qOrder.OrderSubmittedOn, "yyyy-mm-dd HH:nn:ss") : "", "UpdatedOn": len(qOrder.OrderLastEditedOn) ? dateTimeFormat(qOrder.OrderLastEditedOn, "yyyy-mm-dd HH:nn:ss") : "", "Customer": { "UserID": qOrder.OrderUserID, "FirstName": qOrder.UserFirstName, "LastName": qOrder.UserLastName, "Phone": qOrder.UserContactNumber, "Email": qOrder.UserEmailAddress }, "ServicePoint": { "ServicePointID": qOrder.OrderServicePointID, "Name": qOrder.ServicePointName, "TypeID": qOrder.ServicePointTypeID }, "LineItems": lineItems, "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"; } }