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/history.cfm
John Mizerek bffca643b5 Restore API performance tracking and fix perf dashboard
- Add queryTimed(), logPerf(), flushPerfBuffer() to environment.cfm
- Auto-create ApiPerfLogs table on first flush
- Hook logPerf into Application.cfm apiAbort for automatic tracking
- Initialize request perf counters in Application.cfm
- Remove local apiAbort() overrides from 7 endpoints
- Instrument 12 high-traffic endpoints with logPerf calls
- Buffer metrics in application scope, batch INSERT every 100 requests
- 30-day auto-cleanup with probabilistic trigger

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 17:04:12 -08:00

176 lines
5 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>
/**
* Order History API
* Returns list of completed/submitted orders for the authenticated user
*
* GET: ?limit=20&offset=0
*/
function apiAbort(required struct payload) {
writeOutput(serializeJSON(payload));
abort;
}
// Helper to get header value - use servlet request object (CGI scope doesn't expose custom HTTP headers in Lucee)
function getHeader(name) {
try {
req = getPageContext().getRequest();
val = req.getHeader(arguments.name);
if (!isNull(val)) return trim(val);
} catch (any e) {
// Fall back to CGI scope
k = "HTTP_" & ucase(reReplace(arguments.name, "[^A-Za-z0-9]", "_", "all"));
if (structKeyExists(cgi, k)) return trim(cgi[k]);
}
return "";
}
// Get authenticated user - try request scope first, then do token lookup
userId = 0;
if (structKeyExists(request, "UserID") && isNumeric(request.UserID) && request.UserID > 0) {
userId = request.UserID;
} else {
userToken = getHeader("X-User-Token");
if (len(userToken)) {
try {
qTok = queryExecute(
"SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1",
[ { value = userToken, cfsqltype = "cf_sql_varchar" } ],
{ datasource = "payfrit" }
);
if (qTok.recordCount EQ 1) {
userId = qTok.UserID;
}
} catch (any e) { /* ignore */ }
}
}
if (userId <= 0) {
apiAbort({ "OK": false, "ERROR": "not_logged_in", "MESSAGE": "Authentication required" });
}
// Parse params
limit = val(url.limit ?: 20);
offset = val(url.offset ?: 0);
if (limit < 1) limit = 20;
if (limit > 100) limit = 100;
if (offset < 0) offset = 0;
try {
// Get orders for this user (exclude carts - status 0)
qOrders = queryExecute("
SELECT
o.ID,
o.UUID,
o.BusinessID,
o.StatusID,
o.OrderTypeID,
o.AddedOn,
o.LastEditedOn,
b.Name,
COALESCE(ot.Name, 'Unknown') as OrderTypeName
FROM Orders o
LEFT JOIN Businesses b ON b.ID = o.BusinessID
LEFT JOIN tt_OrderTypes ot ON ot.ID = o.OrderTypeID
WHERE o.UserID = :userId
AND o.StatusID > 0
ORDER BY o.AddedOn DESC
LIMIT :limit OFFSET :offset
", {
userId: { value = userId, cfsqltype = "cf_sql_integer" },
limit: { value = limit, cfsqltype = "cf_sql_integer" },
offset: { value = offset, cfsqltype = "cf_sql_integer" }
});
// Get total count
qCount = queryExecute("
SELECT COUNT(*) as TotalCount
FROM Orders
WHERE UserID = :userId
AND StatusID > 0
", { userId: { value = userId, cfsqltype = "cf_sql_integer" } });
// Build orders array with item counts and totals
orders = [];
for (row in qOrders) {
// Get line item count and calculate total
qItems = queryExecute("
SELECT
COUNT(*) as ItemCount,
SUM(Quantity * Price) as Subtotal
FROM OrderLineItems
WHERE OrderID = :orderId
AND ParentOrderLineItemID = 0
AND (IsDeleted = 0 OR IsDeleted IS NULL)
", { orderId: { value = row.ID, cfsqltype = "cf_sql_integer" } });
itemCount = val(qItems.ItemCount);
subtotal = val(qItems.Subtotal);
tax = subtotal * 0.0875;
total = subtotal + tax;
// Get status text
statusText = "";
switch (row.StatusID) {
case 1: statusText = "Submitted"; break;
case 2: statusText = "In Progress"; break;
case 3: statusText = "Ready"; break;
case 4: statusText = "Completed"; break;
case 5: statusText = "Cancelled"; break;
default: statusText = "Unknown";
}
// Safely format dates
createdAt = "";
try {
if (!isNull(row.AddedOn) && len(trim(row.AddedOn))) {
createdAt = dateTimeFormat(row.AddedOn, "yyyy-mm-dd'T'HH:nn:ss");
}
} catch (any de) { createdAt = ""; }
completedAt = "";
try {
if (row.StatusID >= 4 && !isNull(row.LastEditedOn) && len(trim(row.LastEditedOn))) {
completedAt = dateTimeFormat(row.LastEditedOn, "yyyy-mm-dd'T'HH:nn:ss");
}
} catch (any de) { completedAt = ""; }
arrayAppend(orders, {
"OrderID": val(row.ID),
"UUID": row.UUID ?: "",
"BusinessID": val(row.BusinessID),
"Name": row.Name ?: "Unknown",
"OrderTotal": round(val(total) * 100) / 100,
"StatusID": val(row.StatusID),
"StatusName": statusText,
"OrderTypeID": val(row.OrderTypeID),
"TypeName": row.OrderTypeName ?: "Unknown",
"ItemCount": val(itemCount),
"CreatedAt": createdAt,
"CompletedAt": completedAt
});
}
try{logPerf(0);}catch(any e){}
writeOutput(serializeJSON({
"OK": true,
"ORDERS": orders,
"TOTAL_COUNT": qCount.TotalCount
}));
} catch (any e) {
apiAbort({
"OK": false,
"ERROR": "server_error",
"MESSAGE": "Failed to load order history",
"DETAIL": e.message,
"DEBUG_LINE": e.tagContext[1].line ?: 0,
"DEBUG_TEMPLATE": e.tagContext[1].template ?: ""
});
}
</cfscript>