The last structural gap from task 05 was the third occupancy mode. It is passive: the resource is genuinely held — nobody else can take that room while the patient waits for the anaesthetic — but the time is not work done. It blocks exactly like exclusive; the difference is in the report, where without it a room that spends half its day waiting reads as fully utilised. The mode is validated, offered in the segment editor and carried through to the plan. Everything else that was still marked as a deviation is now recorded in docs/architecture/deviations.md, one row each, in the form "what the plan said / what was built / why". That includes the ones I would defend (five plan services collapsed into one builder that only build() calls; a Skill foreign key instead of a JSON array, because a deleted skill in JSON fails silently) and the ones that are simply facts about the product (service_option does not exist here, so a column for it would sit empty until someone read it as a bug). The i18n section says plainly that the product is single-language and describes the order to migrate in if that changes — a translation layer with one language is an indirection, not an abstraction. All sixteen checklists now read zero pending and zero unresolved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
10 KiB
10 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 |
| ۱.۳ | پنج سرویس جدا | ✅ | تصمیم ثبتشده در deviations.md — یک AppointmentPlanBuilder با متدهای خصوصی. تقسیم به Assembler/DurationResolver/RequirementResolver وقتی معنا دارد که هرکدام مصرفکنندهٔ مستقل داشته باشند؛ اینجا هر سه فقط از همین یک مسیر صدا زده میشوند |
| ۱.۴ | build() تابع خالص |
✅ | تصمیم ثبتشده در deviations.md — تا تسک ۰۸ خالص بود. تسک ۰۹ قوانین timing/resource را وصل کرد، پس حالا از دیتابیس میخواند. چیزی که تسک ۰۶ واقعاً به آن نیاز دارد — خروجی قطعی برای ورودی ثابت — هنوز برقرار است |
| ۱.۵ | قلاب سیاست از روز اول در امضا | ✅ | تسک ۰۹ همانجا پر شد؛ همان دلیلِ گذاشتنش |
| ۱.۶ | ادغام: count بیشینه |
✅ | ⭐ برنامه از الگوهای سرویس و آیتمهای انتخابشده ساخته میشود؛ همنامهای mergeable یک بار میآیند (طولانیترین میماند) و تعداد منبع بیشینه میشود |
| ۱.۷ | offset_minutes نسبی |
✅ | تسک ۰۶ برنامه را میلغزاند |
| ۱.۸ | اشغال جدا از offset نمایشی | ✅ | تصمیم ثبتشده در 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 — مفهوم service_option در این پیادهسازی وجود ندارد؛ بخشها فقط به ServiceItem بستهاند |
| ۲.۳ | یکی از fixed_minutes/duration_share |
✅ | تصمیم ثبتشده در deviations.md — مدل دیگری انتخاب شد: duration_source ∈ fixed|items. «مدت از آیتمها» همان نیاز واقعی («خود لیزر با دو ناحیه طولانیتر») را دقیقتر میپوشاند تا سهم درصدی |
| ۲.۴ | جمع duration_share = ۱۰۰ |
— | با مدل بالا موضوعیت ندارد |
| ۲.۵ | required_skills بهصورت JSON |
✅ | تصمیم ثبتشده در 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 — دو حالت مستند شد (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 (۱.۱۵) · تست قطعیت (۴.۴) |