can($appointment, $user, self::ACTION_VIEW); } /** مجوز تغییر نوبت: ویرایش، جابه‌جایی، رزرو، جایگزینی و تغییر وضعیت. */ public function canManage(Appointment $appointment, User $user): bool { return $this->can($appointment, $user, self::ACTION_UPDATE_STATUS); } public function canCancel(Appointment $appointment, User $user): bool { return $this->can($appointment, $user, self::ACTION_CANCEL); } public function can(Appointment $appointment, User $user, string $action): bool { if ($user->hasRole('ROLE_ADMIN')) { return true; } if ($appointment->getDoctor()->getUser()->getId() === $user->getId()) { return true; } // بیمار نوبت خودش را می‌بیند و لغو می‌کند، ولی جابه‌جا/ویرایش نمی‌کند. if ($appointment->getUser()->getId() === $user->getId()) { return $action === self::ACTION_VIEW || $action === self::ACTION_CANCEL; } $clinic = $appointment->getClinic(); if ($clinic !== null && $this->clinicPermissions->can($user, $clinic, self::RESOURCE, $action)) { return true; } 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 { $context = $this->contextResolver->resolve($user); if (!$context->isResolved()) { return false; } if ($context->isClinic()) { if ($clinic === null || $context->id !== $clinic->getId()) { return false; } $relation = $this->secretaryRepo->findActiveClinicRow($user, $context->clinic, $doctor); return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, self::ACTION_UPDATE_STATUS); } // محیطِ مطب شخصی: نوبت هم باید در همان مطب شخصی باشد (clinic == null). if ($clinic !== null || $context->id !== $doctor->getId()) { return false; } $relation = $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $doctor); return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, self::ACTION_UPDATE_STATUS); } /** * کلینیکی که این کاربر در آن اجازهٔ دیدن نوبت‌های این پزشک را دارد، یا null. * برای لیست‌هایی که باید به یک محیط محدود شوند (نه تک‌نوبت). */ public function viewableClinicFor(User $user, \App\Doctor\Entity\Doctor $doctor): ?\App\Clinic\Entity\Clinic { $context = $this->contextResolver->resolve($user); // بیرون از محیط کلینیک، کلینیکِ تحتِ مالکیت بازمی‌گردد: کاربری که هم پزشک // است و هم مالک کلینیک، بدون محیط فعال به‌عنوان پزشک حل می‌شود ولی هنوز // باید لیست نوبت‌های کلینیک خودش را ببیند. $clinic = $context->isClinic() ? $context->clinic : $this->clinicRepo->findByUser($user); if ($clinic === null || !$clinic->hasDoctor($doctor)) { return null; } return $this->clinicPermissions->can($user, $clinic, self::RESOURCE, self::ACTION_VIEW) ? $clinic : null; } /** * منشی در محیط فعالِ خودش. در محیط کلینیک، نوبت باید هم متعلق به همان کلینیک * باشد و هم پزشکش جزو پزشکان تخصیص‌یافته به این منشی — عضویت در کلینیک به‌تنهایی * یعنی منشیِ یک پزشک بتواند نوبت پزشک دیگری را دست‌کاری کند. */ private function secretaryCan(Appointment $appointment, User $user, string $action): bool { $context = $this->contextResolver->resolve($user); if (!$context->isResolved()) { return false; } if ($context->isClinic()) { if ($appointment->getClinic()?->getId() !== $context->id) { return false; } $relation = $this->secretaryRepo->findActiveClinicRow($user, $context->clinic, $appointment->getDoctor()); return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, $action); } $doctor = $appointment->getDoctor(); if ($context->id !== $doctor->getId()) { return false; } $relation = $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $doctor); return $relation !== null && $this->secretaryPermissions->can($relation, self::RESOURCE, $action); } }