fix(subscription): report the acting environment's plan for gating

Feature gates and the resource cap are enforced against the environment
the user is standing in, but /subscription/my only ever returned the
plan of the environment they own. A doctor working as a guest in another
clinic consumed the host clinic's resource quota while the panel showed
their own plan's cap, so the quota number and the menu locks disagreed
with what the server would allow.

/subscription/my now also returns context_plan — limits and features of
the acting environment, without the other environment's plan identity.
effective_plan, subscription and used_trial stay on the owned
environment so the purchase flow is unchanged, and useSubscription
reads its caps and hasFeature from context_plan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-20 14:48:13 +03:30
co-authored by Claude Opus 5
parent 25a12e9a06
commit 601d211d6f
5 changed files with 99 additions and 12 deletions
+11 -2
View File
@@ -107,13 +107,21 @@
"is_active": true
},
"used_trial": false,
"effective_plan": { "name": "basic", "level": 1, "max_secretaries": 3, "max_resources": 3, "features": {...} }
"effective_plan": { "name": "basic", "level": 1, "max_secretaries": 3, "max_resources": 3, "features": {...} },
"context_plan": { "max_secretaries": 3, "max_resources": 3, "features": {...} }
}
}
```
`is_granted` یعنی این اشتراک را ادمین بدون پرداخت اعطا کرده است.
`effective_plan` و `context_plan` دو محیط متفاوت را جواب می‌دهند و برای کاربری که فقط محیط خودش را دارد یکی هستند:
- `effective_plan` و `subscription` و `used_trial` مالِ محیطِ **مالکیت**‌اند. مبنای خرید و ارتقا همین است.
- `context_plan` مالِ محیطی است که کاربر همین حالا **در آن ایستاده**. سرور سقف منابع و قفل قابلیت‌ها را با همین محیط می‌سنجد، پس پنل هم باید سقف‌ها و `hasFeature` را از این بخواند.
پزشکِ مهمانِ یک کلینیک نمونهٔ واگرایی است: منابعی که می‌سازد از سهمیهٔ کلینیک میزبان کم می‌شود، ولی اشتراکِ خودش همان اشتراک شخصی می‌ماند. `context_plan` فقط سقف‌ها و `features` را دارد؛ فیلدهای هویتیِ پلنِ محیط دیگر (`name`, `level`, `uuid`) در آن نمی‌آید.
اگر اشتراک فعالی نداشت `subscription` برابر `null` است، اما `effective_plan` همیشه مقدار دارد: پلن اشتراک فعال، یا در نبود اشتراک، **پلن پیش‌فرض `free`**. فرانت‌اند برای تعیین دسترسی به امکانات (`hasFeature`) باید از `effective_plan` استفاده کند (نه `subscription`) تا کاربرانِ بدون اشتراک هم امکانات پلن free را داشته باشند. `subscription`/`hasPlan` صرفاً برای نمایش وضعیت اشتراک پولی است.
### پاسخ کاهش‌یافته برای کاربرِ بدون مجوزِ `subscription.view` (2026-08)
@@ -129,7 +137,8 @@
{"success":true,"data":{
"subscription": null,
"used_trial": false,
"effective_plan": { "features": { "patient_records": true, "…": true }, "max_secretaries": 1, "max_resources": 1 }
"effective_plan": { "features": { "patient_records": true, "…": true }, "max_secretaries": 1, "max_resources": 1 },
"context_plan": { "features": { "patient_records": true, "…": true }, "max_secretaries": 1, "max_resources": 1 }
}}
```