getId(), 'بخش تست'); $this->em->persist($section); $item = new ServiceItem($section, 'ویزیت', 500_000); $item->setBookable(true); $item->setDurationMinutes(20); $this->em->persist($item); $this->em->flush(); } /** @return array{0: Doctor, 1: DoctorAddress} */ private function makeDoctorWithSlotSchedule(): array { $user = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']); $doctor = new Doctor($user, 'دکتر تست'); $doctor->setMobileNumber($user->getMobileNumber()); $this->em->persist($doctor); $this->em->flush(); $address = DoctorAddress::forDoctor($doctor); $this->em->persist($address); $this->em->flush(); $this->bookableServiceFor($doctor); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $user, [ 'doctor_uuid' => $doctor->getUuid(), 'schedule' => [['day' => 'saturday', 'sessions' => [ ['active' => true, 'location_id' => $address->getId(), 'start' => '09:00', 'end' => '12:00'], ]]], 'meta' => ['booking_mode' => 'slot'], ]); self::assertSame(201, $this->responseCode()); return [$doctor, $address]; } private function switchToService(Doctor $doctor, \App\Auth\Entity\User $actor, bool $force = false): array { return $this->authJson('PATCH', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $actor, [ 'meta' => ['booking_mode' => 'service'], 'force_mode_change' => $force, ]); } public function testDoctorStillCannotChangeTheirOwnLockedMode(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $this->switchToService($doctor, $doctor->getUser()); self::assertSame(422, $this->responseCode()); } public function testAdminChangesModeWhenNothingIsBooked(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $body = $this->switchToService($doctor, $admin); self::assertSame(200, $this->responseCode()); self::assertSame('service', $body['data']['data']['meta']['booking_mode']); } public function testUpcomingAppointmentsBlockTheFirstAttemptAndAreReported(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $start = time() + 3 * 86400; $this->em->persist($this->newAppointment($doctor, $this->createUser(), $start, $start + 1800)); $this->em->flush(); $body = $this->switchToService($doctor, $admin); self::assertSame(422, $this->responseCode()); self::assertStringContainsString('نوبت فعال', $body['errors'][0]['message'] ?? ''); } public function testAdminCanForceThroughUpcomingAppointments(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $start = time() + 3 * 86400; $this->em->persist($this->newAppointment($doctor, $this->createUser(), $start, $start + 1800)); $this->em->flush(); $body = $this->switchToService($doctor, $admin, force: true); self::assertSame(200, $this->responseCode()); self::assertSame('service', $body['data']['data']['meta']['booking_mode']); } public function testResponseTellsThePanelWhoHoldsTheKey(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $asDoctor = $this->authJson('GET', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $doctor->getUser()); self::assertTrue($asDoctor['data']['data']['booking_mode_locked']); self::assertFalse($asDoctor['data']['data']['booking_mode_changeable']); $asAdmin = $this->authJson('GET', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $admin); self::assertTrue($asAdmin['data']['data']['booking_mode_changeable']); } public function testForceFlagFromANonAdminChangesNothing(): void { [$doctor] = $this->makeDoctorWithSlotSchedule(); $this->switchToService($doctor, $doctor->getUser(), force: true); self::assertSame(422, $this->responseCode(), 'پرچم تأیید برای غیر ادمین بی‌اثر است'); } }