Files
clinicpro/tests/Appointment/OnlineBookingManagementTest.php
T
hamed ed516c81a8 feat: Enhance appointment management by decoupling online booking toggle for admin context
- 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.
2026-07-22 16:43:56 +03:30

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']);
}
}