- Introduced management mode for appointment slots, allowing doctors, admins, and clinic managers to view and book slots regardless of the online booking status. - Updated SlotCalculatorService to accept a management context parameter, bypassing online booking restrictions. - Modified appointment-related endpoints to handle management context and ensure proper authorization checks. - Added tests to verify that management users can access slots even when online booking is disabled, while public users are still restricted. - Improved documentation for API endpoints to reflect new management parameters and behaviors.
124 lines
5.1 KiB
PHP
124 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Appointment;
|
|
|
|
use App\Appointment\Entity\WeeklySchedule;
|
|
use App\Appointment\Service\SlotCalculatorService;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Tests\ApiTestCase;
|
|
|
|
/**
|
|
* خاموشبودنِ نوبتدهی آنلاین فقط رزرو عمومی از سایت را متوقف میکند. کانتکست مدیریت
|
|
* (پزشک/منشی/ادمین، $forManagement=true) باید مستقلاً اسلات ببیند و بتواند نوبت ثبت کند.
|
|
* تاریخِ گذشته اما در هر دو حالت رد میشود.
|
|
*/
|
|
class OnlineBookingManagementTest extends ApiTestCase
|
|
{
|
|
/** @return array{0: Doctor, 1: string} */
|
|
private function makeDoctorWithBookingDisabled(): array
|
|
{
|
|
$owner = $this->createUser(['ROLE_DOCTOR']);
|
|
$doctor = new Doctor($owner, 'دکتر خاموش');
|
|
$this->em->persist($doctor);
|
|
|
|
$date = date('Y-m-d', strtotime('tomorrow'));
|
|
$dayKey = (string)(((int) date('w', strtotime($date)) + 1) % 7);
|
|
|
|
$schedule = new WeeklySchedule($doctor, [
|
|
$dayKey => ['sessions' => [[
|
|
'active' => true,
|
|
'start_time' => '10:00',
|
|
'end_time' => '12:00',
|
|
'duration_per_patient' => 30,
|
|
'location_id' => 1,
|
|
]]],
|
|
]);
|
|
$schedule->setMeta(['online_booking_enabled' => false]);
|
|
$this->em->persist($schedule);
|
|
$this->em->flush();
|
|
|
|
return [$doctor, $date];
|
|
}
|
|
|
|
public function testPublicSeesNoSlotsWhenBookingDisabled(): void
|
|
{
|
|
[$doctor, $date] = $this->makeDoctorWithBookingDisabled();
|
|
$calc = static::getContainer()->get(SlotCalculatorService::class);
|
|
|
|
$this->assertSame([], $calc->getAllSlotsWithAvailability($doctor, $date));
|
|
$this->assertSame([], $calc->getAvailableSlots($doctor, $date));
|
|
$this->assertFalse($calc->hasAnyAvailability($doctor, $date));
|
|
$this->assertSame(SlotCalculatorService::EMPTY_OUTSIDE_WINDOW, $calc->explainEmptyDay($doctor, $date));
|
|
}
|
|
|
|
public function testManagementSeesSlotsWhenBookingDisabled(): void
|
|
{
|
|
[$doctor, $date] = $this->makeDoctorWithBookingDisabled();
|
|
$calc = static::getContainer()->get(SlotCalculatorService::class);
|
|
|
|
$sessions = $calc->getAllSlotsWithAvailability($doctor, $date, null, true);
|
|
$this->assertNotSame([], $sessions);
|
|
$this->assertNotSame([], $calc->getAvailableSlots($doctor, $date, null, true));
|
|
$this->assertTrue($calc->hasAnyAvailability($doctor, $date, null, true));
|
|
$this->assertNull($calc->explainEmptyDay($doctor, $date, null, true));
|
|
|
|
// location هم باید تعیین شود تا نوبتِ دستی بدون آدرس ثبت نشود.
|
|
$firstStart = (int) $sessions[0]['slots'][0]['start'];
|
|
$this->assertSame(1, $calc->resolveSlotLocationId($doctor, $firstStart, null, true));
|
|
$this->assertNull($calc->resolveSlotLocationId($doctor, $firstStart, null, false));
|
|
}
|
|
|
|
public function testPastDateRejectedEvenForManagement(): void
|
|
{
|
|
[$doctor] = $this->makeDoctorWithBookingDisabled();
|
|
$calc = static::getContainer()->get(SlotCalculatorService::class);
|
|
|
|
$yesterday = date('Y-m-d', strtotime('yesterday'));
|
|
$this->assertSame([], $calc->getAllSlotsWithAvailability($doctor, $yesterday, null, true));
|
|
}
|
|
|
|
private function slotsUri(Doctor $doctor, string $date, bool $management): string
|
|
{
|
|
return sprintf(
|
|
'/api/v1/appointment-slots?doctor_uuid=%s&date=%s%s',
|
|
$doctor->getUuid(),
|
|
$date,
|
|
$management ? '&management=1' : '',
|
|
);
|
|
}
|
|
|
|
public function testEndpointAnonymousGetsNoSlots(): void
|
|
{
|
|
[$doctor, $date] = $this->makeDoctorWithBookingDisabled();
|
|
|
|
$this->client->request('GET', $this->slotsUri($doctor, $date, false));
|
|
$body = json_decode($this->client->getResponse()->getContent(), true);
|
|
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertSame([], $body['data']['sessions']);
|
|
self::assertSame(SlotCalculatorService::EMPTY_OUTSIDE_WINDOW, $body['data']['empty_reason']);
|
|
}
|
|
|
|
public function testEndpointOwnerDoctorWithManagementGetsSlots(): void
|
|
{
|
|
[$doctor, $date] = $this->makeDoctorWithBookingDisabled();
|
|
|
|
$body = $this->authJson('GET', $this->slotsUri($doctor, $date, true), $doctor->getUser());
|
|
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertNotSame([], $body['data']['sessions']);
|
|
}
|
|
|
|
public function testEndpointManagementFlagIgnoredForUnauthorizedUser(): void
|
|
{
|
|
[$doctor, $date] = $this->makeDoctorWithBookingDisabled();
|
|
$stranger = $this->createUser(['ROLE_USER']);
|
|
|
|
// management=1 بدون دسترسی نباید توگل را دور بزند (fail-safe عمومی).
|
|
$body = $this->authJson('GET', $this->slotsUri($doctor, $date, true), $stranger);
|
|
|
|
self::assertSame(200, $this->responseCode());
|
|
self::assertSame([], $body['data']['sessions']);
|
|
}
|
|
}
|