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()); } }