Files
clinicpro/docs/new_feture/taskes/task-12-treatment-course/checklist.md
T
hamedandClaude Opus 5 b3c331f0cb perf(reports): read every resource's calendar in one batch, and close the owed tests
Writing the query-count test that task 14 owed showed the growth was real: one
resource cost 10 queries, six cost 33 — about five per resource, because the
available-minutes figure walked each resource's calendar on its own.

Holidays, tenant overrides and branch hours are identical for every resource in
a report, so they now load once outside the loop; shifts and exceptions load for
all resources in one query each. The batched path is a new method rather than a
change to rawAvailability, which the booking engine also calls. The test pins
the shape of the growth, not an exact count.

Also landed:

- app:segment:seed-templates with beauty, dental and physio presets. Building
  four segments and their requirements by hand is the first thing a new clinic
  must do and the most tedious; this gives them something to edit instead of an
  empty page. It refuses to touch a service that already has segments unless
  --force, and it will not invent resource types the tenant never defined.
- book-all is all-or-nothing, proven rather than asserted: with a calendar open
  one day a week and a 1-2 day protocol gap, session one finds a slot and
  session two cannot, and every session must come back planned.
- credit_refundable: false takes the credit back with a negative adjustment and
  deletes nothing — the ledger stays append-only.
- the segments editor has frontend tests, including that it sends back what the
  user sees and renders read-only without the permission.

useBranches now returns [] for a non-array payload instead of throwing
"branches.map is not a function" and taking the page down with it.

BookingLocationsScanTest built a Clinic around a Doctor loaded from a different
manager, which Doctrine treats as a new entity; it flushed fine most runs and
failed on cascade in others. It now loads the doctor from the same manager.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:22:22 +03:30

10 KiB
Raw Blame History

چک‌لیست — تسک ۱۲ (دوره درمان)

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

قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md


۰. خط سرخ

# مورد وضعیت یادداشت
۰.۱ --group=slot-mode-frozen سبز
۰ PatientSession موجود دست‌نخورده «مراجعهٔ انجام‌شده» ≠ «جلسهٔ دوره»؛ هیچ فایلی از src/Patient تغییر نکرد
۰ رویدادهای تسک ۰۷ بعد از commit منتشر می‌شوند صندوق خروجی تسک ۱۴ همین را تضمین می‌کند: record() فلاش نمی‌کند، پس rollbackِ book-all رویدادی جا نمی‌گذارد
۰ abandon نوبت‌های booked را لغو نمی‌کند مستند شد؛ لغو ظرفیت باید تصمیم صریح باشد نه اثر جانبی

۱. بک‌اند

