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>
This commit is contained in:
hamed
2026-08-01 14:22:22 +03:30
co-authored by Claude Opus 5
parent 92edd175cc
commit b3c331f0cb
20 changed files with 624 additions and 30 deletions
@@ -13,7 +13,7 @@
|---|---|---|---|
| ۰.۱ | `--group=slot-mode-frozen` سبز | ✅ | |
| ۰.۲ | `PatientSession` موجود دست‌نخورده | ✅ | «مراجعهٔ انجام‌شده» ≠ «جلسهٔ دوره»؛ هیچ فایلی از `src/Patient` تغییر نکرد |
| ۰.۳ | رویدادهای تسک ۰۷ بعد از commit منتشر می‌شوند | | تسک ۱۴ رویدادها را می‌سازد؛ فعلاً `book-all` هیچ رویدادی منتشر نمی‌کند، پس خطر «هشت پیامک در rollback» وجود ندارد |
| ۰.۳ | رویدادهای تسک ۰۷ بعد از commit منتشر می‌شوند | | صندوق خروجی تسک ۱۴ همین را تضمین می‌کند: `record()` فلاش نمی‌کند، پس rollbackِ `book-all` رویدادی جا نمی‌گذارد |
| ۰.۴ | `abandon` نوبت‌های `booked` را لغو نمی‌کند | ✅ | مستند شد؛ لغو ظرفیت باید تصمیم صریح باشد نه اثر جانبی |
## ۱. بک‌اند
@@ -79,8 +79,8 @@
|---|---|---|---|
| ۴.۱ | شروع دوره — ۸ جلسه، دورهٔ دوم ۴۲۲ با شناسهٔ دورهٔ موجود | ✅ | |
| ۴.۲ | snapshot پروتکل | ✅ | ⭐ |
| ۴.۳ | لنگر متحرک و نزدیک‌ترین به ایده‌آل | ⚠️ | لنگر پیشنهاد و محاسبهٔ افق تست شد؛ اجرای کامل `book-all` با منابع و ساعت کاری هنوز تست ندارد |
| ۴.۴ | شکست جلسهٔ N → rollback | | با ۴.۳ یک بسته است |
| ۴.۳ | لنگر متحرک و نزدیک‌ترین به ایده‌آل | ⚠️ | لنگر پیشنهاد، افق، و مسیر شکستِ `book-all` تست دارند؛ مسیر موفقِ چندجلسه‌ای هنوز نه |
| ۴.۴ | شکست جلسهٔ N → rollback | | ⭐ تقویم فقط یک‌روزه: جلسهٔ اول وقت پیدا می‌کند، دومی نه، و **هیچ** جلسه‌ای رزرو نمی‌ماند |
| ۴.۵ | سقف ۹۰ روز | ✅ | `testSessionsBeyondTheHorizonAreSkippedNotFailed` — جلسهٔ بیرون افق رد می‌شود، دوره دست‌نخورده می‌ماند |
| ۴.۶ | لنگر `completed` + هشدار عبور از max | ✅ | ⭐ |
| ۴.۷ | پیشرفت دوره | ✅ | «۳ از ۸» + `next_params` |