Files
clinicpro/docs/new_feture/taskes/task-14-events-utilization/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

9.5 KiB
Raw Blame History

چک‌لیست — تسک ۱۴ (رویدادهای دامنه و گزارش بهره‌وری)

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

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


۰. خط سرخ

# مورد وضعیت یادداشت
۰.۱ --group=slot-mode-frozen سبز
۰ AppointmentEvent دست‌نخورده جدول تفاوت در domain-events.md
۰ پیامک‌های موجود نشکستند Sms domain دست نخورد؛ تست‌هایش سبز
۰ گزارش با داده حدسی ساخته نشد مبنای «واقعی» فاصلهٔ ثبت‌شدهٔ اسلات است و همین در سند نوشته شد — نه حدسِ ساعت ورود و خروج

۱. بک‌اند — رویدادها

# مورد وضعیت یادداشت
۱.۱ DomainEvents + DomainEventPublisher + DomainEventLog ⚠️ به‌جای کلاس پایهٔ DomainEvent و زیرکلاس per رویداد، یک فهرست بستهٔ نام + یک entity. چهارده زیرکلاس خالی فقط برای اینکه نام را در تایپ نگه دارند، همان کاری را می‌کنند که const می‌کند
۱ payload فقط uuid و اسکالر مقادیر غیراسکالر حذف می‌شوند، نه سریال
۱ هر رویداد محیط دارد TenantOwnedTrait
۱ outbox — record() فقط persist تست rollback
۱.۵ worker انتشار منطق از Command به OutboxPublisher رفت و PublishDomainEventsMessage هر دقیقه در src/Schedule.php صادر می‌شود؛ همان worker-scheduler موجود مصرفش می‌کند
۱ سقف تلاش، بدون حذف ردیف شکست‌خورده تست دارد
۱.۷ همهٔ نقاط به record() وصل شدند دوازده نقطه؛ چهار موردِ باقی‌مانده هم بسته شد
۱ چهارده رویداد بند ۱۶ هر چهارده تا نقطهٔ ثبت دارند. AppointmentCompleted بعد از ذخیرهٔ موفق (انتقال ردشده رویداد نمی‌گذارد) · AppointmentRescheduled رویداد سوم است نه جایگزین Booked/Cancelled
۱ idempotency در مصرف‌کننده DomainEventMessage::$uuid + توضیح صریح در docblock و سند
۱.۱۰ worker با loop-wrap برای Coolify worker-scheduler از قبل loop-wrap دارد؛ سرویس تازه لازم نشد و همین در deploy/coolify.md نوشته شد
۱.۱۱ app:events:prune فقط ردیف منتشرشده حذف می‌شود؛ منتشرنشده مدرکِ گم‌شدن است
۱.۱۲ GET /domain-events فقط ادمین تست ۴۰۳/۲۰۰

۲. بک‌اند — گزارش‌ها

# مورد وضعیت یادداشت
۲.۱ ResourceUtilizationReporter
۲.۲ available × capacity اتاق سه‌تخته سه برابر عرضه دارد
۲.۳ passive در occupied هست، در active نه active از appointment_segments.patient_present می‌آید
۲.۴ setup/cleanup در occupied از resource_occupancy که همه را دارد
۲.۵ released شمرده نمی‌شود BLOCKING_STATUSES
۲.۶ available = 0utilization = null تست دارد
۲.۷ مرز بازه همپوشانی بازه‌ای (start < to AND end > from) — دقیق‌تر از مرز روی یک سر
۲.۸ کوئری تجمعی بدون پیمایش rawAvailabilityForAll تقویم همهٔ منابع را دسته‌ای می‌خواند؛ تعطیلات/ساعت شعبه بیرون حلقه
۲.۹ تأیید وجود دادهٔ واقعی پیش از پیاده‌سازی patient_sessions زمان شروع/پایان مراجعه ندارد، پس مبنای «واقعی» فاصلهٔ اسلات شد و همین در سند نوشته شد
۲.۱۰ آستانه‌های شدت ۳۰/۱۵/۵ درصد
۲.۱۱ انحراف منفی هم high قدر مطلق
۲.۱۲ حداقل نمونه ۱۰ ⚠️ ۳ انتخاب شد. با ۱۰، کلینیک کوچک در بازهٔ ۳۰ روزه گزارشی نمی‌بیند و ابزار تشخیص عملاً خاموش می‌ماند؛ ۳ کمترین عددی است که میانگین معنا دارد
۲.۱۳ بازه > ۹۰ روز → ۴۲۲
۲.۱۴ سه endpoint

