feat(secretary): implement scope separation for secretaries in clinics and personal practices
- Added `owner_type` and `clinic_id` fields to `DoctorSecretary` entity to distinguish between clinic and personal practice relationships. - Updated repository methods to be scope-aware, allowing for specific queries based on the context of the secretary's relationship (clinic or doctor). - Modified `SecretaryController` to handle secretary creation with appropriate scope based on the current user's role. - Enhanced `AuthController` to build contexts that reflect the scope of the secretary's access. - Updated `DashboardController` and `PatientController` to respect the new scope logic when retrieving data. - Created migration to update the database schema accordingly, dropping the old unique constraint and adding the new fields and constraints.
This commit is contained in:
@@ -4,8 +4,10 @@ namespace App\Appointment\Controller;
|
||||
|
||||
use App\Appointment\Entity\Appointment;
|
||||
use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserActiveContextRepository;
|
||||
use App\Clinic\Repository\ClinicRepository;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Secretary\Entity\DoctorSecretary;
|
||||
use App\Secretary\Repository\DoctorSecretaryRepository;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -18,10 +20,11 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
class MyAppointmentsController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly DoctorSecretaryRepository $secretaryRepo,
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly DoctorSecretaryRepository $secretaryRepo,
|
||||
private readonly UserActiveContextRepository $contextRepo,
|
||||
) {}
|
||||
|
||||
#[Route('/api/v1/my/appointment', methods: ['POST'])]
|
||||
@@ -127,16 +130,22 @@ class MyAppointmentsController extends BaseController
|
||||
$qb->andWhere('a.doctor = :doctor')
|
||||
->setParameter('doctor', $doctor);
|
||||
} elseif (in_array('ROLE_SECRETARY', $roles, true)) {
|
||||
$rel = $this->secretaryRepo->findActiveBySecretary($user);
|
||||
if ($rel === null) {
|
||||
$filter = $this->resolveSecretaryFilter($user);
|
||||
if ($filter === null) {
|
||||
return $this->paginated([], 0, $page, $limit);
|
||||
}
|
||||
$canView = (bool) ($rel->getPermissions()['resources']['appointments']['view'] ?? false);
|
||||
[$filterType, $filterValue, $canView] = $filter;
|
||||
if (!$canView) {
|
||||
return $this->paginated([], 0, $page, $limit);
|
||||
}
|
||||
$qb->andWhere('a.doctor = :doctor')
|
||||
->setParameter('doctor', $rel->getDoctor());
|
||||
if ($filterType === 'clinic') {
|
||||
$qb->join('App\Clinic\Entity\Clinic', 'c', 'WITH', 'd MEMBER OF c.doctors')
|
||||
->andWhere('c = :clinic')
|
||||
->setParameter('clinic', $filterValue);
|
||||
} else {
|
||||
$qb->andWhere('a.doctor = :doctor')
|
||||
->setParameter('doctor', $filterValue);
|
||||
}
|
||||
} else {
|
||||
return $this->paginated([], 0, $page, $limit);
|
||||
}
|
||||
@@ -217,9 +226,16 @@ class MyAppointmentsController extends BaseController
|
||||
$qb->andWhere('a.doctor = :doctor')->setParameter('doctor', $doctor);
|
||||
}
|
||||
} elseif (in_array('ROLE_SECRETARY', $roles, true)) {
|
||||
$rel = $this->secretaryRepo->findActiveBySecretary($user);
|
||||
if ($rel) {
|
||||
$qb->andWhere('a.doctor = :doctor')->setParameter('doctor', $rel->getDoctor());
|
||||
$filter = $this->resolveSecretaryFilter($user);
|
||||
if ($filter !== null) {
|
||||
[$filterType, $filterValue] = $filter;
|
||||
if ($filterType === 'clinic') {
|
||||
$qb->join('App\Clinic\Entity\Clinic', 'c', 'WITH', 'd MEMBER OF c.doctors')
|
||||
->andWhere('c = :clinic')
|
||||
->setParameter('clinic', $filterValue);
|
||||
} else {
|
||||
$qb->andWhere('a.doctor = :doctor')->setParameter('doctor', $filterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,4 +260,40 @@ class MyAppointmentsController extends BaseController
|
||||
'cancelled' => $cancelled,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* تعیین فیلتر نوبتها برای منشی بر اساس scope فعال:
|
||||
* Returns [type, entity, canView] یا null اگر رابطهای پیدا نشد.
|
||||
* type: 'clinic' | 'doctor'
|
||||
* entity: Clinic | Doctor
|
||||
*/
|
||||
private function resolveSecretaryFilter(User $user): ?array
|
||||
{
|
||||
$activeCtx = $this->contextRepo->findByUser($user);
|
||||
$dbUuid = $activeCtx?->getDbUuid();
|
||||
|
||||
if ($dbUuid === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// بررسی 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];
|
||||
}
|
||||
|
||||
// بررسی scope مطب شخصی
|
||||
$doctor = $this->doctorRepo->findByUuid($dbUuid);
|
||||
if ($doctor !== null) {
|
||||
$rel = $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $doctor);
|
||||
if ($rel === null) return null;
|
||||
$canView = (bool) ($rel->getPermissions()['resources']['appointments']['view'] ?? false);
|
||||
return ['doctor', $doctor, $canView];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user