findOneBy(['uuid' => $uuid]); } public function countActiveByDoctor(Doctor $doctor): int { return (int) $this->createQueryBuilder('s') ->select('COUNT(s.id)') ->where('s.doctor = :doctor') ->andWhere('s.active = true') ->setParameter('doctor', $doctor) ->getQuery() ->getSingleScalarResult(); } /** @return DoctorSecretary[] */ public function findByDoctor(Doctor $doctor): array { return $this->findBy(['doctor' => $doctor], ['createdAt' => 'DESC']); } /** Base query with secretary/doctor/clinic fetch-joined for toArray() (no N+1). */ private function listWithRelations(): \Doctrine\ORM\QueryBuilder { return $this->createQueryBuilder('s') ->addSelect('sec', 'doc', 'cl') ->leftJoin('s.secretary', 'sec') ->leftJoin('s.doctor', 'doc') ->leftJoin('s.clinic', 'cl') ->orderBy('s.createdAt', 'DESC'); } /** منشی ها مطب شخصی یک دکتر (owner_type='doctor') */ public function findByDoctorScope(Doctor $doctor): array { return $this->listWithRelations() ->where('s.doctor = :doctor') ->andWhere('s.entityType = :type') ->setParameter('doctor', $doctor) ->setParameter('type', DoctorSecretary::OWNER_DOCTOR) ->getQuery() ->getResult(); } /** رابطه منشی در scope مطب شخصی */ public function findActiveBySecretaryForDoctor(User $user, Doctor $doctor): ?DoctorSecretary { return $this->findOneBy([ 'secretary' => $user, 'doctor' => $doctor, 'entityType' => DoctorSecretary::OWNER_DOCTOR, 'active' => true, ]); } /** اولین رابطه فعال منشی در یک کلینیک (برای چک دسترسی کلی) */ public function findActiveBySecretaryForClinic(User $user, Clinic $clinic): ?DoctorSecretary { return $this->createQueryBuilder('s') ->where('s.secretary = :user') ->andWhere('s.clinic = :clinic') ->andWhere('s.entityType = :type') ->andWhere('s.active = true') ->setParameter('user', $user) ->setParameter('clinic', $clinic) ->setParameter('type', DoctorSecretary::OWNER_CLINIC) ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); } /** رابطه فعالِ owner=clinic برای یک (منشی، کلینیک، پزشک) مشخص */ public function findActiveClinicRow(User $user, Clinic $clinic, Doctor $doctor): ?DoctorSecretary { return $this->findOneBy([ 'secretary' => $user, 'clinic' => $clinic, 'doctor' => $doctor, 'entityType' => DoctorSecretary::OWNER_CLINIC, 'active' => true, ]); } /** همه دکترهای کلینیک که این منشی به آن‌ها متصل است */ public function findDoctorsBySecretaryInClinic(User $user, Clinic $clinic): array { return $this->getEntityManager()->createQueryBuilder() ->select('d') ->from(Doctor::class, 'd') ->join(DoctorSecretary::class, 's', 'WITH', 's.doctor = d') ->where('s.secretary = :user') ->andWhere('s.clinic = :clinic') ->andWhere('s.entityType = :type') ->andWhere('s.active = true') ->setParameter('user', $user) ->setParameter('clinic', $clinic) ->setParameter('type', DoctorSecretary::OWNER_CLINIC) ->getQuery() ->getResult(); } /** همه روابط فعال یک منشی — برای auth context builder */ public function findAllActiveBySecretary(User $user): array { return $this->findBy(['secretary' => $user, 'active' => true]); } /** اولین رابطه فعال — backward compat برای کدهایی که هنوز migrate نشده‌اند */ public function findActiveBySecretary(User $user): ?DoctorSecretary { return $this->findOneBy(['secretary' => $user, 'active' => true]); } public function isDoctorInClinic(Doctor $doctor, Clinic $clinic): bool { return $clinic->getDoctors()->contains($doctor); } /** روابط owner_type='clinic' یک منشی مشخص در یک کلینیک (فعال و غیرفعال، برای sync) */ public function findByClinicAndSecretary(Clinic $clinic, User $secretary): array { return $this->createQueryBuilder('s') ->addSelect('doc') ->join('s.doctor', 'doc') ->where('s.clinic = :clinic') ->andWhere('s.secretary = :secretary') ->andWhere('s.entityType = :type') ->setParameter('clinic', $clinic) ->setParameter('secretary', $secretary) ->setParameter('type', DoctorSecretary::OWNER_CLINIC) ->getQuery() ->getResult(); } /** همه منشی ها کلینیک (owner_type='clinic') */ public function findByClinic(Clinic $clinic): array { return $this->listWithRelations() ->where('s.clinic = :clinic') ->andWhere('s.entityType = :type') ->setParameter('clinic', $clinic) ->setParameter('type', DoctorSecretary::OWNER_CLINIC) ->getQuery() ->getResult(); } /** * رابطه‌های فعالی که سهم درآمد نوبت آنلاین برایشان روشن است — برای کلینیک همهٔ * منشی‌های همان کلینیک، برای مطب شخصی منشی‌های همان پزشک. * * @return DoctorSecretary[] */ public function findOnlineShareRows(Doctor $doctor, ?Clinic $clinic): array { $qb = $this->createQueryBuilder('s') ->addSelect('sec') ->join('s.secretary', 'sec') ->where('s.active = true') ->andWhere('s.onlineShareEnabled = true') ->andWhere('s.onlineSharePercent > 0'); if ($clinic !== null) { $qb->andWhere('s.clinic = :clinic') ->andWhere('s.entityType = :type') ->setParameter('clinic', $clinic) ->setParameter('type', DoctorSecretary::OWNER_CLINIC); } else { $qb->andWhere('s.doctor = :doctor') ->andWhere('s.entityType = :type') ->setParameter('doctor', $doctor) ->setParameter('type', DoctorSecretary::OWNER_DOCTOR); } return $qb->getQuery()->getResult(); } public function save(DoctorSecretary $entity, bool $flush = true): void { $this->getEntityManager()->persist($entity); if ($flush) { $this->getEntityManager()->flush(); } } public function remove(DoctorSecretary $entity, bool $flush = true): void { $this->getEntityManager()->remove($entity); if ($flush) { $this->getEntityManager()->flush(); } } }