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
@@ -301,9 +301,12 @@ class MyAppointmentsController extends BaseController
return $this->paginated([], 0, $page, $limit);
}
if ($filterType === 'clinic') {
$qb->join('App\Clinic\Entity\Clinic', 'c', 'WITH', 'd MEMBER OF c.doctors')
->andWhere('c = :clinic')
->setParameter('clinic', $filterValue);
// $filterValue = آرایه‌ی idهای پزشکانِ تخصیص‌یافته به این منشی در کلینیک
if (empty($filterValue)) {
return $this->paginated([], 0, $page, $limit);
}
$qb->andWhere('a.doctor IN (:doctorIds)')
->setParameter('doctorIds', $filterValue);
} else {
$qb->andWhere('a.doctor = :doctor')
->setParameter('doctor', $filterValue);
@@ -479,10 +482,8 @@ class MyAppointmentsController extends BaseController
$clinic = $this->clinicRepo->findByUuid($dbUuid);
if ($clinic !== null) {
if (!$clinic->getDoctors()->contains($doctor)) {
return false;
}
$rel = $this->secretaryRepo->findActiveBySecretaryForClinic($user, $clinic);
// منشی فقط برای پزشکانِ تخصیص‌یافته‌ی خودش می‌تواند رزرو کند، نه کل کلینیک
$rel = $this->secretaryRepo->findActiveClinicRow($user, $clinic, $doctor);
return $rel !== null && (bool) ($rel->getPermissions()['resources']['appointments']['create'] ?? false);
}
@@ -510,13 +511,17 @@ class MyAppointmentsController extends BaseController
return null;
}
// بررسی scope کلینیک
// بررسی scope کلینیک — فقط پزشکانِ تخصیص‌یافته به این منشی، نه کل کلینیک
$clinic = $this->clinicRepo->findByUuid($dbUuid);
if ($clinic !== null) {
$rel = $this->secretaryRepo->findActiveBySecretaryForClinic($user, $clinic);
if ($rel === null) return null;
$canView = (bool) ($rel->getPermissions()['resources']['appointments']['view'] ?? false);
return ['clinic', $clinic, $canView];
$canView = (bool) ($rel->getPermissions()['resources']['appointments']['view'] ?? false);
$doctorIds = array_map(
fn(Doctor $d) => $d->getId(),
$this->secretaryRepo->findDoctorsBySecretaryInClinic($user, $clinic)
);
return ['clinic', $doctorIds, $canView];
}
// بررسی scope مطب شخصی