feat(appointment): public month-availability endpoint
Add GET /api/v1/appointment-settings/month-availability/{doctorUuid}
?year=&month= (Gregorian) returning disabled_dates / enabled_dates for
the month plus the doctor's online_booking flag and window. Lives in
AppointmentController (per-method guards) so it is genuinely public —
the class-level IsGranted on AppointmentSettingsController would have
forced auth. Whitelisted in security.yaml (firewall + access_control).
Uses SlotCalculator::hasAnyAvailability per day, so holidays, closed
overrides, non-working days and out-of-window dates all come back
disabled.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
namespace App\Appointment\Controller;
|
||||
|
||||
use App\Appointment\Entity\Appointment;
|
||||
use App\Appointment\Entity\WeeklySchedule;
|
||||
use App\Appointment\Repository\AppointmentRepository;
|
||||
use App\Appointment\Repository\WeeklyScheduleRepository;
|
||||
use App\Appointment\Service\SlotCalculatorService;
|
||||
use App\Auth\Entity\User;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
@@ -26,6 +28,7 @@ class AppointmentController extends BaseController
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly SlotCalculatorService $slotCalculator,
|
||||
private readonly PatientService $patientService,
|
||||
private readonly WeeklyScheduleRepository $scheduleRepo,
|
||||
) {}
|
||||
|
||||
// ── Public: available slots ───────────────────────────────────────────────
|
||||
@@ -126,6 +129,49 @@ class AppointmentController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/month-availability/{doctorUuid}', methods: ['GET'])]
|
||||
public function monthAvailability(string $doctorUuid, Request $request): JsonResponse
|
||||
{
|
||||
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
$year = (int) $request->query->get('year');
|
||||
$month = (int) $request->query->get('month');
|
||||
if ($year < 1970 || $month < 1 || $month > 12) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'سال یا ماه نامعتبر است', 422, 'month');
|
||||
}
|
||||
|
||||
$daysInMonth = (int) date('t', (int) strtotime(sprintf('%04d-%02d-01', $year, $month)));
|
||||
|
||||
$disabled = [];
|
||||
$enabled = [];
|
||||
for ($day = 1; $day <= $daysInMonth; $day++) {
|
||||
$date = sprintf('%04d-%02d-%02d', $year, $month, $day);
|
||||
if ($this->slotCalculator->hasAnyAvailability($doctor, $date)) {
|
||||
$enabled[] = $date;
|
||||
} else {
|
||||
$disabled[] = $date;
|
||||
}
|
||||
}
|
||||
|
||||
$schedule = $this->scheduleRepo->findByDoctor($doctor);
|
||||
$meta = $schedule ? $schedule->getMeta() : WeeklySchedule::DEFAULT_META;
|
||||
|
||||
return $this->success([
|
||||
'year' => $year,
|
||||
'month' => $month,
|
||||
'disabled_dates' => $disabled,
|
||||
'enabled_dates' => $enabled,
|
||||
'online_booking_enabled' => (bool) $meta['online_booking_enabled'],
|
||||
'booking_window' => [
|
||||
'value' => (int) $meta['booking_window_value'],
|
||||
'unit' => $meta['booking_window_unit'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Authenticated: book / manage ─────────────────────────────────────────
|
||||
|
||||
#[OA\Post(
|
||||
|
||||
Reference in New Issue
Block a user