# چک‌لیست — تسک ۰۵ (بخش‌های نوبت و سازندهٔ برنامه)

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

> این چک‌لیست تا امروز روی «شروع نشده» مانده بود در حالی که کد تسک از همان روز ساخته و
> کامیت شده بود — خطای پیگیری، نه خطای پیاده‌سازی. حالا با وضعیت واقعی پر شده.

قواعد: [_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` سبز | ✅ | |
| ۰.۲ | `SlotCalculatorService` دست‌نخورده | ✅ | |
| ۰.۳ | سرویس بدون الگو → یک بخش با منبع `doctor` | ✅ | ⭐ `singleSegment()` — سازگاری کامل با رفتار امروز |
| ۰.۴ | هیچ جدولی برای «برنامهٔ ساخته‌شده» نیست | ✅ | فقط DTO درون‌حافظه؛ ذخیره کار تسک ۰۷ شد |

## ۱. بک‌اند

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | `SegmentTemplate` · `SegmentRequirement` | ✅ | |
| ۱.۲ | DTO های `AppointmentPlan` · `PlannedSegment` · `PlannedRequirement` | ✅ | `readonly` |
| ۱.۳ | پنج سرویس جدا | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — یک `AppointmentPlanBuilder` با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده می‌شوند |
| ۱.۴ | `build()` تابع خالص | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین `timing`/`resource` را وصل کرد، پس حالا از دیتابیس می‌خواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است |
| ۱.۵ | قلاب سیاست از روز اول در امضا | ✅ | تسک ۰۹ همان‌جا پر شد؛ همان دلیلِ گذاشتنش |
| ۱.۶ | ادغام: `count` بیشینه | ✅ | ⭐ برنامه از الگوهای سرویس **و آیتم‌های انتخاب‌شده** ساخته می‌شود؛ هم‌نام‌های `mergeable` یک بار می‌آیند (طولانی‌ترین می‌ماند) و تعداد منبع بیشینه می‌شود |
| ۱.۷ | `offset_minutes` نسبی | ✅ | تسک ۰۶ برنامه را می‌لغزاند |
| ۱.۸ | اشغال جدا از offset نمایشی | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — `setup/cleanup` روی `PlannedRequirement` است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را می‌خواند |
| ۱.۹ | قید جنسیت بدون داده → ۴۲۲ | ✅ | ⭐ نادیده گرفته نمی‌شود |
| ۱.۱۰ | `constraints` فهرست بسته | ✅ | کلید ناشناخته ۴۲۲ می‌گیرد و **پیش از حذف** سنجیده می‌شود؛ فعلاً فقط `same_gender_as_patient` اثر دارد می‌رود |
| ۱.۱۱ | خطای «هیچ منبعی» با پیام انسانی | ✅ | `explainMissing()` — نقش، مهارت و شعبه در متن؛ `meta` ساختاریافته ندارد |
| ۱.۱۲ | سه endpoint | ✅ | `GET/PUT segments` + `POST appointment-plan/preview` |
| ۱.۱۳ | `patient_facing_minutes` در پاسخ | ✅ | یک‌جا در بک‌اند حساب می‌شود تا هر کلاینت خودش جمع نزند |
| ۱.۱۴ | سقف‌ها | ✅ | ۴۸۰ دقیقه · ۲۰ بخش · ۱۰ نیازمندی per بخش. سقف آیتم لازم نشد: انتخاب آیتم از خودِ گروه‌های تسک ۰۴ محدود می‌شود |
| ۱.۱۵ | `app:segment:seed-templates` | ✅ | سه الگو (`beauty`/`dental`/`physio`)؛ بدون `--force` بازنویسی نمی‌کند |
| ۱.۱۶ | `TenantOwnershipChecker` روی هر uuid | ✅ | |

## ۲. دیتابیس

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | `segment_templates` · `segment_requirements` | ✅ | |
| ۲.۲ | یکی از `service_item_id`/`service_option_id` | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — مفهوم `service_option` در این پیاده‌سازی وجود ندارد؛ بخش‌ها فقط به `ServiceItem` بسته‌اند |
| ۲.۳ | یکی از `fixed_minutes`/`duration_share` | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — مدل دیگری انتخاب شد: `duration_source` ∈ `fixed`\|`items`. «مدت از آیتم‌ها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانی‌تر») را دقیق‌تر می‌پوشاند تا سهم درصدی |
| ۲.۴ | جمع `duration_share` = ۱۰۰ | — | با مدل بالا موضوعیت ندارد |
| ۲.۵ | `required_skills` به‌صورت JSON | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — یک `Skill` تک با FK. چند مهارت هم‌زمان نیاز واقعی نداشت و FK اعتبار ارجاعی می‌دهد که JSON نمی‌دهد |
| ۲.۶ | `segment_requirements` در `AGGREGATE_CHILDREN` | ✅ | |
| ۲.۷ | `TenantSchemaCoverageTest` سبز | ✅ | |

## ۳. UI

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | ویرایشگر بخش‌ها در `ServiceDetailPage` | ✅ | تب «بخش‌های نوبت» — `components/ServiceSegmentsTab.tsx` |
| ۳.۲ | لیست بخش‌ها با `sequence` عددی | ✅ | بدون drag، طبق قرارداد |
| ۳.۳ | نیازمندی‌ها با `SearchableSelect` | ✅ | نقش، تعداد، نوع اشغال، قید جنسیت |
| ۳.۴ | هر گزینهٔ نوع اشغال توضیح فارسی دارد | ✅ | «انحصاری — منبع کامل قفل می‌شود» / «اشتراکی — از ظرفیت یکی کم می‌شود» |
| ۳.۵ | نوار پیش‌نمایش با عرض متناسب مدت | ✅ | ⭐ `flex: duration` — بخش سی‌دقیقه‌ای شش برابر پنج‌دقیقه‌ای |
| ۳.۶ | خط «بیمار واقعاً درگیر: N دقیقه» | ✅ | کنار مدت کل |
| ۳.۷ | `backTo` روی صفحه | ✅ | از `ServiceDetailPage` می‌آید |
| ۳.۸ | هیچ رنگ/شعاع hard-code | ✅ | نوار هم با `--primary-soft`/`--surface-2` |
| ۳.۹ | دارک‌مود و حالت فشرده | ✅ | اسکرین‌شات واقعی در دارک‌مود و حالت فشرده؛ ایرادی نماند |
| ۳.۱۰ | RTL و موبایل — اسکرول افقی نوار | ✅ | `overflow-x: auto` با `min-width` |
| ۳.۱۱ | همهٔ رشته‌ها فارسی | ✅ | |
| ۳.۱۲ | خطای «هیچ منبعی» به لینک «افزودن منبع» تبدیل شد | ✅ | ⭐ خطای بدون راه اصلاح، بن‌بست است |

## ۴. تست

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | ادغام بخش‌ها | ✅ | دو تست: ادغام هم‌نام دو ناحیه · بیشینه‌بودن تعداد |
| ۴.۲ | حل مدت — ثابت و از آیتم‌ها | ✅ | داخل `AppointmentPlanTest` |
| ۴.۳ | سناریوی مرجع مستند | ✅ | ⭐ آفست‌های ۰/۵/۳۵/۵۵ و مجموع ۶۰ |
| ۴.۴ | قطعیت — دو build یکسان | ✅ | مقایسهٔ JSON دو `preview` پیاپی |
| ۴.۵ | سرویس بدون الگو | ✅ | ⭐ |
| ۴.۶ | حل نیازمندی — مهارت، بی‌کاندید، جنسیت، محیط دیگر | ✅ | |
| ۴.۷ | سقف‌ها → ۴۲۲ | ✅ | ۴۸۰ دقیقه و سقف تعداد بخش هر دو تست دارند |
| ۴.۸ | تست فرانت ویرایشگر بخش‌ها | ✅ | بارگذاری، ذخیرهٔ همان چیزی که کاربر می‌بیند، و حالت فقط‌خواندنی |

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

## ۵. مستندات

| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | `docs/api/appointment-plan.md` | ✅ | |
| ۵.۲ | جدول حالت‌های اشغال | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — دو حالت مستند شد (`exclusive`/`shared`)؛ حالت سوم ساخته نشد |
| ۵.۳ | تفاوت offset نمایشی و اشغال | ✅ | `occupancy_offset` و دلیل محافظه‌کاری‌اش |
| ۵.۴ | مثال کامل خروجی `preview` | ✅ | |

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

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