# تسک ۱۴: ماژول منشی

## توضیح
مدیریت منشی‌های دکترها که می‌توانند نوبت‌ها و پرداخت‌ها را مدیریت کنند.
هر دکتر بسته به پلن اشتراک می‌تواند ۱ یا ۳ منشی فعال داشته باشد.

## Endpoint ها

| متد | مسیر | توضیح | نیاز به Auth |
|-----|------|-------|-------------|
| POST | `/api/v1/secretary` | ایجاد منشی | بله (Doctor/Admin) |
| PATCH | `/api/v1/secretary/{uuid}` | ویرایش منشی | بله (Doctor/Admin) |
| GET | `/api/v1/secretary/{uuid}` | دریافت اطلاعات منشی | بله |
| DELETE | `/api/v1/secretary/{uuid}` | حذف منشی | بله (Doctor/Admin) |
| GET | `/api/v1/secretaries/{doctorUuid}` | لیست منشی‌های دکتر | بله |

## پیش‌نیازها
- تسک ۰۱، ۰۲، ۰۵ (Doctor)

## زمان تخمینی
۵ تا ۶ ساعت

---

## سیستم مجوزها — Resource-Based Permissions (مقیاس‌پذیر)

فیلد `permissions` در جدول `doctor_secretaries` یک JSON ساختاریافته با نسخه‌بندی است.
طراحی به گونه‌ای است که در آینده بتوان منابع (`resources`) و عملیات (`actions`) جدید اضافه کرد بدون تغییر در ساختار جدول.

### ساختار JSON

```json
{
  "version": 1,
  "resources": {
    "appointments": {
      "view":          true,
      "create":        true,
      "cancel":        false,
      "update_status": true
    },
    "addresses": {
      "view":   true,
      "create": true,
      "update": true,
      "delete": false
    },
    "clinic_info": {
      "view":   true,
      "update": false
    },
    "insurances": {
      "view":   true,
      "create": true,
      "update": true,
      "delete": false
    }
  }
}
```

### منابع و عملیات فعلی

| Resource | Actions | توضیح |
|----------|---------|-------|
| `appointments` | `view`, `create`, `cancel`, `update_status` | نوبت‌ها |
| `addresses` | `view`, `create`, `update`, `delete` | آدرس‌های مطب/کلینیک |
| `clinic_info` | `view`, `update` | اطلاعات مطب یا کلینیک |
| `insurances` | `view`, `create`, `update`, `delete` | بیمه‌ها |

### مقیاس‌پذیری — اضافه کردن Resource جدید در آینده

برای اضافه کردن Resource جدید (مثلاً `patients` یا `reports`) فقط کافی است:
1. کلید جدید به JSON اضافه شود — بدون migration جدید
2. کد Permission Checker به صورت خودکار آن را پشتیبانی می‌کند
3. منشی‌های موجود که کلید جدید را ندارند، به صورت پیش‌فرض `false` دارند

### پیاده‌سازی PHP — SecretaryPermissionChecker

```php
// src/Secretary/Security/SecretaryPermissionChecker.php
class SecretaryPermissionChecker
{
    /**
     * بررسی مجوز منشی برای یک عملیات روی یک منبع
     * مثال: $checker->can($secretary, 'appointments', 'create')
     */
    public function can(Secretary $secretary, string $resource, string $action): bool
    {
        if (!$secretary->isActive()) {
            return false;
        }

        $permissions = $secretary->getPermissions();
        return (bool) ($permissions['resources'][$resource][$action] ?? false);
    }

    /**
     * بررسی دسترسی کامل به یک منبع (همه actions باید true باشند)
     */
    public function canAll(Secretary $secretary, string $resource, array $actions): bool
    {
        return array_reduce(
            $actions,
            fn($carry, $action) => $carry && $this->can($secretary, $resource, $action),
            true
        );
    }
}
```

**مثال استفاده در Controller:**
```php
// در AppointmentController
if (!$this->permissionChecker->can($secretary, 'appointments', 'create')) {
    throw new AccessDeniedHttpException('منشی مجاز به ثبت نوبت نیست');
}

// در InsuranceController
if (!$this->permissionChecker->can($secretary, 'insurances', 'delete')) {
    throw new AccessDeniedHttpException('منشی مجاز به حذف بیمه نیست');
}
```

### پیش‌فرض هنگام ایجاد منشی

