copy()->addMinutes($duration); $query = Appointment::where('user_id', $therapistId) ->whereIn('status', ['scheduled', 'confirmed', 'in_progress']) ->where(function ($q) use ($startTime, $endTime) { // Check if any appointment overlaps with the requested time $q->whereBetween('appointment_date', [$startTime, $endTime->copy()->subSecond()]) ->orWhereRaw("datetime(appointment_date, '+' || duration_minutes || ' minutes') BETWEEN ? AND ?", [$startTime, $endTime]); }); if ($excludeId) { $query->where('id', '!=', $excludeId); } return $query->exists(); } /** * Check for patient double-booking */ public function hasPatientConflict(int $patientId, Carbon $startTime, int $duration, ?int $excludeId = null): bool { $endTime = $startTime->copy()->addMinutes($duration); $query = Appointment::where('patient_id', $patientId) ->whereIn('status', ['scheduled', 'confirmed', 'in_progress']) ->whereBetween('appointment_date', [$startTime, $endTime->copy()->subSecond()]); if ($excludeId) { $query->where('id', '!=', $excludeId); } return $query->exists(); } /** * Get available time slots for a therapist on a given date */ public function getAvailableSlots(int $therapistId, string $date, int $duration = 60): array { $workingHours = ['09:00', '17:00']; // 9 AM to 5 PM $slotInterval = 30; // minutes $slots = []; $start = Carbon::parse("$date {$workingHours[0]}"); $end = Carbon::parse("$date {$workingHours[1]}"); while ($start->lt($end)) { $slotEnd = $start->copy()->addMinutes($duration); if ($slotEnd->lte($end) && !$this->hasConflict($therapistId, $start, $duration)) { $slots[] = $start->format('H:i'); } $start->addMinutes($slotInterval); } return $slots; } /** * Get calendar feed for a date range */ public function getCalendarFeed(string $startDate, string $endDate, ?int $therapistId = null): array { $query = Appointment::with(['patient', 'user']) ->whereBetween('appointment_date', [$startDate, $endDate]); if ($therapistId) { $query->where('user_id', $therapistId); } return $query->orderBy('appointment_date')->get()->toArray(); } /** * Consume a package session for an appointment */ public function consumePackageSession(Appointment $appointment, int $patientPackageId): bool { $patientPackage = \App\Models\PatientPackage::find($patientPackageId); if (!$patientPackage || $patientPackage->patient_id !== $appointment->patient_id) { return false; } return $patientPackage->consumeSession(); } }