- Introduced clinic_id to weekly_schedules, date_overrides, and holidays to differentiate between personal and clinic schedules. - Updated unique constraints and indexes to accommodate the new clinic context. feat(command): create AssignScheduleClinicCommand to move schedules - Added a command to move a doctor's personal weekly schedule into a clinic context. - Implemented checks to ensure sessions align with the target clinic. feat(context): implement EntityContext and EntityContextResolver - Created EntityContext to represent the effective working environment of a request (doctor or clinic). - Developed EntityContextResolver to determine the execution context based on user roles and active contexts. test: add ServiceModeContextTest for appointment scheduling - Implemented tests to ensure service booking respects clinic and personal contexts. - Verified that financial data is omitted in clinic contexts in InvitedDoctorDashboardScopeTest.
131 lines
5.2 KiB
PHP
131 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Appointment;
|
|
|
|
use App\ClinicService\Entity\ServiceItem;
|
|
use App\ClinicService\Entity\ServiceSection;
|
|
use App\Clinic\Entity\Clinic;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Doctor\Entity\DoctorAddress;
|
|
use App\Tests\ApiTestCase;
|
|
|
|
/**
|
|
* نوبتدهی سرویسی، سرویسهای همان محیط را میخواهد. باگ اصلی این بود که شمارش
|
|
* همیشه با entity_type='doctor' انجام میشد، پس کلینیکی که سرویس bookable داشت هم
|
|
* خطای «حداقل یک سرویس لازم است» میگرفت.
|
|
*/
|
|
class ServiceModeContextTest extends ApiTestCase
|
|
{
|
|
private function makeDoctorInClinic(): array
|
|
{
|
|
$doctorUser = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
|
|
$doctor = new Doctor($doctorUser, 'دکتر تست');
|
|
$doctor->setMobileNumber($doctorUser->getMobileNumber());
|
|
$this->em->persist($doctor);
|
|
|
|
$ownerUser = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']);
|
|
$clinic = new Clinic($ownerUser);
|
|
$clinic->setName('کلینیک تست');
|
|
$clinic->getDoctors()->add($doctor);
|
|
$this->em->persist($clinic);
|
|
$this->em->flush();
|
|
|
|
return [$doctor, $clinic, $ownerUser];
|
|
}
|
|
|
|
private function bookableServiceFor(string $entityType, int $entityId): void
|
|
{
|
|
$section = new ServiceSection($entityType, $entityId, 'بخش تست');
|
|
$this->em->persist($section);
|
|
|
|
$item = new ServiceItem($section, 'ویزیت', 500_000);
|
|
$item->setBookable(true);
|
|
$item->setDurationMinutes(20);
|
|
$this->em->persist($item);
|
|
$this->em->flush();
|
|
}
|
|
|
|
private function servicePayload(Doctor $doctor, int $locationId, ?Clinic $clinic): array
|
|
{
|
|
return array_filter([
|
|
'doctor_uuid' => $doctor->getUuid(),
|
|
'clinic_uuid' => $clinic?->getUuid(),
|
|
'schedule' => [
|
|
['day' => 'saturday', 'sessions' => [
|
|
['active' => true, 'location_id' => $locationId, 'start' => '09:00', 'end' => '12:00'],
|
|
]],
|
|
],
|
|
'meta' => ['booking_mode' => 'service'],
|
|
], fn($v) => $v !== null);
|
|
}
|
|
|
|
public function testClinicServiceModeAcceptsClinicOwnedService(): void
|
|
{
|
|
[$doctor, $clinic, $owner] = $this->makeDoctorInClinic();
|
|
$this->bookableServiceFor('clinic', $clinic->getId());
|
|
|
|
$address = DoctorAddress::forClinic($clinic->getId());
|
|
$this->em->persist($address);
|
|
$this->em->flush();
|
|
|
|
$this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->servicePayload($doctor, $address->getId(), $clinic));
|
|
|
|
self::assertSame(201, $this->responseCode());
|
|
}
|
|
|
|
public function testPersonalServiceModeIgnoresClinicServices(): void
|
|
{
|
|
[$doctor, $clinic] = $this->makeDoctorInClinic();
|
|
$this->bookableServiceFor('clinic', $clinic->getId());
|
|
|
|
$address = DoctorAddress::forDoctor($doctor);
|
|
$this->em->persist($address);
|
|
$this->em->flush();
|
|
|
|
$body = $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->servicePayload($doctor, $address->getId(), null));
|
|
|
|
self::assertSame(422, $this->responseCode(), 'سرویس کلینیک نباید مطب شخصی را راضی کند');
|
|
self::assertSame('booking_mode', $body['errors'][0]['field'] ?? null);
|
|
}
|
|
|
|
public function testPersonalServiceModeAcceptsOwnService(): void
|
|
{
|
|
[$doctor] = $this->makeDoctorInClinic();
|
|
$this->bookableServiceFor('doctor', $doctor->getId());
|
|
|
|
$address = DoctorAddress::forDoctor($doctor);
|
|
$this->em->persist($address);
|
|
$this->em->flush();
|
|
|
|
$this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->servicePayload($doctor, $address->getId(), null));
|
|
|
|
self::assertSame(201, $this->responseCode());
|
|
}
|
|
|
|
public function testBookingModeLocksPerContextNotGlobally(): void
|
|
{
|
|
[$doctor, $clinic, $owner] = $this->makeDoctorInClinic();
|
|
$this->bookableServiceFor('clinic', $clinic->getId());
|
|
|
|
$personal = DoctorAddress::forDoctor($doctor);
|
|
$inClinic = DoctorAddress::forClinic($clinic->getId());
|
|
$this->em->persist($personal);
|
|
$this->em->persist($inClinic);
|
|
$this->em->flush();
|
|
|
|
// مطب شخصی: اسلاتی
|
|
$this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), [
|
|
'doctor_uuid' => $doctor->getUuid(),
|
|
'schedule' => [['day' => 'saturday', 'sessions' => [
|
|
['active' => true, 'location_id' => $personal->getId(), 'start' => '09:00', 'end' => '12:00'],
|
|
]]],
|
|
'meta' => ['booking_mode' => 'slot'],
|
|
]);
|
|
self::assertSame(201, $this->responseCode());
|
|
|
|
// همان پزشک در کلینیک: سرویسی — قفلِ محیط دیگر نباید مانع شود
|
|
$this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->servicePayload($doctor, $inClinic->getId(), $clinic));
|
|
self::assertSame(201, $this->responseCode());
|
|
}
|
|
}
|