feat: Enhance appointment management by decoupling online booking toggle for admin context

- Introduced management mode for appointment slots, allowing doctors, admins, and clinic managers to view and book slots regardless of the online booking status.
- Updated SlotCalculatorService to accept a management context parameter, bypassing online booking restrictions.
- Modified appointment-related endpoints to handle management context and ensure proper authorization checks.
- Added tests to verify that management users can access slots even when online booking is disabled, while public users are still restricted.
- Improved documentation for API endpoints to reflect new management parameters and behaviors.
This commit is contained in:
hamed
2026-07-22 16:43:56 +03:30
parent 5507b42fd8
commit ed516c81a8
16 changed files with 658 additions and 83 deletions
@@ -78,6 +78,67 @@ class AppointmentAccessChecker
return $this->secretaryCan($appointment, $user, $action);
}
/**
* آیا این کاربر می‌تواند در محیطِ (پزشک + کلینیک) نوبت مدیریت/ثبت کند — بدون آنکه
* هنوز نوبتی وجود داشته باشد. برای اندپوینت‌های اسلات که عمومی‌اند ولی وقتی از پنل
* (با management=1) صدا زده می‌شوند باید توگلِ نوبت‌دهی آنلاین را دور بزنند.
*
* منطق همان can() است اما روی محیط، نه روی یک Appointment مشخص.
*/
public function canManageContext(User $user, \App\Doctor\Entity\Doctor $doctor, ?\App\Clinic\Entity\Clinic $clinic): bool
{
if ($user->hasRole('ROLE_ADMIN')) {
return true;
}
if ($doctor->getUser()->getId() === $user->getId()) {
return true;
}
if ($clinic !== null && $this->clinicPermissions->can($user, $clinic, self::RESOURCE, self::ACTION_UPDATE_STATUS)) {
return true;
}
return $this->secretaryCanContext($user, $doctor, $clinic);
}
/**
* منشی در محیطِ فعالِ خودش، اما روی محیط (پزشک/کلینیک) نه یک نوبت مشخص.
* قرینهٔ secretaryCan() است.
*/
private function secretaryCanContext(User $user, \App\Doctor\Entity\Doctor $doctor, ?\App\Clinic\Entity\Clinic $clinic): bool
{
$dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
if ($dbUuid === null) {
return false;
}
$ctxClinic = $this->clinicRepo->findByUuid($dbUuid);
if ($ctxClinic !== null) {
if ($clinic === null || $ctxClinic->getId() !== $clinic->getId()) {
return false;
}
$relation = $this->secretaryRepo->findActiveClinicRow($user, $ctxClinic, $doctor);
return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, self::ACTION_UPDATE_STATUS);
}
// محیطِ مطب شخصی: نوبت هم باید در همان مطب شخصی باشد (clinic == null).
if ($clinic !== null) {
return false;
}
$ctxDoctor = $this->doctorRepo->findByUuid($dbUuid);
if ($ctxDoctor === null || $ctxDoctor->getId() !== $doctor->getId()) {
return false;
}
$relation = $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $doctor);
return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, self::ACTION_UPDATE_STATUS);
}
/**
* کلینیکی که این کاربر در آن اجازهٔ دیدن نوبت‌های این پزشک را دارد، یا null.
* برای لیست‌هایی که باید به یک محیط محدود شوند (نه تک‌نوبت).