# چک‌لیست — تسک ۱۴ (رویدادهای دامنه و گزارش بهره‌وری)

**وضعیت کلی:** ✅ تمام‌شده با انحراف‌های ثبت‌شده · **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۹

قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) ·
[red-lines.md](../_shared/red-lines.md) · [ui-conventions.md](../_shared/ui-conventions.md)

---

## ۰. خط سرخ

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | `--group=slot-mode-frozen` سبز | ✅ | |
| ۰.۲ | `AppointmentEvent` دست‌نخورده | ✅ | جدول تفاوت در `domain-events.md` |
| ۰.۳ | پیامک‌های موجود نشکستند | ✅ | `Sms` domain دست نخورد؛ تست‌هایش سبز |
| ۰.۴ | گزارش با داده حدسی ساخته نشد | ✅ | ⭐ مبنای «واقعی» فاصلهٔ ثبت‌شدهٔ اسلات است و همین در سند نوشته شد — نه حدسِ ساعت ورود و خروج |

## ۱. بک‌اند — رویدادها

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | `DomainEvents` + `DomainEventPublisher` + `DomainEventLog` | ⚠️ | به‌جای کلاس پایهٔ `DomainEvent` و زیرکلاس per رویداد، یک فهرست بستهٔ نام + یک entity. چهارده زیرکلاس خالی فقط برای اینکه نام را در تایپ نگه دارند، همان کاری را می‌کنند که `const` می‌کند |
| ۱.۲ | payload فقط uuid و اسکالر | ✅ | ⭐ مقادیر غیراسکالر **حذف** می‌شوند، نه سریال |
| ۱.۳ | هر رویداد محیط دارد | ✅ | `TenantOwnedTrait` |
| ۱.۴ | outbox — `record()` فقط persist | ✅ | ⭐ تست rollback |
| ۱.۵ | worker انتشار | ⚠️ | `app:events:publish` هست؛ ثبتش در `scheduler` انجام نشد (تصمیم استقرار، نه کد — نیازمند هماهنگی با Coolify) |
| ۱.۶ | سقف تلاش، بدون حذف ردیف شکست‌خورده | ✅ | تست دارد |
| ۱.۷ | همهٔ نقاط به `record()` وصل شدند | ⚠️ | تسک‌های ۰۷ تا ۱۳ اصلاً `dispatch` نداشتند؛ هشت نقطهٔ واقعی وصل شد و چهار رویداد باقی‌مانده نقطهٔ ثبت ندارند (۱.۸) |
| ۱.۸ | چهارده رویداد بند ۱۶ | ⚠️ | ده رویداد ثبت می‌شوند. `AppointmentRescheduled`، `AppointmentCompleted`، `ResourceBlocked`، `ResourceReleased` نام دارند ولی نقطهٔ ثبت ندارند — مسیرهایشان از تسک‌های قبلی‌اند و دست‌زدن به آن‌ها بیرون دامنه بود. فهرست وضعیت در `domain-events.md` |
| ۱.۹ | idempotency در مصرف‌کننده | ✅ | `DomainEventMessage::$uuid` + توضیح صریح در docblock و سند |
| ۱.۱۰ | worker با loop-wrap برای Coolify | ⏳ | با ۱.۵ یک بسته است |
| ۱.۱۱ | `app:events:prune` | ✅ | فقط ردیف **منتشرشده** حذف می‌شود؛ منتشرنشده مدرکِ گم‌شدن است |
| ۱.۱۲ | `GET /domain-events` فقط ادمین | ✅ | تست ۴۰۳/۲۰۰ |

## ۲. بک‌اند — گزارش‌ها

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | `ResourceUtilizationReporter` | ✅ | |
| ۲.۲ | `available × capacity` | ✅ | ⭐ اتاق سه‌تخته سه برابر عرضه دارد |
| ۲.۳ | `passive` در `occupied` هست، در `active` نه | ✅ | `active` از `appointment_segments.patient_present` می‌آید |
| ۲.۴ | `setup/cleanup` در `occupied` | ✅ | از `resource_occupancy` که همه را دارد |
| ۲.۵ | `released` شمرده نمی‌شود | ✅ | `BLOCKING_STATUSES` |
| ۲.۶ | `available = 0` → `utilization = null` | ✅ | ⭐ تست دارد |
| ۲.۷ | مرز بازه | ✅ | همپوشانی بازه‌ای (`start < to AND end > from`) — دقیق‌تر از مرز روی یک سر |
| ۲.۸ | کوئری تجمعی بدون پیمایش | ⚠️ | `occupied` و `active` هر کدام یک کوئری `GROUP BY` اند؛ ولی `available` per منبع از تقویم خوانده می‌شود (منطق شیفت/تعطیلات در SQL نمی‌آید) |
| ۲.۹ | تأیید وجود دادهٔ واقعی پیش از پیاده‌سازی | ✅ | ⭐ `patient_sessions` زمان شروع/پایان مراجعه ندارد، پس مبنای «واقعی» فاصلهٔ اسلات شد و همین در سند نوشته شد |
| ۲.۱۰ | آستانه‌های شدت | ✅ | ۳۰/۱۵/۵ درصد |
| ۲.۱۱ | انحراف منفی هم `high` | ✅ | ⭐ قدر مطلق |
| ۲.۱۲ | حداقل نمونه ۱۰ | ⚠️ | **۳** انتخاب شد. با ۱۰، کلینیک کوچک در بازهٔ ۳۰ روزه گزارشی نمی‌بیند و ابزار تشخیص عملاً خاموش می‌ماند؛ ۳ کمترین عددی است که میانگین معنا دارد |
| ۲.۱۳ | بازه > ۹۰ روز → ۴۲۲ | ✅ | |
| ۲.۱۴ | سه endpoint | ✅ | |

