/** * Cancel Tab * Only allowed if no approved orders with non-zero subtotals exist. * Releases the Stripe hold. * * POST: { TabID: int, UserID: int } */ try { requestData = deserializeJSON(toString(getHttpRequestData().content)); tabID = val(requestData.TabID ?: 0); userID = val(requestData.UserID ?: 0); if (tabID == 0) apiAbort({ "OK": false, "ERROR": "missing_TabID" }); if (userID == 0) apiAbort({ "OK": false, "ERROR": "missing_UserID" }); qTab = queryTimed(" SELECT ID, OwnerUserID, StatusID, StripePaymentIntentID, BusinessID FROM Tabs WHERE 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" }); if (qTab.OwnerUserID != userID) apiAbort({ "OK": false, "ERROR": "not_owner" }); // Check for any approved orders (regardless of kitchen status) qApproved = queryTimed(" SELECT COUNT(*) AS Cnt, COALESCE(SUM(SubtotalCents), 0) AS TotalCents FROM TabOrders WHERE TabID = :tabID AND ApprovalStatus = 'approved' ", { tabID: { value: tabID, cfsqltype: "cf_sql_integer" } }); if (qApproved.Cnt > 0 && val(qApproved.TotalCents) > 0) { apiAbort({ "OK": false, "ERROR": "has_orders", "MESSAGE": "Tab has approved orders. Close the tab instead of cancelling." }); } // Cancel Stripe PI if (len(trim(qTab.StripePaymentIntentID))) { cfhttp(method="POST", url="https://api.stripe.com/v1/payment_intents/#qTab.StripePaymentIntentID#/cancel", result="cancelResp") { cfhttpparam(type="header", name="Authorization", value="Bearer #application.stripeSecretKey#"); } } // Mark tab cancelled, release members queryTimed("UPDATE Tabs SET StatusID = 4, ClosedOn = NOW(), PaymentStatus = 'cancelled' WHERE ID = :tabID", { tabID: { value: tabID, cfsqltype: "cf_sql_integer" } }); queryTimed("UPDATE TabMembers SET StatusID = 3, LeftOn = NOW() WHERE TabID = :tabID AND StatusID = 1", { tabID: { value: tabID, cfsqltype: "cf_sql_integer" } }); apiAbort({ "OK": true, "MESSAGE": "Tab cancelled. Card hold released." }); } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": e.message }); }