feat(secretary): implement multi-doctor assignment for clinic secretaries

- Added functionality to assign a single secretary to multiple doctors within a clinic, allowing for scoped access to appointments.
- Introduced `SecretaryService` to handle the logic for assigning and syncing doctors for a secretary.
- Updated `SecretaryController` to support multi-doctor assignment via new endpoints and modified existing ones.
- Enhanced `DoctorSecretary` entity to include secretary UUID in its serialized output.
- Implemented repository methods to facilitate the retrieval and management of doctor-secretary relationships.
- Adjusted appointment filtering in `MyAppointmentsController` to ensure secretaries only see appointments for assigned doctors.
- Created tests to validate the new multi-doctor assignment functionality and appointment access restrictions.
- Updated frontend components to support multi-select for doctors in the secretary management UI.
This commit is contained in:
hamed
2026-07-18 08:49:04 +03:30
parent 6ab7ed38b8
commit 1779e0d6de
13 changed files with 947 additions and 51 deletions
@@ -88,12 +88,25 @@ class DoctorSecretaryRepository extends ServiceEntityRepository
->getOneOrNullResult();
}
/** رابطه فعالِ owner=clinic برای یک (منشی، کلینیک، پزشک) مشخص */
public function findActiveClinicRow(User $user, Clinic $clinic, Doctor $doctor): ?DoctorSecretary
{
return $this->findOneBy([
'secretary' => $user,
'clinic' => $clinic,
'doctor' => $doctor,
'ownerType' => DoctorSecretary::OWNER_CLINIC,
'active' => true,
]);
}
/** همه دکترهای کلینیک که این منشی به آن‌ها متصل است */
public function findDoctorsBySecretaryInClinic(User $user, Clinic $clinic): array
{
return $this->createQueryBuilder('s')
return $this->getEntityManager()->createQueryBuilder()
->select('d')
->join('s.doctor', 'd')
->from(Doctor::class, 'd')
->join(DoctorSecretary::class, 's', 'WITH', 's.doctor = d')
->where('s.secretary = :user')
->andWhere('s.clinic = :clinic')
->andWhere('s.ownerType = :type')
@@ -122,6 +135,22 @@ class DoctorSecretaryRepository extends ServiceEntityRepository
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.ownerType = :type')
->setParameter('clinic', $clinic)
->setParameter('secretary', $secretary)
->setParameter('type', DoctorSecretary::OWNER_CLINIC)
->getQuery()
->getResult();
}
/** همه منشی ها کلینیک (owner_type='clinic') */
public function findByClinic(Clinic $clinic): array
{