# چک‌لیست — تسک ۱۳ (سیاست لغو، عدم حضور، لیست انتظار) **وضعیت کلی:** ⏳ شروع نشده · **آخرین بازبینی:** — قواعد: [_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` سبز | ⏳ | | | ۰.۲ | پیش‌فرض سیاست **بدون جریمه** (`penalty_mode='none'`) | ⏳ | ⭐⭐ وگرنه لحظهٔ deploy همه مشمول جریمه | | ۰.۳ | بیمار پرریسک **مسدود نمی‌شود** — فقط برچسب | ⏳ | ⭐ مسدودسازی = قانون `eligibility` | | ۰.۴ | `ReserveAppointmentsPage`/`is_reserve` دست‌نخورده | ⏳ | مفهوم متفاوت از لیست انتظار | | ۰.۵ | وضعیت‌های لغو موجود (`cancelled_by_*`, `no_show`) دست‌نخورده | ⏳ | | ## ۱. بک‌اند — لغو و جریمه | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `CancellationPolicy` · `NoShowRecord` | ⏳ | | | ۱.۲ | `CancellationPolicyResolver` — سرویس بر محیط اولویت دارد | ⏳ | | | ۱.۳ | `PenaltyCalculator` — شرط «لغو توسط کلینیک» **اولین خط** | ⏳ | ⭐ | | ۱.۴ | سقف جریمه = مبلغ پرداختی (`min($penalty, $paid)`) | ⏳ | | | ۱.۵ | نوبت نقدی → جریمه صفر + `note` | ⏳ | | | ۱.۶ | `GET /cancellation-preview` پیش از لغو | ⏳ | ⭐ | | ۱.۷ | `CancellationService` هفت مرحله در یک تراکنش | ⏳ | | | ۱.۸ | جریمه در `WalletTransaction` با `setRecordedEntity()` | ⏳ | ⭐ وگرنه نشتی بین محیط‌ها | | ۱.۹ | بازگشت اعتبار پکیج **طبق سیاست** (`credit_refundable`)، نه همیشه | ⏳ | تسک ۱۱ `TODO` را برمی‌دارد | | ۱.۱۰ | `CourseSessionLinker::releaseSession()` صدا زده می‌شود | ⏳ | تسک ۱۲ | | ۱.۱۱ | لغو دوباره → idempotent | ⏳ | | | ۱.۱۲ | لغو نوبت گذشته → ۴۲۲ | ⏳ | | ## ۲. بک‌اند — عدم حضور | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | `NoShowTracker` با پنجرهٔ **۱۲ ماه** | ⏳ | نه کل تاریخ | | ۲.۲ | برچسب پرریسک از `TenantTag` موجود، نه ستون بولین جدید | ⏳ | ⭐ | | ۲.۳ | `UNIQUE(appointment_id)` → یک رکورد per نوبت | ⏳ | | | ۲.۴ | جدول جدا، نه ستون شمارنده روی بیمار | ⏳ | همان استدلال دفتر اعتبار | ## ۳. بک‌اند — لیست انتظار | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `WaitlistEntry` · `WaitlistService` · `WaitlistMatcher` | ⏳ | | | ۳.۲ | **broadcast** به حداکثر ۱۰ نفر، اولین رزروکننده می‌برد | ⏳ | تصمیم مکتوب | | ۳.۳ | متن پیامک شامل «اولین نفری که رزرو کند آن را می‌گیرد» | ⏳ | ⭐ اجباری | | ۳.۴ | `notify_count` سقف دارد (پیشنهاد ۳) | ⏳ | جلوگیری از اسپم | | ۳.۵ | اطلاع‌رسانی **async** روی رویداد، بیرون تراکنش لغو | ⏳ | ⭐ لغو مستقل از پیامک | | ۳.۶ | ترتیب: `priority DESC, created_at ASC` | ⏳ | | | ۳.۷ | `preferred_day_parts` در PHP فیلتر می‌شود | ⏳ | | | ۳.۸ | بیمار که خودش نوبت گرفت → `converted` خودکار روی رویداد `AppointmentBooked` | ⏳ | ⭐ وگرنه پیامک اضافه می‌گیرد | | ۳.۹ | `app:waitlist:expire` روزانه | ⏳ | | | ۳.۱۰ | بازهٔ دلخواه > ۹۰ روز → ۴۲۲ | ⏳ | | | ۳.۱۱ | هفت endpoint | ⏳ | | ## ۴. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۴.۱ | سه جدول | ⏳ | | | ۴.۲ | `idx_waitlist_match (service_item_id, branch_id, status, desired_from, desired_to)` | ⏳ | | | ۴.۳ | `idx_no_show_patient (patient_record_id, recorded_at)` | ⏳ | کوئری پنجرهٔ ۱۲ ماه | | ۴.۴ | `risk_tag_uuid` بدون FK (الگوی `DiscountRule.target_tag_uuid`) | ⏳ | | | ۴.۵ | `app:cancellation:seed-default-policy` — محافظه‌کار | ⏳ | | | ۴.۶ | `TenantSchemaCoverageTest` سبز | ⏳ | | ## ۵. UI | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | `CancellationPolicyPage` — سیاست محیط + جدول override سرویس‌ها | ⏳ | | | ۵.۲ | `WaitlistPage` — لیست + تب «قابل تطبیق» | ⏳ | | | ۵.۳ | دکمهٔ لغو → `ConfirmDialog` با محتوای **preview** | ⏳ | ⭐ نه لغو بعد جریمه | | ۵.۴ | نشان «پرریسک» + شمارش عدم حضور در `PatientDetailPage` | ⏳ | | | ۵.۵ | `ConfirmDialog` موجود استفاده شد، مودال دست‌ساز نه | ⏳ | | | ۵.۶ | `DataTable` با فیلتر بازه/سرویس در URL | ⏳ | | | ۵.۷ | تاریخ‌ها شمسی · مبالغ با `formatRial` | ⏳ | | | ۵.۸ | `backTo`/`BackButton` روی زیرصفحه‌ها | ⏳ | | | ۵.۹ | هیچ رنگ/شعاع hard-code — نشان پرریسک از `--danger-bg` | ⏳ | | | ۵.۱۰ | دارک‌مود و حالت فشرده | ⏳ | | | ۵.۱۱ | RTL و موبایل | ⏳ | | | ۵.۱۲ | همهٔ رشته‌ها فارسی | ⏳ | | | ۵.۱۳ | `ReserveAppointmentsPage` موجود دست‌نخورده ماند | ⏳ | ادغام خارج از دامنه | ## ۶. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | `PenaltyCalculatorTest` — پنج حالت شامل «کلینیک همیشه صفر» | ⏳ | ⭐ | | ۶.۲ | `CancellationServiceTest` — آزادسازی، `recorded_entity`، idempotent، گذشته ۴۲۲ | ⏳ | | | ۶.۳ | `PolicyResolverTest` — اولویت سرویس | ⏳ | | | ۶.۴ | `NoShowTrackerTest` — سوم برچسب، قدیمی‌تر از ۱۲ ماه نه، دوبار یک رکورد | ⏳ | | | ۶.۵ | `NoShowTrackerTest` — بیمار پرریسک **رزرو موفق** دارد | ⏳ | ⭐ | | ۶.۶ | `WaitlistMatcherTest` — سقف ۱۰، ترتیب، فیلتر روزبخش، `notify_count` | ⏳ | | | ۶.۷ | `WaitlistConversionTest` | ⏳ | | | ۶.۸ | `WaitlistAsyncTest` — شکست پیامک لغو را rollback نمی‌کند | ⏳ | ⭐ | | ۶.۹ | `PatientWalletTenantTest` موجود سبز ماند | ⏳ | ⭐ | | ۶.۱۰ | `CourseLifecycleTest` موجود — سیاست اعتبار اعمال شد | ⏳ | | ## ۷. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۷.۱ | `docs/api/cancellation.md` — preview اجباری، کلینیک بی‌جریمه | ⏳ | | | ۷.۲ | `docs/api/waitlist.md` — تصمیم broadcast و دلیلش | ⏳ | | ## ۸. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۸.۱ | هیچ 🔄 و ⏳ بی‌دلیل نمانده | ⏳ | | | ۸.۲ | `bin/phpunit` کامل سبز | ⏳ | | | ۸.۳ | `--group=slot-mode-frozen` سبز | ⏳ | | | ۸.۴ | `phpstan` بدون خطای جدید | ⏳ | | | ۸.۵ | `npx tsc --noEmit` و `yarn test` سبز | ⏳ | | | ۸.۶ | تست‌های tenant سبز | ⏳ | | | ۸.۷ | `docs/api/*` به‌روز | ⏳ | | | ۸.۸ | چک‌لیست UI کامل | ⏳ | | | ۸.۹ | ⚠️ سایت باید preview لغو را نشان دهد → `nobat724_front` بررسی و تسک ثبت شد | ⏳ | ⭐ | | ۸.۱۰ | `clinic-pro-tauri` بررسی شد | ⏳ | | | ۸.۱۱ | commit، سپس `graphify update .` | ⏳ | | | ۸.۱۲ | موارد به‌تعویق با دلیل و تسک مقصد | ⏳ | |