tenantEntityType(), $address->tenantEntityId(), 'بخش ' . $name); $this->em->persist($section); $this->em->flush(); $item = new ServiceItem($section, $name, 5_000_000); $item->setDurationMinutes(30); $this->em->persist($item); $this->em->flush(); return $item; } private function resource(DoctorAddress $address, ResourceType $type, string $name): ClinicResource { $resource = new ClinicResource($address, $type, $name); $this->em->persist($resource); $this->em->flush(); return $resource; } private function repo(): ClinicResourceRepository { return $this->em->getRepository(ClinicResource::class); } // ── ✅ موفق ────────────────────────────────────────────────────────────── public function testOnlyTheResourceThatOffersTheServiceIsEligible(): void { [, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $service = $this->service($address, 'لیزر CO2'); $offering = $this->resource($address, $type, 'دستگاه شمارهٔ ۱'); $this->resource($address, $type, 'دستگاه شمارهٔ ۲'); // این یکی وصل نیست $this->em->persist(new ResourceServiceOffering($offering, $service)); $this->em->flush(); $eligible = $this->repo()->findEligible($address, $type, [], $service); self::assertCount(1, $eligible); self::assertSame('دستگاه شمارهٔ ۱', $eligible[0]->getName()); } // ── ⚠️ مرزی: سازگاری عقب‌رو ────────────────────────────────────────────── public function testWithoutAnyOfferingTheFilterIsNotAppliedAtAll(): void { [, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $service = $this->service($address, 'سرویس بدون رابطه'); $this->resource($address, $type, 'دستگاه الف'); $this->resource($address, $type, 'دستگاه ب'); // هیچ ردیفی برای این سرویس نیست → همان رفتار قبلی، هر دو کاندیدند. self::assertCount(2, $this->repo()->findEligible($address, $type, [], $service)); } public function testDeactivatingEveryOfferingLeavesNoCandidate(): void { [, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $service = $this->service($address, 'لیزر خاموش'); $first = $this->resource($address, $type, 'دستگاه الف'); $this->resource($address, $type, 'دستگاه ب'); $offering = new ResourceServiceOffering($first, $service); $offering->setActive(false); $this->em->persist($offering); $this->em->flush(); // ردیف هست ولی خاموش: یعنی «کسی این را نمی‌دهد»، نه «فیلتری در کار نیست». self::assertSame([], $this->repo()->findEligible($address, $type, [], $service)); } public function testWithoutAServiceTheOldBehaviourIsUntouched(): void { [, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $service = $this->service($address, 'لیزر'); $only = $this->resource($address, $type, 'دستگاه تنها'); $this->resource($address, $type, 'دستگاه دیگر'); $this->em->persist(new ResourceServiceOffering($only, $service)); $this->em->flush(); // بدون سرویس، هیچ فیلتری اعمال نمی‌شود — مسیرهایی که سرویس ندارند نباید بشکنند. self::assertCount(2, $this->repo()->findEligible($address, $type)); } public function testResourcesCoveringTheServiceCategoryComeFirst(): void { [, $clinic, $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $category = new \App\ClinicService\Entity\CatalogCategory('clinic', (int) $clinic->getId(), 'پا'); $this->em->persist($category); $this->em->flush(); $service = $this->service($address, 'لیزر پا'); $service->setCatalogCategory($category); // نامش عمداً الفبایی دوم است تا معلوم شود ترتیب از دسته می‌آید نه از نام. $plain = $this->resource($address, $type, 'الف بدون دسته'); $covering = $this->resource($address, $type, 'ی با دستهٔ پا'); $covering->getCategories()->add($category); $this->em->flush(); $eligible = $this->repo()->findEligible($address, $type, [], $service); self::assertCount(2, $eligible, 'تقدم است نه فیلتر — منبع بی‌دسته هم کاندید می‌ماند'); self::assertSame('ی با دستهٔ پا', $eligible[0]->getName()); } public function testTheFilterStacksWithTheSkillFilter(): void { [, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $service = $this->service($address, 'لیزر تخصصی'); $skilled = $this->resource($address, $type, 'دستگاه با مهارت'); $plain = $this->resource($address, $type, 'دستگاه بی‌مهارت'); $skill = new \App\Resource\Entity\Skill($address->tenantEntityType(), $address->tenantEntityId(), 'کار با لیزر'); $this->em->persist($skill); $this->em->flush(); $this->em->persist(new \App\Resource\Entity\ResourceSkill($skilled, $skill, 3)); // هر دو سرویس را می‌دهند، ولی فقط یکی مهارتش را دارد. $this->em->persist(new ResourceServiceOffering($skilled, $service)); $this->em->persist(new ResourceServiceOffering($plain, $service)); $this->em->flush(); $eligible = $this->repo()->findEligible($address, $type, [(int) $skill->getId()], $service); self::assertCount(1, $eligible); self::assertSame('دستگاه با مهارت', $eligible[0]->getName()); } }