fix(subscription): read the subscription of the environment the user owns
A user who is both a doctor and a clinic owner always resolved to the doctor: SubscriptionController had its own role-first resolveEntity, and ownedEntity() returned the doctor whenever one existed. So a subscription granted to that user's clinic was stored correctly but never surfaced — /subscription/my kept reporting the free plan and the panel kept the feature-gated menu items locked. ownedEntity() now disambiguates with the active context when the user owns both environments, and the controller delegates to it instead of re-deriving the pair from roles. Payment already used ownedEntity(), so display, purchase and admin grant now agree on one environment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -81,16 +81,35 @@ class EntityContextResolver
|
||||
* برای خریدهایی است که به حساب خودِ صاحب مینشیند (اشتراک): آنجا «کجا ایستادهام»
|
||||
* مهم نیست، «چه چیزی دارم» مهم است. تنها مرجعِ این پرسش همین متد است تا پرداختِ
|
||||
* اشتراک و خودِ اشتراک هرگز روی دو محیط متفاوت ننشینند.
|
||||
*
|
||||
* استثنا: کاربری که هر دو محیط را دارد. آنجا محیط فعال تعیین میکند کدامیک،
|
||||
* وگرنه یکی از دو محیطِ صاحبشده هرگز اشتراک نمیگیرد.
|
||||
*/
|
||||
public function ownedEntity(User $user): EntityContext
|
||||
{
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
$clinic = $this->clinicRepo->findByUser($user);
|
||||
|
||||
// کاربری که هم پزشک است و هم مالک کلینیک، دو محیطِ صاحبشده دارد و «چه چیزی
|
||||
// دارم» بهتنهایی جواب یکتا ندارد. محیط فعال گره را باز میکند: اشتراک همان
|
||||
// جایی مینشیند که کاربر ایستاده و آن را میبیند. بدون این، اعطای اشتراک به
|
||||
// کلینیک برای چنین کاربری بیاثر میماند، چون پنل همیشه پزشک را میخواند.
|
||||
if ($doctor !== null && $clinic !== null) {
|
||||
$active = $this->fromActiveContext($user);
|
||||
if ($active !== null) {
|
||||
if ($active->isClinic() && $active->toEntityPair()[1] === $clinic->getId()) {
|
||||
return EntityContext::forClinic($clinic);
|
||||
}
|
||||
if (!$active->isClinic() && $active->toEntityPair()[1] === $doctor->getId()) {
|
||||
return EntityContext::forDoctor($doctor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($doctor !== null) {
|
||||
return EntityContext::forDoctor($doctor);
|
||||
}
|
||||
|
||||
$clinic = $this->clinicRepo->findByUser($user);
|
||||
|
||||
return $clinic !== null ? EntityContext::forClinic($clinic) : EntityContext::unknown();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Subscription\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserActiveContextRepository;
|
||||
use App\Clinic\Repository\ClinicRepository;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Context\EntityContextResolver;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use App\Shared\Exception\AppException;
|
||||
use App\Subscription\Entity\SubscriptionPeriod;
|
||||
@@ -34,7 +34,7 @@ class SubscriptionController extends BaseController
|
||||
private readonly ClinicSubscriptionRepository $subscriptionRepo,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly ClinicRepository $clinicRepo,
|
||||
private readonly UserActiveContextRepository $contextRepo,
|
||||
private readonly EntityContextResolver $contextResolver,
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly \App\Secretary\Security\SecretaryAccessChecker $secretaryAccess,
|
||||
private readonly SubscriptionTaxCalculator $tax,
|
||||
@@ -430,32 +430,23 @@ class SubscriptionController extends BaseController
|
||||
*
|
||||
* @return array{0: string, 1: int|null} [entityType, entityId]
|
||||
*/
|
||||
/**
|
||||
* محیطی که اشتراکِ این کاربر روی آن مینشیند.
|
||||
*
|
||||
* همان مرجعی که خریدِ اشتراک استفاده میکند (`PaymentController::subscriptionInit`)،
|
||||
* تا نمایش پنل و پرداخت و اعطای ادمین هر سه یک محیط را ببینند. نقشمحورِ محلی
|
||||
* بود و برای کاربری که هم پزشک است و هم مالک کلینیک، همیشه پزشک را برمیگرداند:
|
||||
* اشتراکِ کلینیک چنین کاربری در پنل «اعمالنشده» دیده میشد.
|
||||
*
|
||||
* منشی و پرسنل صاحب محیطی نیستند، پس برایشان محیط فعال خوانده میشود.
|
||||
*/
|
||||
private function resolveEntity(User $user): array
|
||||
{
|
||||
if ($user->hasRole('ROLE_DOCTOR')) {
|
||||
$doctor = $this->doctorRepo->findByUser($user);
|
||||
return $doctor !== null ? ['doctor', $doctor->getId()] : ['doctor', null];
|
||||
$owned = $this->contextResolver->ownedEntity($user);
|
||||
if ($owned->isResolved()) {
|
||||
return $owned->toEntityPair();
|
||||
}
|
||||
|
||||
if ($user->hasRole('ROLE_CLINIC')) {
|
||||
$clinic = $this->clinicRepo->findByUser($user);
|
||||
return $clinic !== null ? ['clinic', $clinic->getId()] : ['clinic', null];
|
||||
}
|
||||
|
||||
if ($user->hasRole('ROLE_SECRETARY')) {
|
||||
$dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
|
||||
if ($dbUuid !== null) {
|
||||
$clinic = $this->clinicRepo->findByUuid($dbUuid);
|
||||
if ($clinic !== null) {
|
||||
return ['clinic', $clinic->getId()];
|
||||
}
|
||||
$doctor = $this->doctorRepo->findByUuid($dbUuid);
|
||||
if ($doctor !== null) {
|
||||
return ['doctor', $doctor->getId()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['unknown', null];
|
||||
return $this->contextResolver->tryResolve($user)?->toEntityPair() ?? ['unknown', null];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user