```json
{
  "version": 1,
  "resources": {
    "appointments": {
      "view":          true,
      "create":        true,
      "cancel":        false,
      "update_status": true
    },
    "addresses": {
      "view":   true,
      "create": false,
      "update": false,
      "delete": false
    },
    "clinic_info": {
      "view":   true,
      "update": false
    },
    "insurances": {
      "view":   true,
      "create": false,
      "update": false,
      "delete": false
    }
  }
}
```

---

## POST /api/v1/secretary

```json
// Request
{
  "mobile_number": "09120671756",
  "doctor_uuid": "61be915b-...",
  "permissions": {
    "version": 1,
    "resources": {
      "appointments": {
        "view": true,
        "create": true,
        "cancel": false,
        "update_status": true
      },
      "addresses": {
        "view": true,
        "create": false,
        "update": false,
        "delete": false
      },
      "clinic_info": {
        "view": true,
        "update": false
      },
      "insurances": {
        "view": true,
        "create": false,
        "update": false,
        "delete": false
      }
    }
  }
}

// Response 201
{
  "success": true,
  "data": {
    "uuid": "...",
    "user": { "uuid": "...", "realname": "فاطمه رضایی", "mobile": "09120671756" },
    "doctor": { "uuid": "...", "name": "دکتر احمدی" },
    "active": true,
    "permissions": { ... },
    "created_at": 1748000000
  }
}

// Response 422 — حد مجاز منشی
{
  "success": false,
  "errors": [{ "code": "ERR_SECRETARY_001", "message": "پلن فعلی اجازه منشی بیشتر را نمی‌دهد" }]
}
```

**قانون بررسی پلن (سمت سرور):**
```
پلن بیسیک  → max 1 منشی فعال
پلن پیشرفته → max 3 منشی فعال

هنگام POST /secretary:
  activeCount = COUNT(*) WHERE doctor_id=X AND active=true
  if activeCount >= maxAllowed → 422
```

---

## PATCH /api/v1/secretary/{uuid}

```json
// Request (فقط resources موردنظر — deep merge با پیش‌فرض‌ها)
{
  "active": false,
  "permissions": {
    "resources": {
      "insurances": {
        "create": true,
        "update": true
      }
    }
  }
}
// نکته: فقط resources/actions ارسال‌شده تغییر می‌کنند — بقیه دست‌نخورده می‌مانند
```

// Response 200
{
  "success": true,
  "data": { ... }
}
```

---

## GET /api/v1/secretary/{uuid}

```json
{
  "success": true,
  "data": {
    "uuid": "...",
    "user": {
      "uuid": "...",
      "realname": "فاطمه رضایی",
      "mobile": "09120671756",
      "picture": null
    },
    "doctor": { "uuid": "...", "name": "دکتر احمدی" },
    "active": true,
    "permissions": {
      "version": 1,
      "resources": {
        "appointments": { "view": true, "create": true, "cancel": false, "update_status": true },
        "addresses":    { "view": true, "create": false, "update": false, "delete": false },
        "clinic_info":  { "view": true, "update": false },
        "insurances":   { "view": true, "create": false, "update": false, "delete": false }
      }
    },
    "created_at": 1748000000
  }
}
```

---

## GET /api/v1/secretaries/{doctorUuid}

```json
{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "user": { "uuid": "...", "realname": "فاطمه رضایی", "mobile": "09120671756" },
      "active": true,
      "permissions": { ... },
      "created_at": 1748000000
    }
  ]
}
```

---

## نکات مهم

- **کاربر منشی:** هنگام ایجاد منشی با mobile_number، ابتدا بررسی می‌شود آیا کاربر با این شماره وجود دارد — اگر نه، کاربر جدید ایجاد می‌شود
- **ROLE:** کاربر منشی باید role `doctor_s_secretary` داشته باشد
- **لاگین منشی:** منشی می‌تواند با username/password لاگین کند (تسک ۰۲)
- **بررسی پلن:** کاملاً سمت سرور انجام می‌شود، قابل دور زدن نیست
- **نوبت آفلاین:** نوبتی که منشی ثبت می‌کند (`appointments.create`) کمیسیون نماینده ندارد
- **PATCH permissions:** فقط resources/actions ارسال‌شده تغییر می‌کنند (deep merge) — بقیه دست‌نخورده
- **Resource ناشناخته:** اگر resource جدیدی در JSON باشد که سرور نمی‌شناسد، نادیده گرفته می‌شود (forward compat)
- **پیش‌فرض `false`:** اگر resource یا action در JSON وجود نداشته باشد → `false` (deny by default)
