createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'دکتر تست'); $this->em->persist($doctor); $this->em->flush(); return [$owner, $doctor]; } private function serviceItem(Doctor $doctor, string $name, int $minutes): ServiceItem { $section = new ServiceSection('doctor', $doctor->getId(), 'بخش ' . $name); $this->em->persist($section); $item = new ServiceItem($section, $name, 500_000); $item->setDurationMinutes($minutes)->setBookable(true); $this->em->persist($item); $this->em->flush(); return $item; } private function nationalCode(): string { return '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT); } private function body(Doctor $doctor, int $start, int $end, array $extra): array { return array_merge([ 'doctor_uuid' => $doctor->getUuid(), 'slot_start' => $start, 'slot_end' => $end, 'patient_mobile' => '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT), 'patient_name' => 'بیمار تست', 'patient_national_code' => $this->nationalCode(), ], $extra); } private function reload(string $uuid): Appointment { $this->em->clear(); return $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $uuid]); } public function testSlotModeAttachesMultipleServicesAndKeepsManualEnd(): void { [$owner, $doctor] = $this->doctor(); $a = $this->serviceItem($doctor, 'تزریق ژل', 30); $b = $this->serviceItem($doctor, 'کندلا', 20); $start = time() + 86_400 + random_int(0, 3_600) * 100; $end = $start + 3_600; // ساعت پایانِ دستی: ۶۰ دقیقه $res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->body($doctor, $start, $end, [ 'service_item_uuids' => [$a->getUuid(), $b->getUuid()], ])); self::assertSame(201, $this->responseCode()); $appt = $this->reload($res['data']['uuid']); self::assertCount(2, $appt->getServiceItems()); // ساعت پایان دست‌نخورده (بدون بازمحاسبه از مدت سرویس‌ها) self::assertSame($end, $appt->getSlotEnd()); // سرویسِ اصلی = اولین سرویس self::assertSame($a->getUuid(), $appt->getServiceItem()?->getUuid()); } public function testServiceModeRecomputesEndFromDurations(): void { [$owner, $doctor] = $this->doctor(); $a = $this->serviceItem($doctor, 'تزریق ژل', 30); $b = $this->serviceItem($doctor, 'کندلا', 20); $start = time() + 86_400 + random_int(0, 3_600) * 100; $res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->body($doctor, $start, $start + 60, [ 'service_item_uuids' => [$a->getUuid(), $b->getUuid()], 'duration_from_services' => true, ])); self::assertSame(201, $this->responseCode()); $appt = $this->reload($res['data']['uuid']); self::assertCount(2, $appt->getServiceItems()); // مجموع مدت ۳۰+۲۰=۵۰ دقیقه → slot_end بازمحاسبه‌شده self::assertSame($start + 50 * 60, $appt->getSlotEnd()); } public function testUnknownServiceUuidIsRejected(): void { [$owner, $doctor] = $this->doctor(); $start = time() + 86_400 + random_int(0, 3_600) * 100; $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->body($doctor, $start, $start + 1_800, [ 'service_item_uuids' => ['no-such-uuid'], ])); self::assertSame(422, $this->responseCode()); } }