# Reports — بهره‌وری منابع و دقت برنامه

اندپوینت‌های `src/Report/*`. بند ۱۷ مستند، ریسک سوم: «کلینیک بخش‌های نوبت را اشتباه
تعریف کند → ظرفیت غلط حساب می‌شود». این دو گزارش تنها بازخوردی‌اند که آن اشتباه را
نشان می‌دهند.

---

## GET `/api/v1/reports/resource-utilization`

| Query | Type | Required | Description |
|---|---|---|---|
| `branch_uuid` | string | ✅ | |
| `from` / `to` | int | — | Unix؛ پیش‌فرض هفتهٔ گذشته، حداکثر ۹۰ روز |

### Response `200`
```json
{
  "success": true,
  "data": {
    "from": 1784880000,
    "to": 1785484800,
    "rows": [
      {
        "resource_uuid": "…",
        "resource_name": "اپراتور مریم",
        "role": "operator",
        "available_minutes": 2400,
        "occupied_minutes": 1800,
        "active_minutes": 1200,
        "utilization": 0.75,
        "active_ratio": 0.5,
        "wasted_capacity": false
      }
    ]
  }
}
```

سه عدد، سه معنا:

| عدد | یعنی |
|---|---|
| `available_minutes` | منبع طبق تقویمش چقدر در دسترس بوده |
| `occupied_minutes` | چقدر **گرفته** شده — شامل آماده‌سازی، تمیزکاری و بخش‌های انتظار |
| `active_minutes` | چقدر واقعاً کار شده — فقط بخش‌هایی که بیمار حاضر بوده |

فاصلهٔ `occupied` و `active` همان چیزی است که تعریف غلط بخش‌ها را لو می‌دهد. `active_ratio`
زیر ۰٫۳ با `wasted_capacity: true` می‌آید: منبعی که هشت ساعت اشغال بوده ولی دو ساعت کار
کرده یا بخش‌های `passive` زیادی گرفته یا انتظارها اشتباه به او نسبت داده شده.

⚠️ منبعی بدون تقویم `available_minutes: 0` و **`utilization: null`** می‌دهد، نه صفر:
تقسیم بر صفر معنای متفاوتی دارد — بهره‌وری‌اش تعریف‌نشده است، نه بد.

ردیف‌های `released` (لغوشده) در محاسبه نمی‌آیند، وگرنه هر لغو بهره‌وری را بالا می‌برد.

---

## GET `/api/v1/reports/plan-accuracy`

| Query | Type | Required | Description |
|---|---|---|---|
| `from` / `to` | int | — | پیش‌فرض هفتهٔ گذشته، حداکثر ۹۰ روز |

### Response `200`
```json
{
  "success": true,
  "data": {
    "from": 1784880000,
    "to": 1785484800,
    "rows": [
      {
        "service_uuid": "…",
        "service_name": "لیزر فول‌بادی",
        "sample_size": 4,
        "planned_minutes": 60,
        "actual_minutes": 90,
        "deviation_percent": 50,
        "severity": "high"
      }
    ]
  }
}
```

| قدر مطلق انحراف | شدت |
|---|---|
| ≥ ۳۰٪ | `high` |
| ≥ ۱۵٪ | `medium` |
| ≥ ۵٪ | `low` |
| کمتر | `none` |

شدت از **قدر مطلق** می‌آید: سرویسی که نصف زمان پیش‌بینی‌شده طول می‌کشد هم غلط تعریف
شده — ظرفیتی که می‌شد فروخت، خالی مانده.

فقط نوبت‌های `completed` شمرده می‌شوند (لغوشده چیزی دربارهٔ مدت واقعی نمی‌گوید) و
سرویس با کمتر از **سه** نمونه اصلاً نمی‌آید — میانگین دو نوبت، میانگین نیست.

مبنای «واقعی» فاصلهٔ ثبت‌شدهٔ اسلات است، نه ساعت ورود و خروج بیمار؛ آن دومی جایی ثبت
نمی‌شود و حدس زدنش بدتر از نداشتنش است.

---

## GET `/api/v1/domain-events`

**Permission:** `ROLE_ADMIN` (بقیه `403`)

| Query | Type | Description |
|---|---|---|
| `name` | string | فیلتر نام رویداد |
| `limit` | int | پیش‌فرض ۱۰۰، سقف ۵۰۰ |

```json
{
  "success": true,
  "data": [
    {
      "uuid": "…",
      "name": "AppointmentBooked",
      "payload": { "appointment_uuid": "…", "hold_uuid": "…" },
      "occurred_at": 1785484800,
      "published_at": 1785484802,
      "attempts": 0,
      "last_error": null
    }
  ]
}
```

`published_at: null` یعنی هنوز در صندوق خروجی است. جزئیات:
[../architecture/domain-events.md](../architecture/domain-events.md)

---

## خطاها

| Code | HTTP | Description |
|---|---|---|
| `ERR_VALIDATION_001` | 422 | بازهٔ وارونه یا بزرگ‌تر از ۹۰ روز |
| `ERR_VALIDATION_002` | 422 | `branch_uuid` غایب |

## تست‌ها

```bash
ddev exec php bin/phpunit tests/Report   # ۱۶ تست
```
