- open.cfm: accept optional ApprovalMode param (0=auto, 1=manual, NULL=business default) - addOrder.cfm: check tab ApprovalMode first, fall back to business TabApprovalRequired - getActive.cfm: return resolved ApprovalRequired in tab response Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
6.1 KiB
Text
150 lines
6.1 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>
|
|
/**
|
|
* Add Order to Tab
|
|
* Links an order to the tab. Auto-approves for owner, pending for members.
|
|
*
|
|
* POST: { TabID: int, OrderID: int, UserID: int }
|
|
*/
|
|
|
|
try {
|
|
requestData = deserializeJSON(toString(getHttpRequestData().content));
|
|
tabID = val(requestData.TabID ?: 0);
|
|
orderID = val(requestData.OrderID ?: 0);
|
|
userID = val(requestData.UserID ?: 0);
|
|
|
|
if (tabID == 0) apiAbort({ "OK": false, "ERROR": "missing_TabID" });
|
|
if (orderID == 0) apiAbort({ "OK": false, "ERROR": "missing_OrderID" });
|
|
if (userID == 0) apiAbort({ "OK": false, "ERROR": "missing_UserID" });
|
|
|
|
// Get tab
|
|
qTab = queryTimed("
|
|
SELECT t.ID, t.StatusID, t.BusinessID, t.OwnerUserID, t.AuthAmountCents, t.RunningTotalCents,
|
|
t.ApprovalMode, b.TabApprovalRequired, b.TabAutoIncreaseThreshold
|
|
FROM Tabs t
|
|
JOIN Businesses b ON b.ID = t.BusinessID
|
|
WHERE t.ID = :tabID LIMIT 1
|
|
", { tabID: { value: tabID, cfsqltype: "cf_sql_integer" } });
|
|
|
|
if (qTab.recordCount == 0) apiAbort({ "OK": false, "ERROR": "tab_not_found" });
|
|
if (qTab.StatusID != 1) apiAbort({ "OK": false, "ERROR": "tab_not_open" });
|
|
|
|
// Verify user is a member
|
|
qMember = queryTimed("
|
|
SELECT RoleID FROM TabMembers
|
|
WHERE TabID = :tabID AND UserID = :userID AND StatusID = 1 LIMIT 1
|
|
", {
|
|
tabID: { value: tabID, cfsqltype: "cf_sql_integer" },
|
|
userID: { value: userID, cfsqltype: "cf_sql_integer" }
|
|
});
|
|
if (qMember.recordCount == 0) apiAbort({ "OK": false, "ERROR": "not_a_member" });
|
|
|
|
isOwner = qMember.RoleID == 1;
|
|
|
|
// Verify order belongs to same business and is in cart state
|
|
qOrder = queryTimed("
|
|
SELECT ID, BusinessID, StatusID, UserID FROM Orders
|
|
WHERE ID = :orderID LIMIT 1
|
|
", { orderID: { value: orderID, cfsqltype: "cf_sql_integer" } });
|
|
|
|
if (qOrder.recordCount == 0) apiAbort({ "OK": false, "ERROR": "order_not_found" });
|
|
if (qOrder.BusinessID != qTab.BusinessID) apiAbort({ "OK": false, "ERROR": "wrong_business" });
|
|
if (qOrder.StatusID != 0) apiAbort({ "OK": false, "ERROR": "order_not_in_cart", "MESSAGE": "Order must be in cart state." });
|
|
|
|
// Calculate order subtotal + tax
|
|
qTotals = queryTimed("
|
|
SELECT COALESCE(SUM(oli.Price * oli.Quantity), 0) AS Subtotal
|
|
FROM OrderLineItems oli
|
|
WHERE oli.OrderID = :orderID AND oli.IsDeleted = 0
|
|
", { orderID: { value: orderID, cfsqltype: "cf_sql_integer" } });
|
|
|
|
subtotal = val(qTotals.Subtotal);
|
|
subtotalCents = round(subtotal * 100);
|
|
|
|
// Get tax rate from business
|
|
qBizTax = queryTimed("SELECT TaxRate FROM Businesses WHERE ID = :bizID", {
|
|
bizID: { value: qTab.BusinessID, cfsqltype: "cf_sql_integer" }
|
|
});
|
|
taxRate = val(qBizTax.TaxRate);
|
|
taxCents = round(subtotalCents * taxRate);
|
|
|
|
// Determine approval status: per-tab ApprovalMode overrides business default
|
|
approvalStatus = "approved";
|
|
requiresApproval = len(trim(qTab.ApprovalMode)) && isNumeric(qTab.ApprovalMode)
|
|
? val(qTab.ApprovalMode) == 1
|
|
: qTab.TabApprovalRequired == 1;
|
|
if (!isOwner && requiresApproval) {
|
|
approvalStatus = "pending";
|
|
}
|
|
|
|
// Check if adding this would exceed authorization (only for auto-approved orders)
|
|
newRunning = qTab.RunningTotalCents;
|
|
if (approvalStatus == "approved") {
|
|
newRunning = qTab.RunningTotalCents + subtotalCents + taxCents;
|
|
if (newRunning > qTab.AuthAmountCents) {
|
|
apiAbort({
|
|
"OK": false, "ERROR": "exceeds_authorization",
|
|
"MESSAGE": "This order would exceed your tab authorization. Please increase your authorization first.",
|
|
"RUNNING_TOTAL_CENTS": qTab.RunningTotalCents,
|
|
"ORDER_CENTS": subtotalCents + taxCents,
|
|
"AUTH_AMOUNT_CENTS": qTab.AuthAmountCents
|
|
});
|
|
}
|
|
}
|
|
|
|
// Link order to tab
|
|
queryTimed("UPDATE Orders SET TabID = :tabID WHERE ID = :orderID", {
|
|
tabID: { value: tabID, cfsqltype: "cf_sql_integer" },
|
|
orderID: { value: orderID, cfsqltype: "cf_sql_integer" }
|
|
});
|
|
|
|
// Insert into TabOrders
|
|
queryTimed("
|
|
INSERT INTO TabOrders (TabID, OrderID, UserID, ApprovalStatus, SubtotalCents, TaxCents, AddedOn)
|
|
VALUES (:tabID, :orderID, :userID, :status, :subtotalCents, :taxCents, NOW())
|
|
ON DUPLICATE KEY UPDATE ApprovalStatus = VALUES(ApprovalStatus), SubtotalCents = VALUES(SubtotalCents), TaxCents = VALUES(TaxCents)
|
|
", {
|
|
tabID: { value: tabID, cfsqltype: "cf_sql_integer" },
|
|
orderID: { value: orderID, cfsqltype: "cf_sql_integer" },
|
|
userID: { value: userID, cfsqltype: "cf_sql_integer" },
|
|
status: { value: approvalStatus, cfsqltype: "cf_sql_varchar" },
|
|
subtotalCents: { value: subtotalCents, cfsqltype: "cf_sql_integer" },
|
|
taxCents: { value: taxCents, cfsqltype: "cf_sql_integer" }
|
|
});
|
|
|
|
// If auto-approved, update running total
|
|
if (approvalStatus == "approved") {
|
|
queryTimed("
|
|
UPDATE Tabs SET RunningTotalCents = :newRunning, LastActivityOn = NOW()
|
|
WHERE ID = :tabID
|
|
", {
|
|
newRunning: { value: newRunning, cfsqltype: "cf_sql_integer" },
|
|
tabID: { value: tabID, cfsqltype: "cf_sql_integer" }
|
|
});
|
|
}
|
|
|
|
// Check if auto-increase threshold reached
|
|
needsIncrease = false;
|
|
threshold = val(qTab.TabAutoIncreaseThreshold);
|
|
if (threshold > 0 && qTab.AuthAmountCents > 0) {
|
|
needsIncrease = (newRunning / qTab.AuthAmountCents) >= threshold;
|
|
}
|
|
|
|
apiAbort({
|
|
"OK": true,
|
|
"APPROVAL_STATUS": approvalStatus,
|
|
"RUNNING_TOTAL_CENTS": newRunning,
|
|
"AUTH_REMAINING_CENTS": qTab.AuthAmountCents - newRunning,
|
|
"NEEDS_INCREASE": needsIncrease,
|
|
"ORDER_SUBTOTAL_CENTS": subtotalCents,
|
|
"ORDER_TAX_CENTS": taxCents
|
|
});
|
|
|
|
} catch (any e) {
|
|
apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": e.message });
|
|
}
|
|
</cfscript>
|