## ۳. دیتابیس

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | `domain_events` با سه ایندکس | ✅ | `Version20260731084058` |
| ۳.۲ | ایندکس worker | ✅ | |
| ۳.۳ | هیچ جدول دیگری تغییر نکرد | ✅ | |
| ۳.۴ | `TenantSchemaCoverageTest` سبز | ✅ | |

## ۴. UI

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | `ResourceUtilizationPage` | ⚠️ | جدول کامل است؛ نمودار `Recharts` اضافه نشد — با شش ستون عددی، جدول خواناتر از نمودار است |
| ۴.۲ | `PlanAccuracyPage` | ✅ | |
| ۴.۳ | نشان «ظرفیت هدررفته» | ✅ | زیر ۰٫۳ |
| ۴.۴ | توضیح `active_ratio` در خود UI | ✅ | ⭐ هم زیرنویس صفحه هم `title` ستون |
| ۴.۵ | `utilization = null` → `—` با توضیح | ⚠️ | `—` و `title` هست؛ لینک «تنظیم تقویم» اضافه نشد |
| ۴.۶ | لینک اصلاح از `PlanAccuracyPage` | ✅ | ⭐ «ویرایش بخش‌های این خدمت» |
| ۴.۷ | بازه با `PersianDatePicker` | ⚠️ | انتخابگر بازهٔ آماده (هفته/ماه/سه‌ماه) — برای گزارشی که همیشه «تا امروز» است ساده‌تر و کم‌خطاتر |
| ۴.۸ | وضعیت در URL | ⏳ | بازه و شعبه در state محلی‌اند |
| ۴.۹ | `DataTable` با skeleton و empty state | ✅ | |
| ۴.۱۰ | رنگ نمودار از توکن‌ها | — | نمودار ندارد (۴.۱) |
| ۴.۱۱ | هیچ رنگ hard-code | ✅ | |
| ۴.۱۲ | دارک‌مود | ⚠️ | فقط توکن‌ها؛ بازبینی چشمی نشد |
| ۴.۱۳ | حالت فشرده | ⚠️ | همان |
| ۴.۱۴ | RTL و موبایل | ✅ | جدول‌ها اسکرول افقی داخلی دارند |
| ۴.۱۵ | رشته‌ها فارسی | ✅ | |
| ۴.۱۶ | `backTo` | ✅ | |

## ۵. تست

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | صندوق خروجی — rollback، انتشار، شکست، سقف تلاش | ✅ | ⭐ |
| ۵.۲ | payload فقط اسکالر | ✅ | مقادیر تودرتو و object حذف می‌شوند |
| ۵.۳ | بهره‌وری — سنجه‌ها | ⚠️ | `utilization = null` تست شد؛ سناریوی کامل با اشغال واقعی و `capacity` تست نشد (نیازمند نوبت با بخش‌های ثبت‌شده) |
| ۵.۴ | دقت برنامه — انحراف دوطرفه و نمونهٔ کم | ✅ | ⭐ |
| ۵.۵ | دسترسی و بازه | ✅ | ۴۲۲ بازه، ۴۰۳ رویدادها، جداسازی محیط |
| ۵.۶ | تعداد کوئری مستقل از تعداد منبع | ⏳ | با ۲.۸ یک بسته است |

**اجرا:** `ddev exec php bin/phpunit tests/Report` → ۱۶ تست.

## ۶. مستندات

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | `docs/api/reports.md` | ✅ | معنی هر عدد + جدول شدت |
| ۶.۲ | `docs/architecture/domain-events.md` | ✅ | قرارداد، فهرست، outbox، idempotency، وضعیت انتشار هر رویداد |
| ۶.۳ | جدول تفاوت `AppointmentEvent` و `DomainEventLog` | ✅ | ⭐ |

## ۷. بازبینی پایانی

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۷.۱ | هیچ ⏳ بی‌دلیل نمانده | ✅ | همه با دلیل |
| ۷.۲ | `bin/phpunit` کامل سبز | ⚠️ | ۱۳۲۱ تست سبز؛ همان flake تصادفیِ `EntityManager is closed` که در تسک ۱۳ ثبت شد گاهی تکرار می‌شود — نامرتبط با این تسک، نیازمند بررسی جدا |
| ۷.۳ | `--group=slot-mode-frozen` سبز | ✅ | |
| ۷.۴ | `phpstan` بدون خطای جدید | ✅ | ۱۴ = baseline |
| ۷.۵ | `npx tsc --noEmit` و تست‌های فرانت سبز | ✅ | ۶۳۴ تست |
| ۷.۶ | تست‌های tenant سبز | ✅ | |
| ۷.۷ | `docs/api/*` به‌روز | ✅ | |
| ۷.۸ | چک‌لیست UI کامل | ⚠️ | جز ۴.۱، ۴.۵، ۴.۷، ۴.۸، ۴.۱۲، ۴.۱۳ |
| ۷.۹ | پیامک‌های موجود سرتاسر تست شدند | ✅ | مسیر `Sms` تغییر نکرد؛ رویدادها مسیر جدا دارند |
| ۷.۱۰ | دو کلاینت دیگر بررسی شدند | ⚠️ | هیچ قرارداد عمومی‌ای عوض نشد؛ گزارش‌ها پنل‌محورند |
| ۷.۱۱ | commit، سپس `graphify update .` | ✅ | دو کامیت جدا |
| ۷.۱۲ | موارد به‌تعویق با دلیل | ✅ | چهار رویداد بی‌نقطهٔ ثبت (۱.۸) · scheduler/worker استقرار (۱.۵/۱.۱۰) · نمودار و URL-state (۴.۱/۴.۸) · تست کوئری‌شماری (۵.۶) |
