# تسک ۱۱: پنل اشتراکی (Subscription Tiers)

## توضیح
پیاده‌سازی سیستم پنل‌های اشتراکی سه‌سطحی. ادمین دوره‌ها و قیمت‌ها را تعریف می‌کند.
این تسک **gate check** برای تسک‌های ۱۲، ۱۳، ۱۵ فراهم می‌کند.

**نکته مهم:** `/api/v1/subscription-payment` و callback آن از قبل در `PaymentController` موجود است.
در این تسک فقط باید بعد از callback موفق، `ClinicSubscription` ساخته شود + endpoint های Subscription خودش.

## سطوح پنل

| Level | Name | منشی | patient_records | services |
|-------|------|------|----------------|---------|
| 0 | free | ۱ | ❌ | ❌ |
| 1 | basic | ۲ | ✅ | ✅ |
| 2 | professional | ۵ | ✅ | ✅ |

## Endpoint ها

| متد | مسیر | Permission | توضیح |
|-----|------|-----------|-------|
| GET | `/api/v1/subscription/plans` | public | لیست پنل‌ها + دوره‌ها + قیمت |
| GET | `/api/v1/subscription/my` | doctor/clinic | اشتراک فعال + `used_trial` |
| POST | `/api/v1/subscription/trial` | doctor/clinic | فعال‌سازی تریال Basic (یک‌بار) |
| GET | `/api/v1/admin/subscription/plans` | ROLE_ADMIN | لیست مدیریت پنل‌ها |
| POST | `/api/v1/admin/subscription/plan` | ROLE_ADMIN | ایجاد پنل |
| PATCH | `/api/v1/admin/subscription/plan/{uuid}` | ROLE_ADMIN | ویرایش پنل |
| POST | `/api/v1/admin/subscription/period` | ROLE_ADMIN | افزودن دوره |
| PATCH | `/api/v1/admin/subscription/period/{uuid}` | ROLE_ADMIN | ویرایش دوره/قیمت |
| DELETE | `/api/v1/admin/subscription/period/{uuid}` | ROLE_ADMIN | غیرفعال‌سازی دوره |
| GET | `/api/v1/admin/subscription/report` | ROLE_ADMIN | گزارش فروش + تریال‌ها |

**موجود (تغییر نمی‌کند):**
- `POST /api/v1/subscription-payment` — شروع پرداخت (body: `{ period_uuid }`)
- `GET|POST /api/v1/subscription-payment/callback/{gateway}` — callback gateway

## پیش‌نیازها
- تسک ۰۲ (Auth/JWT)
- تسک ۱۵-payment (PaymentController — موجود)

## زمان تخمینی
۱۴ تا ۱۶ ساعت

## نمونه Request

### POST /api/v1/subscription/trial
```json
{}
```
(body خالی — entity از JWT گرفته می‌شود)

### POST /api/v1/admin/subscription/period
```json
{
  "plan_uuid": "uuid-of-basic-plan",
  "label": "۶ ماهه",
  "duration_months": 6,
  "price_rials": 1200000,
  "is_trial": false,
  "sort_order": 3
}
```

### POST /api/v1/subscription-payment (موجود)
```json
{
  "period_uuid": "uuid-of-selected-period",
  "gateway": "mellat"
}
```

## نمونه Response

### GET /api/v1/subscription/plans
```json
{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "name": "basic",
      "level": 1,
      "max_secretaries": 2,
      "features": { "patient_records": true, "services": true, "sms_panel": false },
      "periods": [
        { "uuid": "...", "label": "تریال ۱ ماهه", "duration_months": 1, "price_rials": 0, "is_trial": true },
        { "uuid": "...", "label": "۱ ماهه", "duration_months": 1, "price_rials": 250000, "is_trial": false },
        { "uuid": "...", "label": "۶ ماهه", "duration_months": 6, "price_rials": 1200000, "is_trial": false }
      ]
    }
  ]
}
```

### GET /api/v1/subscription/my
```json
{
  "success": true,
  "data": {
    "plan": { "name": "basic", "level": 1, "features": { "patient_records": true, "services": true } },
    "period": { "label": "۶ ماهه", "duration_months": 6 },
    "is_trial": false,
    "starts_at": 1718000000,
    "expires_at": 1733360000,
    "used_trial": true,
    "days_remaining": 42
  }
}
```

وقتی اشتراک فعال ندارد:
```json
{
  "success": true,
  "data": {
    "plan": { "name": "free", "level": 0 },
    "expires_at": null,
    "used_trial": false
  }
}
```

### POST /api/v1/subscription/trial (موفق)
```json
{
  "success": true,
  "data": {
    "plan": "basic",
    "starts_at": 1718000000,
    "expires_at": 1720678400
  }
}
```

### POST /api/v1/subscription/trial (خطا — قبلاً استفاده شده)
```json
{
  "success": false,
  "errors": [{ "code": "ERR_TRIAL_ALREADY_USED", "message": "قبلاً از تریال استفاده کرده‌اید" }]
}
```

## قوانین تریال
- فقط برای پنل **basic** (level=1)
- هر entity یک‌بار — constraint `UNIQUE(entity_type, entity_id, is_trial)` + مقدار `is_trial=1` در DB
- بدون پرداخت — `payment_id = null`
- بعد از انقضا → برگشت به Free (داده‌ها حفظ می‌شوند)
- ادمین می‌تواند تریال را کلاً غیرفعال کند: `SiteConfig.trial_enabled = false`

## قانون تمدید
```
expires_at جدید = max(expires_at فعلی, time()) + duration_months × 30 × 86400
```
یعنی اگر اشتراک هنوز منقضی نشده، تمدید از تاریخ انقضا محاسبه می‌شود (نه از now).

## gate check در تسک‌های بعدی
```php
// SubscriptionService::hasFeature('patient_records') → bool
// false → $this->error(ErrorCodes::ERR_SUBSCRIPTION_REQUIRED, '...', 403)
```
