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:
hamed
2026-08-19 22:07:56 +03:30
co-authored by Claude Opus 5
parent b81cb8a90e
commit 32044c8fa9
4 changed files with 89 additions and 28 deletions
@@ -158,6 +158,51 @@ class EntityContextResolverTest extends ApiTestCase
self::assertSame(['doctor', $doctor->getId()], $context->toEntityPair());
}
// ── محیطِ صاحب‌شده (اشتراک) ──────────────────────────────────────────────
/**
* اشتراک روی محیطی می‌نشیند که کاربر صاحبش است. کاربری که هم پزشک است و هم
* مالک کلینیک، دو محیط صاحب‌شده دارد و محیط فعال تعیین می‌کند کدام‌یک؛ وگرنه
* اشتراکِ اعطاشده به کلینیک هرگز در پنل دیده نمی‌شود.
*/
public function testOwnedEntityFollowsActiveContextWhenUserOwnsBoth(): void
{
$user = $this->createUser(['ROLE_DOCTOR', 'ROLE_CLINIC']);
$this->makeDoctor($user);
$clinic = $this->makeClinic($user);
$this->setClinicContext($user, $clinic);
self::assertSame(['clinic', $clinic->getId()], $this->resolver()->ownedEntity($user)->toEntityPair());
}
public function testOwnedEntityIsOwnPracticeWhenActiveContextIsSelf(): void
{
$user = $this->createUser(['ROLE_DOCTOR', 'ROLE_CLINIC']);
$doctor = $this->makeDoctor($user);
$this->makeClinic($user);
$this->setDoctorContext($user, $doctor);
self::assertSame(['doctor', $doctor->getId()], $this->resolver()->ownedEntity($user)->toEntityPair());
}
/** بدون محیط فعال، مطب شخصی همان پیش‌فرض قبلی می‌ماند. */
public function testOwnedEntityFallsBackToOwnPracticeWithoutActiveContext(): void
{
$user = $this->createUser(['ROLE_DOCTOR', 'ROLE_CLINIC']);
$doctor = $this->makeDoctor($user);
$this->makeClinic($user);
self::assertSame(['doctor', $doctor->getId()], $this->resolver()->ownedEntity($user)->toEntityPair());
}
/** مالک کلینیکِ غیرپزشک تنها یک محیط دارد و محیط فعال چیزی را عوض نمی‌کند. */
public function testOwnedEntityIsClinicForNonDoctorOwner(): void
{
$clinic = $this->makeClinic();
self::assertSame(['clinic', $clinic->getId()], $this->resolver()->ownedEntity($clinic->getUser())->toEntityPair());
}
// ── سناریو ۴: مدیر/مالک کلینیک (غیرپزشک) ────────────────────────────────
public function testClinicManagerAlwaysResolvesToClinic(): void