106 lines
3.3 KiB
PHP
106 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Appointment;
|
|
use App\Models\Patient;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
|
|
class AppointmentService
|
|
{
|
|
/**
|
|
* Check for conflicting appointments for a therapist
|
|
*/
|
|
public function hasConflict(int $therapistId, Carbon $startTime, int $duration, ?int $excludeId = null): bool
|
|
{
|
|
$endTime = $startTime->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();
|
|
}
|
|
}
|