*/ class TreatmentCaseRepository extends ServiceEntityRepository { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, TreatmentCase::class); } public function findByUuid(string $uuid): ?TreatmentCase { return $this->findOneBy(['uuid' => $uuid]); } /** * پروندهٔ بازِ همین بیمار برای همین سرویس. * * نقطهٔ تصمیمِ «پروندهٔ دوم نساز»: بیماری که وسط دورهٔ لیزرش نوبت دیگری از همان * سرویس می‌گیرد، باید جلسهٔ همان دوره را بگیرد، نه یک دورهٔ موازی. */ public function findOpenFor(PatientRecord $record, ServiceItem $service): ?TreatmentCase { return $this->findOneBy([ 'patientRecord' => $record, 'serviceItem' => $service, 'status' => TreatmentCase::STATUS_ACTIVE, ]); } /** @return TreatmentCase[] */ /** * @param ?string $q جستجو روی نام بیمار، موبایل، کد ملی، شمارهٔ پرونده و نام سرویس. * منشی همان کلیدی را می‌زند که در فرم نوبت می‌زند، پس هر چهار * شناسهٔ بیمار باید بگیرد نه فقط نام. * @param ?string $recordUuid فقط دوره‌های همین بیمار — نمای «پروندهٔ بیمار». */ public function findForTenant( string $entityType, int $entityId, ?string $status = null, ?string $q = null, ?int $openedFrom = null, ?int $openedTo = null, ?string $recordUuid = null, ): array { $qb = $this->createQueryBuilder('c') ->where('c.entityType = :type') ->andWhere('c.entityId = :id') ->setParameter('type', $entityType) ->setParameter('id', $entityId) ->orderBy('c.openedAt', 'DESC'); $hasSearch = $q !== null && $q !== ''; // `patientRecord` را یک بار join می‌کنیم؛ هم فیلتر بیمار و هم جستجو لازمش // دارند و دو join با یک alias خطای DQL است. if ($hasSearch || ($recordUuid !== null && $recordUuid !== '')) { $qb->join('c.patientRecord', 'pr'); } if ($recordUuid !== null && $recordUuid !== '') { $qb->andWhere('pr.uuid = :record')->setParameter('record', $recordUuid); } if ($status !== null) { $qb->andWhere('c.status = :status')->setParameter('status', $status); } // بازه روی تاریخِ باز شدن پرونده است، نه سررسید جلسه: سؤالِ این فهرست // «چه پرونده‌هایی در این بازه شروع شدند» است. if ($openedFrom !== null) { $qb->andWhere('c.openedAt >= :from')->setParameter('from', $openedFrom); } if ($openedTo !== null) { $qb->andWhere('c.openedAt <= :to')->setParameter('to', $openedTo); } if ($hasSearch) { /** * پرسنل از دو راه می‌آید: کسی که به پرونده اختصاص یافته، و کسی که واقعاً * جلسه‌ای از آن را انجام داده. مدیر که نام اپراتور را می‌زند هر دو را * می‌خواهد — «کارهای این نفر» شامل کارِ انجام‌شده هم هست. */ $assignedStaff = <<<'DQL' EXISTS ( SELECT 1 FROM App\Treatment\Entity\TreatmentCaseStaff cs JOIN cs.staff cst WHERE cs.treatmentCase = c AND cst.fullName LIKE :q ) DQL; $performingStaff = <<<'DQL' EXISTS ( SELECT 1 FROM App\Treatment\Entity\TreatmentSession ts JOIN ts.performedBy tsp WHERE ts.treatmentCase = c AND tsp.fullName LIKE :q ) DQL; $qb->join('pr.user', 'u') ->join('c.serviceItem', 'si') ->andWhere( 'u.realName LIKE :q OR u.mobileNumber LIKE :q OR u.nationalCode LIKE :q' . ' OR pr.recordNumber LIKE :q OR si.name LIKE :q' . ' OR ' . $assignedStaff . ' OR ' . $performingStaff, ) ->setParameter('q', '%' . $q . '%'); } return $qb->getQuery()->getResult(); } }