false, 'ERROR' => 'missing_params', 'MESSAGE' => 'BusinessID and UserID are required.']); } if ($OrderTypeID === 1 && $ServicePointID <= 0) { apiAbort(['OK' => false, 'ERROR' => 'missing_service_point', 'MESSAGE' => 'ServicePointID is required for dine-in. Please scan a table beacon.']); } if ($OrderTypeID < 0 || $OrderTypeID > 3) { apiAbort(['OK' => false, 'ERROR' => 'invalid_order_type', 'MESSAGE' => 'OrderTypeID must be 0-3 (0=undecided, 1=dine-in, 2=takeaway, 3=delivery).']); } try { $qBiz = queryOne("SELECT DeliveryFlatFee FROM Businesses WHERE ID = ? LIMIT 1", [$BusinessID]); if (!$qBiz) { apiAbort(['OK' => false, 'ERROR' => 'bad_business', 'MESSAGE' => 'Business not found']); } // SP-SM: Resolve grant if ServicePoint doesn't belong to this business $grantID = 0; $grantOwnerBusinessID = 0; $grantEconomicsType = ''; $grantEconomicsValue = 0; if ($ServicePointID > 0) { $qSPOwner = queryOne("SELECT BusinessID FROM ServicePoints WHERE ID = ? LIMIT 1", [$ServicePointID]); if ($qSPOwner && (int) $qSPOwner['BusinessID'] !== $BusinessID) { // SP belongs to another business $qParent = queryOne("SELECT ParentBusinessID FROM Businesses WHERE ID = ? LIMIT 1", [$BusinessID]); $isChildOfSPOwner = $qParent && (int) ($qParent['ParentBusinessID'] ?? 0) === (int) $qSPOwner['BusinessID']; if (!$isChildOfSPOwner) { // Check for active grant $qGrant = queryOne( "SELECT ID, OwnerBusinessID, EconomicsType, EconomicsValue, EligibilityScope, TimePolicyType, TimePolicyData FROM ServicePointGrants WHERE GuestBusinessID = ? AND ServicePointID = ? AND StatusID = 1 LIMIT 1", [$BusinessID, $ServicePointID] ); if (!$qGrant) { apiAbort(['OK' => false, 'ERROR' => 'sp_not_accessible', 'MESSAGE' => 'Service point is not accessible to your business.']); } if (!isGrantTimeActive($qGrant['TimePolicyType'], $qGrant['TimePolicyData'])) { apiAbort(['OK' => false, 'ERROR' => 'grant_time_inactive', 'MESSAGE' => 'Service point access is not available at this time.']); } if (!checkGrantEligibility($qGrant['EligibilityScope'], $UserID, (int) $qGrant['OwnerBusinessID'], $BusinessID)) { apiAbort(['OK' => false, 'ERROR' => 'grant_eligibility_failed', 'MESSAGE' => 'You are not eligible to order at this service point.']); } $grantID = (int) $qGrant['ID']; $grantOwnerBusinessID = (int) $qGrant['OwnerBusinessID']; $grantEconomicsType = $qGrant['EconomicsType']; $grantEconomicsValue = (float) $qGrant['EconomicsValue']; } } } // Check if user is on an active tab at this business $tabID = 0; $qUserTab = queryOne(" SELECT t.ID FROM TabMembers tm JOIN Tabs t ON t.ID = tm.TabID WHERE tm.UserID = ? AND tm.StatusID = 1 AND t.BusinessID = ? AND t.StatusID = 1 LIMIT 1 ", [$UserID, $BusinessID]); if ($qUserTab) { $tabID = (int) $qUserTab['ID']; } $nowISO = gmdate('Y-m-d H:i:s'); $newUUID = generateUUID(); $deliveryFee = ($OrderTypeID === 3) ? (float) $qBiz['DeliveryFlatFee'] : 0; // Generate new OrderID (table is not auto-inc) $qNext = queryOne("SELECT IFNULL(MAX(ID),0) + 1 AS NextID FROM Orders", []); $NewOrderID = (int) $qNext['NextID']; queryTimed(" INSERT INTO Orders ( ID, UUID, UserID, BusinessID, DeliveryMultiplier, OrderTypeID, DeliveryFee, StatusID, AddressID, PaymentID, Remarks, AddedOn, LastEditedOn, SubmittedOn, ServicePointID, GrantID, GrantOwnerBusinessID, GrantEconomicsType, GrantEconomicsValue, TabID ) VALUES ( ?, ?, ?, ?, 1.0, ?, ?, 0, NULL, NULL, NULL, ?, ?, NULL, ?, ?, ?, ?, ?, ? ) ", [ $NewOrderID, $newUUID, $UserID, $BusinessID, $OrderTypeID, $deliveryFee, $nowISO, $nowISO, $ServicePointID, $grantID > 0 ? $grantID : null, $grantOwnerBusinessID > 0 ? $grantOwnerBusinessID : null, strlen($grantEconomicsType) > 0 ? $grantEconomicsType : null, ($grantEconomicsType !== '' && $grantEconomicsType !== 'none') ? $grantEconomicsValue : null, $tabID > 0 ? $tabID : null, ]); // Get the final ID $qLatest = queryOne("SELECT MAX(ID) AS NextID FROM Orders", []); $FinalOrderID = (int) $qLatest['NextID']; $payload = loadCartPayload($FinalOrderID); jsonResponse($payload); } catch (Exception $e) { jsonResponse([ 'OK' => false, 'ERROR' => 'server_error: ' . $e->getMessage(), 'MESSAGE' => 'DB error creating cart', 'DETAIL' => $e->getMessage(), ]); }