opener = new TreatmentCaseOpener( new \App\ClinicService\Service\CategoryClosureResolver( $this->em->getRepository(CatalogCategoryInclude::class), ), $this->em->getRepository(CatalogCategory::class), $this->em, ); $user = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $this->clinic = new Clinic($user); $this->clinic->setName('کلینیک پروندهٔ درمان'); $this->em->persist($this->clinic); $this->em->flush(); $this->section = new ServiceSection('clinic', (int) $this->clinic->getId(), 'لیزر'); $this->em->persist($this->section); $this->em->flush(); } private function category(string $name): CatalogCategory { $category = new CatalogCategory('clinic', (int) $this->clinic->getId(), $name); $this->em->persist($category); $this->em->flush(); return $category; } private function includes(CatalogCategory $parent, CatalogCategory $child): void { $this->em->persist(new CatalogCategoryInclude($parent, $child)); $this->em->flush(); } private function service(string $name, ?CatalogCategory $category): ServiceItem { $item = new ServiceItem($this->section, $name, 10_000_000); if ($category !== null) { $item->setCatalogCategory($category); } $this->em->persist($item); $this->em->flush(); return $item; } private function protocolFor(ServiceItem $service, array $offsets): TreatmentProtocol { $staff = new ClinicStaff('clinic', (int) $this->clinic->getId(), 'اپراتور'); $this->em->persist($staff); $protocol = new TreatmentProtocol($service); $this->em->persist($protocol); $steps = []; foreach ($offsets as $index => $offset) { $steps[] = new TreatmentProtocolStep($protocol, $index + 1, $offset); } $protocol->replaceSteps($steps); $protocol->replaceAllowedStaff([new TreatmentProtocolStaff($protocol, $staff)]); $this->em->flush(); return $protocol; } private function record(): PatientRecord { $patient = $this->createUser(); $record = new PatientRecord('clinic', (int) $this->clinic->getId(), $patient, 'clinic', (int) $this->clinic->getId()); $this->em->persist($record); $this->em->flush(); return $record; } private function open(ServiceItem $service, TreatmentProtocol $protocol): TreatmentCase { return $this->opener->open( 'clinic', (int) $this->clinic->getId(), $this->record(), $service, $protocol, ); } /** «توتال» شامل بیکینی و پا و دست است؛ درمان روی آن سه انجام می‌شود نه روی خودِ توتال. */ public function testAreasAreTheLeavesOfTheServiceCategory(): void { $total = $this->category('توتال'); $bikini = $this->category('بیکینی'); $leg = $this->category('پا'); $hand = $this->category('دست'); $this->includes($total, $bikini); $this->includes($total, $leg); $this->includes($total, $hand); $service = $this->service('لیزر توتال', $total); $protocol = $this->protocolFor($service, [0, 30]); $case = $this->open($service, $protocol); $names = array_map(static fn ($a) => $a->getName(), $case->getAreas()->toArray()); sort($names); self::assertSame(['بیکینی', 'دست', 'پا'], $names); } /** دستهٔ میانی گروه‌بندی است، نه ناحیهٔ درمان. */ public function testIntermediateCategoriesAreNotAreas(): void { $body = $this->category('تمام بدن'); $lower = $this->category('نیم‌تنهٔ پایین'); $leg = $this->category('پا'); $this->includes($body, $lower); $this->includes($lower, $leg); $service = $this->service('لیزر تمام بدن', $body); $protocol = $this->protocolFor($service, [0, 30]); $case = $this->open($service, $protocol); $names = array_map(static fn ($a) => $a->getName(), $case->getAreas()->toArray()); self::assertSame(['پا'], $names); } /** «لیزر دست» یک سرویس واقعی است و باید یک ناحیه داشته باشد، نه صفر. */ public function testCategoryWithoutChildrenIsItsOwnArea(): void { $hand = $this->category('دست'); $service = $this->service('لیزر دست', $hand); $protocol = $this->protocolFor($service, [0, 30]); $case = $this->open($service, $protocol); $names = array_map(static fn ($a) => $a->getName(), $case->getAreas()->toArray()); self::assertSame(['دست'], $names); } public function testServiceWithoutCategoryIsRejected(): void { $service = $this->service('لیزر بی‌دسته', null); $protocol = $this->protocolFor($service, [0, 30]); $this->expectException(AppException::class); $this->open($service, $protocol); } /** همهٔ جلسات از ابتدا ساخته می‌شوند تا «جلسهٔ ۵ از ۸» جایی برای زندگی داشته باشد. */ public function testEverySessionIsCreatedUpFrontAndNoneIsBooked(): void { $hand = $this->category('دست'); $service = $this->service('لیزر دست', $hand); $protocol = $this->protocolFor($service, [0, 15, 30, 30]); $case = $this->open($service, $protocol); self::assertSame(4, $case->getTotalSessions()); self::assertCount(4, $case->getSessions()); self::assertSame([1, 2, 3, 4], array_map( static fn (TreatmentSession $s): int => $s->getSessionNumber(), $case->getSessions()->toArray(), )); foreach ($case->getSessions() as $session) { self::assertSame(TreatmentSession::STATUS_PLANNED, $session->getStatus()); self::assertNull($session->getAppointment()); } } /** تعداد جلسات کپی می‌شود: تغییر فردای پروتکل نباید «۳ از ۸» را به «۳ از ۶» تبدیل کند. */ public function testTotalSessionsIsSnapshotted(): void { $hand = $this->category('دست'); $service = $this->service('لیزر دست', $hand); $protocol = $this->protocolFor($service, [0, 30, 30, 30]); $case = $this->open($service, $protocol); self::assertSame(4, $case->getTotalSessions()); // دو flush، مثل TreatmentProtocolWriter: در یک flush، درج پیش از حذف می‌رود و // به قید یکتای (protocol, step_number) می‌خورد. $protocol->replaceSteps([]); $this->em->flush(); $protocol->replaceSteps([ new TreatmentProtocolStep($protocol, 1, 0), new TreatmentProtocolStep($protocol, 2, 30), ]); $this->em->flush(); self::assertSame(4, $case->getTotalSessions()); } /** اسم ناحیه هم snapshot است — سابقهٔ درمان با ویرایش تنظیمات بازنویسی نمی‌شود. */ public function testAreaNameIsSnapshotted(): void { $hand = $this->category('دست'); $service = $this->service('لیزر دست', $hand); $protocol = $this->protocolFor($service, [0, 30]); $case = $this->open($service, $protocol); $hand->setName('دست و ساعد'); $this->em->flush(); $names = array_map(static fn ($a) => $a->getName(), $case->getAreas()->toArray()); self::assertSame(['دست'], $names); } public function testCaseCarriesTheProtocolSupervisor(): void { $hand = $this->category('دست'); $service = $this->service('لیزر دست', $hand); $protocol = $this->protocolFor($service, [0, 30]); $doctorUser = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']); $doctor = new \App\Doctor\Entity\Doctor($doctorUser, 'دکتر ناظر'); $doctor->setMobileNumber($doctorUser->getMobileNumber()); $this->em->persist($doctor); $protocol->setSupervisorDoctor($doctor); $this->em->flush(); $case = $this->open($service, $protocol); self::assertSame($doctor->getId(), $case->getSupervisorDoctor()?->getId()); } }