false, 'ERROR' => 'missing_params', 'MESSAGE' => 'TaskID is required.']); } try { $qTask = queryOne(" SELECT t.ID AS TaskID, t.BusinessID, t.OrderID, t.TaskTypeID, t.CreatedOn, t.ClaimedByUserID, t.ServicePointID AS TaskServicePointID, tt.Name AS TaskTypeName, tt.Color AS TaskTypeColor, o.ID AS OID, o.UUID AS OrderUUID, o.UserID AS OrderUserID, o.OrderTypeID, o.StatusID AS OrderStatusID, o.ServicePointID AS OrderServicePointID, o.Remarks, o.SubmittedOn, o.TipAmount, o.DeliveryFee, b.TaxRate, b.PayfritFee, COALESCE(sp.Name, tsp.Name) AS ServicePointName, COALESCE(sp.TypeID, tsp.TypeID) AS ServicePointTypeID, COALESCE(sp.ID, tsp.ID) AS ServicePointID, u.ID AS CustomerUserID, u.FirstName, u.LastName, u.ContactNumber, u.ImageExtension AS CustomerImageExtension FROM Tasks t LEFT JOIN tt_TaskTypes tt ON tt.ID = t.TaskTypeID LEFT JOIN Orders o ON o.ID = t.OrderID LEFT JOIN Businesses b ON b.ID = t.BusinessID LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID LEFT JOIN ServicePoints tsp ON tsp.ID = t.ServicePointID LEFT JOIN Users u ON u.ID = COALESCE(NULLIF(o.UserID, 0), NULLIF(t.UserID, 0)) WHERE t.ID = ? ", [$taskID]); if (!$qTask) { apiAbort(['OK' => false, 'ERROR' => 'not_found', 'MESSAGE' => 'Task not found.']); } $taskTitle = ((int) ($qTask['OrderID'] ?? 0) > 0) ? "Order #" . $qTask['OrderID'] : "Task #" . $qTask['TaskID']; // Check if user photo file exists $customerPhotoUrl = ''; $customerUserID = (int) ($qTask['CustomerUserID'] ?? 0); if ($customerUserID > 0) { $baseDir = '/uploads/users/'; foreach (['jpg', 'png', 'PNG'] as $ext) { $checkPath = luceeWebroot() . $baseDir . $customerUserID . '.' . $ext; if (file_exists($checkPath)) { $customerPhotoUrl = baseUrl() . $baseDir . $customerUserID . '.' . $ext; break; } } } $result = [ 'TaskID' => (int) $qTask['TaskID'], 'TaskBusinessID' => (int) $qTask['BusinessID'], 'TaskTypeID' => (int) ($qTask['TaskTypeID'] ?? 1), 'TaskTypeName' => $qTask['TaskTypeName'] ?? '', 'TaskTypeColor' => !empty(trim($qTask['TaskTypeColor'] ?? '')) ? $qTask['TaskTypeColor'] : '#9C27B0', 'TaskTitle' => $taskTitle, 'TaskCreatedOn' => toISO8601($qTask['CreatedOn']), 'TaskStatusID' => (int) $qTask['ClaimedByUserID'] > 0 ? 1 : 0, 'OrderID' => (int) ($qTask['OrderID'] ?? 0), 'OrderRemarks' => $qTask['Remarks'] ?? '', 'OrderSubmittedOn' => toISO8601($qTask['SubmittedOn'] ?? ''), 'OrderTotal' => 0, 'OrderTotalCents' => 0, 'ServicePointID' => (int) ($qTask['ServicePointID'] ?? 0), 'ServicePointName' => $qTask['ServicePointName'] ?? '', 'ServicePointTypeID' => (int) ($qTask['ServicePointTypeID'] ?? 0), 'DeliveryAddress' => '', 'DeliveryLat' => 0, 'DeliveryLng' => 0, 'CustomerUserID' => $customerUserID, 'CustomerFirstName' => $qTask['FirstName'] ?? '', 'CustomerLastName' => $qTask['LastName'] ?? '', 'CustomerPhone' => $qTask['ContactNumber'] ?? '', 'CustomerPhotoUrl' => $customerPhotoUrl, 'BeaconUUID' => '', 'BeaconMajor' => 0, 'BeaconMinor' => 0, 'LineItems' => [], 'TableMembers' => [], ]; // Get beacon sharding info $spID = (int) ($qTask['ServicePointID'] ?? 0); if ($spID > 0) { $qBeacon = queryOne(" SELECT bs.UUID AS ShardUUID, b.BeaconMajor, sp.BeaconMinor FROM ServicePoints sp JOIN Businesses b ON b.ID = sp.BusinessID JOIN BeaconShards bs ON bs.ID = b.BeaconShardID WHERE sp.ID = ? AND bs.IsActive = 1 LIMIT 1 ", [$spID]); if ($qBeacon) { $result['BeaconUUID'] = $qBeacon['ShardUUID']; $result['BeaconMajor'] = (int) $qBeacon['BeaconMajor']; $result['BeaconMinor'] = (int) $qBeacon['BeaconMinor']; } } // Get order line items if there's an order if ((int) ($qTask['OrderID'] ?? 0) > 0) { $qLineItems = queryTimed(" SELECT oli.ID AS OrderLineItemID, oli.ParentOrderLineItemID, oli.ItemID, oli.Price AS LineItemPrice, oli.Quantity, oli.Remark, i.ID AS IID, i.Name AS ItemName, i.ParentItemID, i.Price AS ItemPrice, i.IsCheckedByDefault FROM OrderLineItems oli INNER JOIN Items i ON i.ID = oli.ItemID WHERE oli.OrderID = ? AND oli.IsDeleted = 0 ORDER BY oli.ID ", [$qTask['OrderID']]); $subtotal = 0; foreach ($qLineItems as $li) { $subtotal += (float) $li['LineItemPrice'] * (int) $li['Quantity']; $result['LineItems'][] = [ 'LineItemID' => (int) $li['OrderLineItemID'], 'ParentLineItemID' => (int) $li['ParentOrderLineItemID'], 'ItemID' => (int) $li['ItemID'], 'ItemName' => $li['ItemName'], 'ItemPrice' => (float) $li['LineItemPrice'], 'Quantity' => (int) $li['Quantity'], 'Remark' => $li['Remark'] ?? '', 'IsModifier' => (int) $li['ParentOrderLineItemID'] > 0, ]; } // Calculate order total $taxAmount = $subtotal * (float) ($qTask['TaxRate'] ?? 0); $tipAmount = (float) ($qTask['TipAmount'] ?? 0); $deliveryFee = ((int) ($qTask['OrderTypeID'] ?? 0) === 3) ? (float) ($qTask['DeliveryFee'] ?? 0) : 0; $feeRate = (is_numeric($qTask['PayfritFee'] ?? null) && (float) $qTask['PayfritFee'] > 0) ? (float) $qTask['PayfritFee'] : 0.05; $platformFee = $subtotal * $feeRate; $totalAmount = $subtotal + $taxAmount + $tipAmount + $deliveryFee + $platformFee; $result['OrderTotal'] = number_format($totalAmount, 2, '.', ''); $result['OrderTotalCents'] = (int) round($totalAmount * 100); } jsonResponse(['OK' => true, 'ERROR' => '', 'TASK' => $result]); } catch (Exception $e) { jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => 'Error loading task details', 'DETAIL' => $e->getMessage()]); }