# 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 # ۱۶ تست ```