# چک‌لیست — تسک ۰۵ (بخش‌های نوبت و سازندهٔ برنامه) **وضعیت کلی:** ✅ تمام‌شده با انحراف‌های ثبت‌شده · **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۹ > این چک‌لیست تا امروز روی «شروع نشده» مانده بود در حالی که کد تسک از همان روز ساخته و > کامیت شده بود — خطای پیگیری، نه خطای پیاده‌سازی. حالا با وضعیت واقعی پر شده. قواعد: [_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` | | ۱.۳ | پنج سرویس جدا | ⚠️ | یک `AppointmentPlanBuilder` با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده می‌شوند | | ۱.۴ | `build()` تابع خالص | ⚠️ | تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین `timing`/`resource` را وصل کرد، پس حالا از دیتابیس می‌خواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است | | ۱.۵ | قلاب سیاست از روز اول در امضا | ✅ | تسک ۰۹ همان‌جا پر شد؛ همان دلیلِ گذاشتنش | | ۱.۶ | ادغام: `count` بیشینه | ✅ | ⭐ برنامه از الگوهای سرویس **و آیتم‌های انتخاب‌شده** ساخته می‌شود؛ هم‌نام‌های `mergeable` یک بار می‌آیند (طولانی‌ترین می‌ماند) و تعداد منبع بیشینه می‌شود | | ۱.۷ | `offset_minutes` نسبی | ✅ | تسک ۰۶ برنامه را می‌لغزاند | | ۱.۸ | اشغال جدا از offset نمایشی | ⚠️ | `setup/cleanup` روی `PlannedRequirement` است (بیشینهٔ کاندیدها) نه دو offset جدا؛ اثر عملی یکی است و تسک ۰۷ همان را می‌خواند | | ۱.۹ | قید جنسیت بدون داده → ۴۲۲ | ✅ | ⭐ نادیده گرفته نمی‌شود | | ۱.۱۰ | `constraints` فهرست بسته | ⚠️ | فقط `same_gender_as_patient` اثر دارد؛ کلید ناشناخته ۴۲۲ نمی‌گیرد و بی‌صدا نادیده می‌رود | | ۱.۱۱ | خطای «هیچ منبعی» با پیام انسانی | ✅ | `explainMissing()` — نقش، مهارت و شعبه در متن؛ `meta` ساختاریافته ندارد | | ۱.۱۲ | سه endpoint | ✅ | `GET/PUT segments` + `POST appointment-plan/preview` | | ۱.۱۳ | `patient_facing_minutes` در پاسخ | ⚠️ | در API نیست؛ UI از `patient_present` هر بخش خودش جمع می‌زند | | ۱.۱۴ | سقف‌ها | ⚠️ | ۴۸۰ دقیقه اعمال می‌شود؛ سقف ۲۰ بخش / ۱۰ نیازمندی / ۲۰ آیتم اعمال نشد | | ۱.۱۵ | `app:segment:seed-templates` | ✅ | سه الگو (`beauty`/`dental`/`physio`)؛ بدون `--force` بازنویسی نمی‌کند | | ۱.۱۶ | `TenantOwnershipChecker` روی هر uuid | ✅ | | ## ۲. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | `segment_templates` · `segment_requirements` | ✅ | | | ۲.۲ | یکی از `service_item_id`/`service_option_id` | ⚠️ | مفهوم `service_option` در این پیاده‌سازی وجود ندارد؛ بخش‌ها فقط به `ServiceItem` بسته‌اند | | ۲.۳ | یکی از `fixed_minutes`/`duration_share` | ⚠️ | مدل دیگری انتخاب شد: `duration_source` ∈ `fixed`\|`items`. «مدت از آیتم‌ها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانی‌تر») را دقیق‌تر می‌پوشاند تا سهم درصدی | | ۲.۴ | جمع `duration_share` = ۱۰۰ | — | با مدل بالا موضوعیت ندارد | | ۲.۵ | `required_skills` به‌صورت JSON | ⚠️ | یک `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` | ✅ | | | ۵.۲ | جدول حالت‌های اشغال | ⚠️ | دو حالت مستند شد (`exclusive`/`shared`)؛ حالت سوم ساخته نشد | | ۵.۳ | تفاوت offset نمایشی و اشغال | ✅ | `occupancy_offset` و دلیل محافظه‌کاری‌اش | | ۵.۴ | مثال کامل خروجی `preview` | ✅ | | ## ۶. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | هیچ ⏳ بی‌دلیل نمانده | ✅ | ۵ مورد با دلیل | | ۶.۲ | `bin/phpunit` کامل سبز | ✅ | | | ۶.۳ | `--group=slot-mode-frozen` سبز | ✅ | | | ۶.۴ | `phpstan` بدون خطای جدید | ✅ | baseline ۱۴ | | ۶.۵ | `npx tsc --noEmit` و تست‌های فرانت سبز | ✅ | ۶۳۷ تست | | ۶.۶ | تست‌های tenant سبز | ✅ | | | ۶.۷ | `docs/api/*` به‌روز | ✅ | | | ۶.۸ | چک‌لیست UI کامل | ✅ | جز ۳.۹ | | ۶.۹ | دو کلاینت دیگر بررسی شدند | ⚠️ | `preview` هنوز در `nobat724_front` مصرف نمی‌شود | | ۶.۱۰ | commit، سپس `graphify update .` | ✅ | | | ۶.۱۱ | موارد به‌تعویق با دلیل | ✅ | ادغام بخش‌ها (۱.۶/۴.۱) · سقف‌های فرعی (۱.۱۴) · seed (۱.۱۵) · تست قطعیت (۴.۴) |