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>
Three gaps on the treatment-course page, all of them about the difference
between the protocol and reality.
The sessions table listed each date but not the gap between them, leaving the
operator to subtract two Jalali dates in their head. It now shows the real gap
and colours it as a warning past the protocol maximum.
A course cancelled mid-way stretches silently: the session goes back to
planned and nobody is told. The suggestion endpoint does warn, but only once a
branch is picked, so the warning could go unseen indefinitely. The page now
derives "N days since the last session, past the protocol maximum" from the
course itself, so it shows immediately.
The course's preferred resource was applied by the engine but never named in
the UI. The API now returns preferred_resource_name alongside the uuid, and
the text says plainly that it is a preference — the engine moves it up the
list, it does not hold the slot.
Two backend tests that were owed: the stricter of the protocol spacing and a
spacing policy wins (protocol 7 days, policy 21, effective 21 — otherwise a
clinic's safety rule could be bypassed by writing a short protocol), and a
session whose earliest possible date falls outside the 90-day horizon is
skipped rather than failing book-all, leaving the course untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Laser is six to eight sessions; the previous design only knew single
appointments, which is the exception rather than the rule.
- CourseProtocol per service: session count and three distinct spacings —
min is the earliest that is clinically allowed, ideal is best, max is where
the course starts losing its effect
- Starting a course creates every session up front as `planned` and copies the
protocol's numbers and per-session params, so changing the protocol tomorrow
leaves a running course alone
- Suggestions anchor on the last *completed* session, not the course start:
when session 2 slips, session 3 moves with it
- Slots are ranked by distance from ideal, not by earliest available — day 21
is worse than day 27 when 28 is the target
- book-all is all-or-nothing inside one transaction, with a moving anchor and a
90-day horizon; sessions past the horizon stay planned and are reported, not
treated as failures
- The effective minimum is the stricter of the protocol and the task-09 spacing
policy, so a clinic rule never fights the protocol
- Cancelling one session returns only that session to planned; abandoning a
course does not cancel its appointments, which stays an explicit decision
One active course per (patient, service) via active_course_key, the same
partial-uniqueness trick as Appointment::activeSlotKey.
Admin: CourseProtocolsPage, TreatmentCoursePage and a courses tab on the
patient record.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>