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' => '15:00', 'end_time' => '19:00', 'duration_per_patient' => 20, 'location_id' => 1, ]]], ]); $schedule->setMeta(['booking_mode' => WeeklySchedule::MODE_SERVICE, 'buffer_minutes' => 0]); $this->em->persist($schedule); $this->em->flush(); return [$owner, $doctor, $date]; } private function service(Doctor $doctor, string $name, int $minutes): ServiceItem { $section = new ServiceSection('doctor', $doctor->getId(), 'بخش ' . $name); $this->em->persist($section); $item = new ServiceItem($section, $name, 0); $item->setDurationMinutes($minutes)->setBookable(true); $this->em->persist($item); $this->em->flush(); return $item; } public function testBookingServicesReturnsSection(): void { [$owner, $doctor] = $this->serviceDoctor(); $svc = $this->service($doctor, 'بوتاکس', 30); $res = $this->authJson('GET', '/api/v1/appointment-booking-services/' . $doctor->getUuid(), $owner); self::assertSame(200, $this->responseCode()); $row = $res['data']['services'][0]; self::assertSame($svc->getUuid(), $row['uuid']); self::assertArrayHasKey('service_section', $row); self::assertSame($svc->getSection()->getUuid(), $row['service_section']['uuid']); self::assertSame($svc->getSection()->getName(), $row['service_section']['name']); } public function testServiceSlotsHonorsDurationOverride(): void { [$owner, $doctor, $date] = $this->serviceDoctor(); $svc = $this->service($doctor, 'فیلر', 30); // بدون override: مدت کل = ۳۰ $base = $this->authJson('GET', sprintf( '/api/v1/appointment-service-slots?doctor_uuid=%s&date=%s&service_item_uuids[]=%s', $doctor->getUuid(), $date, $svc->getUuid() ), $owner); self::assertSame(30, $base['data']['total_duration_minutes']); // با override = ۹۰ $over = $this->authJson('GET', sprintf( '/api/v1/appointment-service-slots?doctor_uuid=%s&date=%s&service_item_uuids[]=%s&durations[%s]=90', $doctor->getUuid(), $date, $svc->getUuid(), $svc->getUuid() ), $owner); self::assertSame(90, $over['data']['total_duration_minutes']); // پیش‌فرضِ سرویس در DB تغییر نکرده $this->em->clear(); $reloaded = $this->em->getRepository(ServiceItem::class)->findOneBy(['uuid' => $svc->getUuid()]); self::assertSame(30, $reloaded->getDurationMinutes()); } public function testCreateAppliesDurationOverrideToSlotEnd(): void { [$owner, $doctor] = $this->serviceDoctor(); $svc = $this->service($doctor, 'لیزر', 30); $start = time() + 86_400 + random_int(0, 3_600) * 100; $nc = '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT); $res = $this->authJson('POST', '/api/v1/my/appointment', $owner, [ 'doctor_uuid' => $doctor->getUuid(), 'slot_start' => $start, 'slot_end' => $start + 60, // نادیده گرفته می‌شود (بازمحاسبه) 'patient_mobile' => '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT), 'patient_name' => 'بیمار تست', 'patient_national_code' => $nc, 'service_item_uuids' => [$svc->getUuid()], 'duration_from_services' => true, 'service_durations' => [$svc->getUuid() => 75], ]); self::assertSame(201, $this->responseCode()); $this->em->clear(); $appt = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $res['data']['uuid']]); // slot_end = start + 75 دقیقه (override)، نه ۳۰ پیش‌فرض self::assertSame($start + 75 * 60, $appt->getSlotEnd()); // پیش‌فرضِ سرویس دست‌نخورده $reloaded = $this->em->getRepository(ServiceItem::class)->findOneBy(['uuid' => $svc->getUuid()]); self::assertSame(30, $reloaded->getDurationMinutes()); } }