complete story 3.3 with qa tests
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\ConsultationStatus;
|
||||
use App\Models\BlockedTime;
|
||||
use App\Models\Consultation;
|
||||
use App\Models\WorkingHour;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class AvailabilityService
|
||||
{
|
||||
/**
|
||||
* Get availability status for all days in a month.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getMonthAvailability(int $year, int $month): array
|
||||
{
|
||||
$startOfMonth = Carbon::create($year, $month, 1)->startOfMonth();
|
||||
$endOfMonth = $startOfMonth->copy()->endOfMonth();
|
||||
|
||||
$availability = [];
|
||||
$current = $startOfMonth->copy();
|
||||
|
||||
while ($current->lte($endOfMonth)) {
|
||||
$availability[$current->format('Y-m-d')] = $this->getDateStatus($current);
|
||||
$current->addDay();
|
||||
}
|
||||
|
||||
return $availability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the availability status for a specific date.
|
||||
*/
|
||||
public function getDateStatus(Carbon $date): string
|
||||
{
|
||||
// Past date
|
||||
if ($date->lt(today())) {
|
||||
return 'past';
|
||||
}
|
||||
|
||||
// Check if fully blocked
|
||||
if ($this->isDateFullyBlocked($date)) {
|
||||
return 'blocked';
|
||||
}
|
||||
|
||||
// Check working hours
|
||||
$workingHour = WorkingHour::query()
|
||||
->where('day_of_week', $date->dayOfWeek)
|
||||
->where('is_active', true)
|
||||
->first();
|
||||
|
||||
if (! $workingHour) {
|
||||
return 'closed';
|
||||
}
|
||||
|
||||
// Get available slots
|
||||
$availableSlots = $this->getAvailableSlots($date);
|
||||
|
||||
if (empty($availableSlots)) {
|
||||
return 'full';
|
||||
}
|
||||
|
||||
$totalSlots = count($workingHour->getSlots(60));
|
||||
if (count($availableSlots) < $totalSlots) {
|
||||
return 'partial';
|
||||
}
|
||||
|
||||
return 'available';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available time slots for a specific date.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getAvailableSlots(Carbon $date): array
|
||||
{
|
||||
$workingHour = WorkingHour::query()
|
||||
->where('day_of_week', $date->dayOfWeek)
|
||||
->where('is_active', true)
|
||||
->first();
|
||||
|
||||
if (! $workingHour) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// All possible slots
|
||||
$allSlots = $workingHour->getSlots(60);
|
||||
|
||||
// Booked slots (pending and approved consultations block the slot)
|
||||
$bookedSlots = Consultation::query()
|
||||
->whereDate('booking_date', $date)
|
||||
->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved])
|
||||
->pluck('booking_time')
|
||||
->map(fn ($t) => Carbon::parse($t)->format('H:i'))
|
||||
->toArray();
|
||||
|
||||
// Blocked slots
|
||||
$blockedSlots = $this->getBlockedSlots($date);
|
||||
|
||||
return array_values(array_diff($allSlots, $bookedSlots, $blockedSlots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blocked time slots for a specific date.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getBlockedSlots(Carbon $date): array
|
||||
{
|
||||
$blockedTimes = BlockedTime::query()
|
||||
->whereDate('block_date', $date)
|
||||
->get();
|
||||
|
||||
$blockedSlots = [];
|
||||
|
||||
foreach ($blockedTimes as $blocked) {
|
||||
if ($blocked->isAllDay()) {
|
||||
// Block all slots for the day
|
||||
$workingHour = WorkingHour::query()
|
||||
->where('day_of_week', $date->dayOfWeek)
|
||||
->first();
|
||||
|
||||
return $workingHour ? $workingHour->getSlots(60) : [];
|
||||
}
|
||||
|
||||
// Calculate blocked slots from time range
|
||||
$start = Carbon::parse($blocked->start_time);
|
||||
$end = Carbon::parse($blocked->end_time);
|
||||
$current = $start->copy();
|
||||
|
||||
while ($current->lt($end)) {
|
||||
$blockedSlots[] = $current->format('H:i');
|
||||
$current->addMinutes(60);
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($blockedSlots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a date is fully blocked (all-day block).
|
||||
*/
|
||||
private function isDateFullyBlocked(Carbon $date): bool
|
||||
{
|
||||
return BlockedTime::query()
|
||||
->whereDate('block_date', $date)
|
||||
->whereNull('start_time')
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user