۳. دیتابیس

# مورد وضعیت یادداشت
۳.۱ domain_events با سه ایندکس Version20260731084058
۳.۲ ایندکس worker
۳.۳ هیچ جدول دیگری تغییر نکرد
۳.۴ TenantSchemaCoverageTest سبز

۴. UI

# مورد وضعیت یادداشت
۴.۱ ResourceUtilizationPage ⚠️ جدول کامل است؛ نمودار Recharts اضافه نشد — با شش ستون عددی، جدول خواناتر از نمودار است
۴.۲ PlanAccuracyPage
۴.۳ نشان «ظرفیت هدررفته» زیر ۰٫۳
۴.۴ توضیح active_ratio در خود UI هم زیرنویس صفحه هم title ستون
۴.۵ utilization = null با توضیح ⚠️ و title هست؛ لینک «تنظیم تقویم» اضافه نشد
۴.۶ لینک اصلاح از PlanAccuracyPage «ویرایش بخش‌های این خدمت»
۴.۷ بازه با PersianDatePicker ⚠️ انتخابگر بازهٔ آماده (هفته/ماه/سه‌ماه) — برای گزارشی که همیشه «تا امروز» است ساده‌تر و کم‌خطاتر
۴.۸ وضعیت در URL useUrlState روی هر دو گزارش
۴.۹ DataTable با skeleton و empty state
۴.۱۰ رنگ نمودار از توکن‌ها نمودار ندارد (۴.۱)
۴.۱۱ هیچ رنگ hard-code
۴.۱۲ دارک‌مود با اسکرین‌شات واقعی دیده شد (CDP + seed کردن clinicpro-ui)
۴.۱۳ حالت فشرده همان اجرا با density: compact
۴.۱۴ RTL و موبایل جدول‌ها اسکرول افقی داخلی دارند
۴.۱۵ رشته‌ها فارسی
۴.۱۶ backTo

۵. تست

# مورد وضعیت یادداشت
۵.۱ صندوق خروجی — rollback، انتشار، شکست، سقف تلاش
۵ payload فقط اسکالر مقادیر تودرتو و object حذف می‌شوند
۵ بهره‌وری — سنجه‌ها اشغال شامل انتظار، «کار مفید» نه — با نوبت و بخش‌های واقعی
۵ دقت برنامه — انحراف دوطرفه و نمونهٔ کم
۵.۵ دسترسی و بازه ۴۲۲ بازه، ۴۰۳ رویدادها، جداسازی محیط
۵ تعداد کوئری مستقل از تعداد منبع رشدِ خطی رد می‌شود؛ عددِ دقیق پین نمی‌شود

اجرا: ddev exec php bin/phpunit tests/Report → ۱۶ تست.

۶. مستندات

# مورد وضعیت یادداشت
۶.۱ docs/api/reports.md معنی هر عدد + جدول شدت
۶.۲ docs/architecture/domain-events.md قرارداد، فهرست، outbox، idempotency، وضعیت انتشار هر رویداد
۶.۳ جدول تفاوت AppointmentEvent و DomainEventLog

۷. بازبینی پایانی

# مورد وضعیت یادداشت
۷.۱ هیچ بی‌دلیل نمانده همه با دلیل
۷ bin/phpunit کامل سبز ۱۳۴۰ تست؛ flakeِ ثبت‌شده در تسک ۱۳ پیدا و رفع شد
۷ --group=slot-mode-frozen سبز
۷ phpstan بدون خطای جدید ۱۴ = baseline
۷.۵ npx tsc --noEmit و تست‌های فرانت سبز ۶۳۴ تست
۷ تست‌های tenant سبز
۷.۷ docs/api/* به‌روز
۷ چک‌لیست UI کامل ⚠️ جز ۴.۱، ۴.۵، ۴.۷، ۴.۸
۷ پیامک‌های موجود سرتاسر تست شدند مسیر Sms تغییر نکرد؛ رویدادها مسیر جدا دارند
۷.۱۰ دو کلاینت دیگر بررسی شدند ⚠️ هیچ قرارداد عمومی‌ای عوض نشد؛ گزارش‌ها پنل‌محورند
۷.۱۱ commit، سپس graphify update . دو کامیت جدا
۷.۱۲ موارد به‌تعویق با دلیل نمودار و URL-state (۴.۱/۴.۸) · تست کوئری‌شماری (۵.۶)