The cancellation policy page showed only the tenant policy, so nothing said which services opt out of it. Service policies do not blend with the tenant one — a service that has its own follows it completely — and without the table an operator cannot tell why one service's penalty differs. It lists them with a link to each service. The waitlist had the matches endpoint and no way to reach it. The list answers "who is waiting"; the question asked when capacity frees up is "who is waiting for this slot", so the page now takes a service and a date and answers that. The note says plainly that cancelling notifies them anyway — this is for looking before deciding, not a second notification path. Spacing is enforced at hold time rather than during candidate generation, which costs one slot being shown and then refused, and saves a patient-history query per candidate. That trade had no test; now a booking five days after the last one is refused and one thirty days later goes through. Checklists across all sixteen tasks are final: no pending rows, and the warnings that remain are recorded decisions — one resolver instead of six engines, a closed list instead of a registry, sample size three instead of ten — each with the reason it was taken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.5 KiB
9.5 KiB
چکلیست — تسک ۰۵ (بخشهای نوبت و سازندهٔ برنامه)
وضعیت کلی: ✅ تمامشده با انحرافهای ثبتشده · آخرین بازبینی: ۱۴۰۵/۰۵/۰۹
این چکلیست تا امروز روی «شروع نشده» مانده بود در حالی که کد تسک از همان روز ساخته و کامیت شده بود — خطای پیگیری، نه خطای پیادهسازی. حالا با وضعیت واقعی پر شده.
قواعد: _shared/definition-of-done.md · red-lines.md · 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 در پاسخ |
✅ | یکجا در بکاند حساب میشود تا هر کلاینت خودش جمع نزند |
| ۱.۱۴ | سقفها | ✅ | ۴۸۰ دقیقه · ۲۰ بخش · ۱۰ نیازمندی per بخش. سقف آیتم لازم نشد: انتخاب آیتم از خودِ گروههای تسک ۰۴ محدود میشود |
| ۱.۱۵ | 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 کامل | ✅ | جز ۳.۹ |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ✅ | با graphify بررسی شدند؛ پیشنمایش قیمت پنلمحور است و هیچ کلاینتی مصرفش نمیکند |
| ۶.۱۰ | commit، سپس graphify update . |
✅ | |
| ۶.۱۱ | موارد بهتعویق با دلیل | ✅ | ادغام بخشها (۱.۶/۴.۱) · سقفهای فرعی (۱.۱۴) · seed (۱.۱۵) · تست قطعیت (۴.۴) |