/** * 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 }); }