# چک‌لیست — تسک ۱۲ (دوره درمان) **وضعیت کلی:** ⏳ شروع نشده · **آخرین بازبینی:** — قواعد: [_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` سبز | ⏳ | | | ۰.۲ | `PatientSession` موجود دست‌نخورده | ⏳ | «مراجعهٔ انجام‌شده» ≠ «جلسهٔ دوره» | | ۰.۳ | رویدادهای تسک ۰۷ **بعد از** commit منتشر می‌شوند | ⏳ | ⭐ وگرنه در rollback هشت پیامک اشتباه | | ۰.۴ | `abandon` نوبت‌های `booked` را **لغو نمی‌کند** | ⏳ | عمل برگشت‌ناپذیر روی ظرفیت | ## ۱. بک‌اند | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `CourseProtocol` · `CourseProtocolStep` · `TreatmentCourse` · `CourseSession` | ⏳ | | | ۱.۲ | `CourseStarter` · `CourseScheduler` · `CourseProgressCalculator` · `CourseSessionLinker` | ⏳ | | | ۱.۳ | چهار فیلد فاصله و `params` **snapshot** می‌شوند | ⏳ | ⭐ قانون پنجم | | ۱.۴ | `book-all` **همه یا هیچ** در یک تراکنش | ⏳ | ⭐⭐ رزرو نیمه‌کاره بدترین حالت | | ۱.۵ | لنگر **متحرک** — فاصله از جلسهٔ قبلی، نه از شروع دوره | ⏳ | ⭐ | | ۱.۶ | `findNearestInRange` — نزدیک‌ترین به **ایده‌آل**، نه اولین موجود | ⏳ | | | ۱.۷ | لنگر پیشنهاد بعدی = آخرین جلسهٔ **`completed`**، نه `booked` | ⏳ | ⭐ | | ۱.۸ | سقف ۹۰ روز → جلسات باقی `planned` + **پیام روشن** | ⏳ | ⭐ | | ۱.۹ | `same_as_previous` ترجیح است نه الزام — fallback به `least_gap` | ⏳ | | | ۱.۱۰ | `preferredResourceIds` در `PlanRequest` حمل می‌شود | ⏳ | | | ۱.۱۱ | `SameAsPreviousPicker` تسک ۰۶ ورودی گرفت | ⏳ | | | ۱.۱۲ | تعامل با `spacing`: `max(min)` و `min(max)`؛ بازهٔ تهی → ۴۲۲ روشن | ⏳ | سخت‌گیرانه‌تر برنده | | ۱.۱۳ | اعتبار پکیج کمتر از جلسات → **هشدار**، نه خطا | ⏳ | | | ۱.۱۴ | `active_course_key` با الگوی `active_slot_key` | ⏳ | ⭐ نه UNIQUE روی `status` | | ۱.۱۵ | `CourseSessionLinker` هر دو سمت رابطه را هم‌زمان ست می‌کند | ⏳ | جای دیگری نه | | ۱.۱۶ | جلسهٔ آخر `completed` → دوره `completed` خودکار + رویداد | ⏳ | | | ۱.۱۷ | نُه endpoint | ⏳ | | | ۱.۱۸ | `TenantOwnershipChecker` روی هر uuid از request | ⏳ | | ## ۲. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | چهار جدول | ⏳ | | | ۲.۲ | `min_days <= ideal_days <= max_days` و `session_count >= 2` | ⏳ | | | ۲.۳ | `UNIQUE(protocol_id, session_number)` و `UNIQUE(course_id, session_number)` | ⏳ | | | ۲.۴ | `UNIQUE(appointment_id)` روی `course_sessions` | ⏳ | | | ۲.۵ | `appointments.course_session_id` تهی‌پذیر (رابطهٔ دوطرفه، عمدی) | ⏳ | | | ۲.۶ | `course_protocol_steps` در `AGGREGATE_CHILDREN` | ⏳ | | | ۲.۷ | `TenantSchemaCoverageTest` سبز | ⏳ | | ## ۳. UI | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `CourseProtocolsPage` — پروتکل + جدول پارامتر جلسات | ⏳ | | | ۳.۲ | `TreatmentCoursePage` — نوار پیشرفت، جدول جلسات، دو دکمهٔ رزرو | ⏳ | | | ۳.۳ | کارت «دوره‌های درمان» در `PatientDetailPage` | ⏳ | | | ۳.۴ | ستون «فاصله» عدد **واقعی** بین جلسات را نشان می‌دهد، نه ایده‌آل | ⏳ | ⭐ کلینیک نظم بیمار را می‌فهمد | | ۳.۵ | نوار زرد هشدار عبور از حداکثر فاصله | ⏳ | | | ۳.۶ | پیشنهاد جلسهٔ بعدی به‌صورت بنر پس از `completed` شدن جلسه | ⏳ | | | ۳.۷ | نام منبع ترجیحی روی دکمهٔ رزرو («رزرو با اپراتور مریم») | ⏳ | | | ۳.۸ | پس از لغو جلسهٔ وسط: پیشنهاد «بازچینی جلسات باقی‌مانده» با کلیک صریح | ⏳ | خودکار نه | | ۳.۹ | `DataTable` برای جدول جلسات | ⏳ | | | ۳.۱۰ | `StatusBadge` برای وضعیت جلسه و دوره | ⏳ | | | ۳.۱۱ | تاریخ‌ها شمسی با `PersianDatePicker`/`formatDate` | ⏳ | | | ۳.۱۲ | `backTo`/`BackButton` روی زیرصفحه‌ها | ⏳ | | | ۳.۱۳ | هیچ رنگ/شعاع hard-code — نوار پیشرفت هم | ⏳ | | | ۳.۱۴ | دارک‌مود و حالت فشرده | ⏳ | | | ۳.۱۵ | RTL و موبایل | ⏳ | | | ۳.۱۶ | همهٔ رشته‌ها فارسی | ⏳ | | | ۳.۱۷ | پیام سقف ۹۰ روز در UI نمایش داده می‌شود | ⏳ | ⭐ وگرنه کاربر فکر می‌کند خراب است | ## ۴. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۴.۱ | `CourseStarterTest` — ۸ جلسه، بدون پروتکل ۴۲۲، دورهٔ دوم ۴۲۲ با `meta` | ⏳ | | | ۴.۲ | `ProtocolSnapshotTest` | ⏳ | ⭐ | | ۴.۳ | `CourseSchedulerTest` — لنگر متحرک، نزدیک‌ترین به ایده‌آل | ⏳ | ⭐ | | ۴.۴ | `CourseSchedulerTest` — شکست جلسهٔ N → rollback ۱..N-1 | ⏳ | ⭐⭐ | | ۴.۵ | `CourseSchedulerTest` — سقف ۹۰ روز + پیام | ⏳ | | | ۴.۶ | `NextSuggestionTest` — لنگر `completed`، هشدار عبور از max | ⏳ | | | ۴.۷ | `CourseProgressTest` | ⏳ | | | ۴.۸ | `SameResourcePreferenceTest` — fallback بدون خطا | ⏳ | | | ۴.۹ | `CoursePolicyInteractionTest` — سخت‌گیرانه‌تر برنده، بازهٔ تهی ۴۲۲ | ⏳ | | | ۴.۱۰ | `CoursePackageTest` — مصرف per جلسه، هشدار نه خطا | ⏳ | | | ۴.۱۱ | `CourseLifecycleTest` — لغو، `no_show`، تکمیل خودکار، `abandon` | ⏳ | | ## ۵. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | `docs/api/course.md` | ⏳ | | | ۵.۲ | قاعدهٔ «سخت‌گیرانه‌تر برنده» بین پروتکل و قانون | ⏳ | | | ۵.۳ | رفتار سقف ۹۰ روز | ⏳ | | | ۵.۴ | «`abandon` نوبت‌ها را لغو نمی‌کند» صریح | ⏳ | | ## ۶. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | هیچ 🔄 و ⏳ بی‌دلیل نمانده | ⏳ | | | ۶.۲ | `bin/phpunit` کامل سبز | ⏳ | | | ۶.۳ | `--group=slot-mode-frozen` سبز | ⏳ | | | ۶.۴ | `phpstan` بدون خطای جدید | ⏳ | | | ۶.۵ | `npx tsc --noEmit` و `yarn test` سبز | ⏳ | | | ۶.۶ | تست‌های tenant سبز | ⏳ | | | ۶.۷ | `docs/api/*` به‌روز | ⏳ | | | ۶.۸ | چک‌لیست UI کامل | ⏳ | | | ۶.۹ | دو کلاینت دیگر بررسی شدند | ⏳ | نوبت‌های دوره در پنل بیمار درست دیده می‌شوند؟ | | ۶.۱۰ | commit، سپس `graphify update .` | ⏳ | | | ۶.۱۱ | موارد به‌تعویق با دلیل و تسک مقصد | ⏳ | |