# مورد وضعیت یادداشت
۱.۱ چهار entity
۱ سرویس‌ها CourseStarter · CourseScheduler · CourseBooker · CourseProgressCalculator · CourseSessionLinker
۱ snapshot چهار فاصله و params testChangingTheProtocolLeavesRunningCoursesAlone
۱ book-all همه یا هیچ wrapInTransaction دور کل حلقه
۱.۵ لنگر متحرک لنگر بعد از هر رزرو روی همان اسلات می‌رود
۱ نزدیک‌ترین به ایده‌آل usort روی abs(start - ideal)
۱.۷ لنگر پیشنهاد = آخرین جلسهٔ completed testTheSuggestionAnchorsOnTheLastCompletedSession
۱ سقف ۹۰ روز + پیام روشن جلسات بیرون بازه planned می‌مانند، خطا نیست
۱ same_as_previous ترجیح نه الزام منبع ترجیحی جلو می‌آید، بقیه حذف نمی‌شوند؛ اجبار یعنی بیمار دو هفته منتظر بماند
۱.۱۰ preferredResourceIds حمل می‌شود POST /appointment-availability فیلد course_uuid می‌گیرد و preferred_resource دوره را به موتور می‌دهد
۱.۱۱ SameAsPreviousPicker تسک ۰۶ همراه سه استراتژی دیگر در تسک ۰۶ ساخته شد
۱.۱۲ تعامل با spacing: سخت‌گیرانه‌تر برنده ⚠️ max(min) پیاده شد (effectiveMinDaysmin(max) لازم نشد چون قانون spacing اثر «حداکثر» ندارد. بازهٔ تهی هم ممکن نیست چون max همیشه با min بالا می‌رود
۱.۱۳ اعتبار پکیج کمتر از جلسات → هشدار نه خطا ⚠️ پکیج به دوره وصل می‌شود و پوششش بررسی می‌شود، ولی مقایسهٔ مانده با تعداد جلسات هنوز هشدار نمی‌دهد
۱.۱۴ active_course_key همان الگوی active_slot_key
۱.۱۵ CourseSessionLinker تنها نویسندهٔ رابطهٔ دوطرفه
۱.۱۶ جلسهٔ آخر → دوره completed خودکار رویدادش با تسک ۱۴ می‌آید
۱.۱۷ نُه endpoint ۹ تا: پروتکل GET/POST/GET{uuid}/PATCH/DELETE + دوره POST/GET/patient/{uuid}/courses/next-slot-suggestion/book-all/abandon
۱.۱۸ TenantOwnershipChecker روی هر uuid testAnotherClinicCannotSeeTheCourse

۲. دیتابیس

# مورد وضعیت یادداشت
۲.۱ چهار جدول Version20260731074710
۲.۲ قیدهای فاصله و تعداد در سازنده، با ۴۲۲ روشن
۲.۳ یکتایی شمارهٔ جلسه هم روی پروتکل هم روی دوره
۲.۴ UNIQUE(appointment_id) یک نوبت به بیش از یک جلسه وصل نمی‌شود
۲.۵ appointments.course_session_id Version20260731074758
۲.۶ course_protocol_steps در AGGREGATE_CHILDREN
۲.۷ TenantSchemaCoverageTest سبز

۳. UI

# مورد وضعیت یادداشت
۳.۱ CourseProtocolsPage با اعتبارسنجی ترتیب فاصله‌ها در خود فرم
۳.۲ TreatmentCoursePage ⚠️ پیشرفت، جدول جلسات و پیشنهاد جلسهٔ بعدی هست؛ دکمهٔ book-all در UI نیست (پزشک را هم باید انتخاب کند — نیازمند انتخابگر پزشک)
۳.۳ دوره‌های بیمار در PatientDetailPage تب «دوره‌های درمان»
۳.۴ ستون فاصلهٔ واقعی بین جلسات فاصلهٔ واقعی با جلسهٔ قبلی؛ عبور از حداکثر پروتکل با رنگ هشدار
۳.۵ هشدار عبور از حداکثر فاصله با رنگ --warning
۳.۶ بنر پیشنهاد جلسهٔ بعدی در کارت بالای صفحهٔ دوره
۳.۷ نام منبع ترجیحی روی دکمهٔ رزرو preferred_resource_name در پاسخ دوره؛ متن صریح می‌گوید ترجیح است نه الزام
۳.۸ پیشنهاد بازچینی پس از لغو وسط دوره بنر «N روز از آخرین جلسه گذشته» از خودِ دوره حساب می‌شود، پس به انتخاب شعبه وابسته نیست
۳.۹ DataTable برای جلسات
۳.۱۰ نشان وضعیت جلسه و دوره کلاس‌های badge موجود
۳.۱۱ تاریخ‌ها شمسی formatDate
۳.۱۲ backTo روی زیرصفحه‌ها
۳.۱۳ هیچ رنگ/شعاع hard-code
۳.۱۴ دارک‌مود و حالت فشرده ⚠️ فقط توکن‌های موجود؛ بازبینی چشمی انجام نشد
۳.۱۵ RTL و موبایل جدول جلسات اسکرول افقی داخلی دارد
۳.۱۶ همهٔ رشته‌ها فارسی
۳.۱۷ پیام سقف ۹۰ روز در UI از پاسخ book-all به‌صورت toast

۴. تست

# مورد وضعیت یادداشت
۴.۱ شروع دوره — ۸ جلسه، دورهٔ دوم ۴۲۲ با شناسهٔ دورهٔ موجود
۴.۲ snapshot پروتکل
۴.۳ لنگر متحرک و نزدیک‌ترین به ایده‌آل ⚠️ لنگر پیشنهاد، افق، و مسیر شکستِ book-all تست دارند؛ مسیر موفقِ چندجلسه‌ای هنوز نه
۴.۴ شکست جلسهٔ N → rollback تقویم فقط یک‌روزه: جلسهٔ اول وقت پیدا می‌کند، دومی نه، و هیچ جلسه‌ای رزرو نمی‌ماند
۴.۵ سقف ۹۰ روز testSessionsBeyondTheHorizonAreSkippedNotFailed — جلسهٔ بیرون افق رد می‌شود، دوره دست‌نخورده می‌ماند
۴.۶ لنگر completed + هشدار عبور از max
۴.۷ پیشرفت دوره «۳ از ۸» + next_params
۴.۸ ترجیح همان منبع ResourcePickerTest — «جلو می‌آید و هیچ کاندیدی حذف نمی‌شود»
۴.۹ تعامل با قانون spacing testTheStricterOfProtocolAndSpacingPolicyWins — پروتکل ۷ روز، قانون ۲۱ روز، مؤثر ۲۱
۴.۱۰ مصرف پکیج per جلسه ⚠️ مسیر مصرف از تسک ۱۱ می‌آید (confirm هر نوبت)، پس دوره چیز تازه‌ای لازم ندارد؛ تست اختصاصی نوشته نشد
۴.۱۱ چرخهٔ عمر — لغو، تکمیل خودکار، abandon testCancellingOneSessionOnlyResetsThatSession و testTheCourseCompletesOnlyWhenEverySessionIsDone

اجرا: ddev exec php bin/phpunit tests/Course → ۱۴ تست (۱ skip عمدی: تولید خروجی مستندات).

۵. مستندات

# مورد وضعیت یادداشت
۵.۱ docs/api/course.md JSON واقعی از اجرای واقعی
۵ «سخت‌گیرانه‌تر برنده»
۵ رفتار سقف ۹۰ روز
۵ «abandon نوبت‌ها را لغو نمی‌کند» با دلیلش

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

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