prepare( "SELECT t.id, t.status, t.route_id, r.name_ar AS route_name FROM transit_trips t JOIN transit_routes r ON r.id = t.route_id WHERE t.id=? AND t.org_id=? LIMIT 1" ); $st->execute([$tripId, $transit_org_id]); $trip = $st->fetch(); if (!$trip) jsonError('Trip not found or access denied', 404); if (in_array($trip['status'], ['completed', 'cancelled'])) { jsonError('Trip is already ' . $trip['status'], 409); } $wasStarted = $trip['status'] === 'started'; $transit_con->prepare( "UPDATE transit_trips SET status='cancelled', delay_reason=?, updated_at=NOW() WHERE id=?" )->execute([filterRequest('reason'), $tripId]); // إن كانت الرحلة قد بدأت فعلياً، أوقف بث الباص وحرّر ملكية الرحلة فوراً if ($wasStarted) { transitClearTripOwner($tripId); global $redisLocation; if ($redisLocation) $redisLocation->del("transit:trip:{$tripId}:pos"); global $redis; if ($redis) $redis->del("transit:trip:{$tripId}:status"); } transitSendTopicNotification( transitRouteTopic((int)$trip['route_id']), 'تم إلغاء الرحلة', 'أُلغيت رحلة خط ' . $trip['route_name'] . ' اليوم.', ['type' => 'transit_cancelled', 'trip_id' => (string)$tripId] ); appLog("[TRANSIT][TRIP][cancel] trip #{$tripId} org#{$transit_org_id} cancelled by admin #{$transit_admin_id}"); jsonSuccess(['trip_id' => $tripId, 'status' => 'cancelled'], 'Trip cancelled');