Section 7 of the design document, and the reason the whole resource layer exists. A laser session is not one block: numbing cream (5 min, room + operator), waiting for it to work (30 min, room only), the laser itself (20 min, room + operator + device), aftercare (5 min, room + operator). Under the single-interval model the operator is locked for all 60 minutes while actually working 30 — half the capacity thrown away. AppointmentPlanBuilder turns (service, selected items, branch, patient) into a plan: segments with offsets, durations and resource requirements. It deliberately assigns no absolute time and no specific resource — that is the next task. This only produces the *shape* of the appointment. Segment duration comes from one of two sources. A fixed segment carries its own number; an item-driven one gets its duration from task 04's DurationCalculator, so "the laser itself" grows with two treated areas while "waiting for the cream" does not. One number could not have expressed that. Three contracts worth stating: - A service with no segment templates falls back to a single continuous segment requiring the doctor resource — exactly today's behaviour. Without it every existing service would have become unplannable overnight. - A segment with no requirements is valid: "waiting at home" consumes time but occupies nothing. - same_gender_as_patient with an unknown patient gender is a 422, not a silently dropped requirement. Dropping it quietly would route the patient to a resource the clinic said must not serve them. When no resource qualifies, the error names the role, the skill and the branch — "no female operator with the skill «Alexandrite laser» is available at «Central»" — rather than an empty result the caller has to interpret (section 10). occupancy_offset carries each requirement's setup/cleanup minutes for the availability engine. It is taken as the maximum across candidates, because the builder does not yet know which resource will be picked and under-reserving means the next appointment lands on top of the cleanup. 11 tests covering the document's reference example (offsets 0/5/35/55, total 60), item-driven scaling, the no-template fallback, all three gender-constraint outcomes, merging and both caps. 1186 tests overall. phpstan back at its 14-error baseline; slot-mode frozen contract green. The admin segments page is not built; the checklist records it with a target. The backend and preview endpoint are complete and consumable without it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.7 KiB
8.7 KiB
چکلیست — تسک ۰۵ (بخشهای نوبت و سازندهٔ برنامه)
وضعیت کلی: ⏳ شروع نشده · آخرین بازبینی: —
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
⏳ | |
| ۰.۲ | SlotCalculatorService دستنخورده |
⏳ | |
| ۰.۳ | سرویس بدون SegmentTemplate → یک بخش مجازی با منبع type=doctor |
⏳ | ⭐ سازگاری حالت service فعلی |
| ۰.۴ | هیچ جدولی برای «برنامهٔ ساختهشده» ساخته نشد | ⏳ | DTO درونحافظه؛ ذخیره کار تسک ۰۷ |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | SegmentTemplate · SegmentRequirement |
⏳ | |
| ۱.۲ | DTO های AppointmentPlan · PlannedSegment · PlannedRequirement (immutable) |
⏳ | |
| ۱.۳ | پنج سرویس جدا: Builder · Assembler · DurationResolver · RequirementResolver |
⏳ | نه یک کلاس بزرگ |
| ۱.۴ | build() تابع خالص است — بدون write، بدون cache، بدون time() |
⏳ | ⭐ تسک ۰۶ رویش حساب میکند |
| ۱.۵ | مرحلهٔ ۷ (policies->applyToPlan) از روز اول در امضا، حتی no-op |
⏳ | تسک ۰۹ پرش میکند |
| ۱.۶ | ادغام: count بیشینه، نه جمع |
⏳ | |
| ۱.۷ | offset_minutes نسبی است، نه timestamp مطلق |
⏳ | تسک ۰۶ برنامه را میلغزاند |
| ۱.۸ | occupancyStartOffset/occupancyEndOffset جدا از offset نمایشی |
⏳ | setup/cleanup |
| ۱.۹ | قید جنسیت بدون داده → ۴۲۲، نه نادیده گرفتن | ⏳ | ⭐ الزام جدی کلینیک زیبایی |
| ۱.۱۰ | constraints فهرست بسته؛ کلید ناشناخته → ۴۲۲ |
⏳ | |
| ۱.۱۱ | NoEligibleResourceException با پیام انسانی + meta |
⏳ | نقش، مهارت، شعبه |
| ۱.۱۲ | سه endpoint | ⏳ | |
| ۱.۱۳ | patient_facing_minutes جدا از total_minutes در پاسخ |
⏳ | بخشهای بدون حضور بیمار |
| ۱.۱۴ | سقفها اعمال شد: ۲۰ بخش · ۴۸۰ دقیقه · ۱۰ نیازمندی · ۲۰ آیتم | ⏳ | |
| ۱.۱۵ | app:segment:seed-templates --preset=beauty|dental|physio — dry-run |
⏳ | |
| ۱.۱۶ | TenantOwnershipChecker روی هر uuid از request |
⏳ |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | segment_templates · segment_requirements |
⏳ | |
| ۲.۲ | دقیقاً یکی از service_item_id/service_option_id — قید اپلیکیشنی |
⏳ | |
| ۲.۳ | دقیقاً یکی از fixed_minutes/duration_share |
⏳ | |
| ۲.۴ | جمع duration_share یک سرویس = ۱۰۰ (اعتبارسنجی هنگام ذخیره) |
⏳ | |
| ۲.۵ | required_skills بهصورت JSON با دلیل مکتوب |
⏳ | نه جدول واسط |
| ۲.۶ | segment_requirements در AGGREGATE_CHILDREN |
⏳ | |
| ۲.۷ | TenantSchemaCoverageTest سبز |
⏳ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | ServiceSegmentsPage بهعنوان زیرصفحهٔ ServiceDetailPage |
⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۲ | لیست بخشها با sequence عددی (بدون drag) |
⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۳ | نیازمندیها: نقش با SearchableSelect، مهارت با چیپ، اشغال با رادیو |
⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۴ | هر گزینهٔ نوع اشغال توضیح فارسی دارد | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۵ | نوار پیشنمایش زمانی با عرض متناسب مدت و آیکن منابع | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۶ | خط «اپراتور واقعاً درگیر: N دقیقه» زیر نوار | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۷ | backTo روی صفحه |
⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۸ | هیچ رنگ/شعاع hard-code — نوار پیشنمایش هم | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۹ | دارکمود و حالت فشرده | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۱۰ | RTL و موبایل — نوار پیشنمایش روی موبایل اسکرول افقی داخلی دارد | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۱۱ | همهٔ رشتهها فارسی | ⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
| ۳.۱۲ | meta خطای «هیچ منبعی» به لینک «افزودن منبع» تبدیل شد |
⏳ | UI این تسک ساخته نشد — بکاند و POST /appointment-plan/preview کاملاند و بدون UI مصرفشدنی. مقصد: پاس UI کاتالوگ و بخشها |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | SegmentAssemblerTest — ادغام، count بیشینه، غیر-mergeable |
⏳ | |
| ۴.۲ | SegmentDurationResolverTest — fixed ثابت، سهمی مقیاسپذیر، جمع = total |
⏳ | |
| ۴.۳ | AppointmentPlanBuilderTest — سناریوی کامل مستند (۰/۵/۳۵/۵۵، total=60) |
⏳ | |
| ۴.۴ | AppointmentPlanBuilderTest — قطعیت: دو build یکسان → خروجی یکسان |
⏳ | |
| ۴.۵ | سرویس بدون الگو → یک بخش با منبع doctor | ⏳ | ⭐ |
| ۴.۶ | RequirementResolverTest — مهارت، هیچ کاندید، قید جنسیت، محیط دیگر |
⏳ | |
| ۴.۷ | PlanLimitsTest — ۲۱ بخش و ۴۸۱ دقیقه → ۴۲۲ |
⏳ |
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/appointment-plan.md |
⏳ | |
| ۵.۲ | جدول سه حالت اشغال و تفاوت عملیشان | ⏳ | |
| ۵.۳ | تفاوت offset نمایشی با occupancy_offset |
⏳ | |
| ۵.۴ | مثال کامل خروجی preview |
⏳ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ⏳ | |
| ۶.۲ | bin/phpunit کامل سبز |
⏳ | |
| ۶.۳ | --group=slot-mode-frozen سبز |
⏳ | |
| ۶.۴ | phpstan بدون خطای جدید |
⏳ | |
| ۶.۵ | npx tsc --noEmit و yarn test سبز |
⏳ | |
| ۶.۶ | تستهای tenant سبز | ⏳ | |
| ۶.۷ | docs/api/* بهروز |
⏳ | |
| ۶.۸ | چکلیست UI کامل | ⏳ | |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ⏳ | preview هنوز مصرف نمیشود |
| ۶.۱۰ | commit، سپس graphify update . |
⏳ | |
| ۶.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ⏳ |