Commit Graph
8 Commits
Author SHA1 Message Date
hamedandClaude Opus 5 1972fdd20f Document the resource-first model and retire the deleted tasks' checklists
docs/architecture/resource-first-model.md describes the shape: the three
entities, why an option is a ServiceItem rather than a fourth table, the
four-level resolution chain, the two conditions on the eligibility filter and
what each of them prevented, and why containment is a graph beside the display
tree rather than the tree itself.

docs/api/resource.md gains both offering endpoints with the response captured
from a real call, including a row where the price comes from the branch and one
where it comes from the resource — the two cases the *_source fields exist for.
docs/api/appointment.md documents resource_uuid, the doctor inference, and the
nullable resource/service_option in the response.

The checklists for tasks 9 to 14 keep their rows but open with a banner saying
the task was removed, when, by whose decision, and which commit to revert. They
are history now; deleting them would erase the record of work that shipped and
was then withdrawn.

Verified end to end: 1304 tests, slot-mode-frozen green, phpstan at 14, tsc
clean, 648 panel tests, and app:seed-scenarios --reset builds all three
environments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 23:05:33 +03:30
hamedandClaude Opus 5 e5b74ebab4 docs: settle every remaining row, and add the third occupancy mode
The last structural gap from task 05 was the third occupancy mode. It is
passive: the resource is genuinely held — nobody else can take that room while
the patient waits for the anaesthetic — but the time is not work done. It
blocks exactly like exclusive; the difference is in the report, where without
it a room that spends half its day waiting reads as fully utilised. The mode is
validated, offered in the segment editor and carried through to the plan.

Everything else that was still marked as a deviation is now recorded in
docs/architecture/deviations.md, one row each, in the form "what the plan said
/ what was built / why". That includes the ones I would defend (five plan
services collapsed into one builder that only build() calls; a Skill foreign
key instead of a JSON array, because a deleted skill in JSON fails silently)
and the ones that are simply facts about the product (service_option does not
exist here, so a column for it would sit empty until someone read it as a bug).

The i18n section says plainly that the product is single-language and describes
the order to migrate in if that changes — a translation layer with one language
is an indirection, not an abstraction.

All sixteen checklists now read zero pending and zero unresolved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 16:40:55 +03:30
hamedandClaude Opus 5 ca2f9b8652 feat(admin): build the last two screens, and pin spacing with a test
The cancellation policy page showed only the tenant policy, so nothing said
which services opt out of it. Service policies do not blend with the tenant one
— a service that has its own follows it completely — and without the table an
operator cannot tell why one service's penalty differs. It lists them with a
link to each service.

The waitlist had the matches endpoint and no way to reach it. The list answers
"who is waiting"; the question asked when capacity frees up is "who is waiting
for this slot", so the page now takes a service and a date and answers that.
The note says plainly that cancelling notifies them anyway — this is for
looking before deciding, not a second notification path.

Spacing is enforced at hold time rather than during candidate generation, which
costs one slot being shown and then refused, and saves a patient-history query
per candidate. That trade had no test; now a booking five days after the last
one is refused and one thirty days later goes through.

Checklists across all sixteen tasks are final: no pending rows, and the
warnings that remain are recorded decisions — one resolver instead of six
engines, a closed list instead of a registry, sample size three instead of ten
— each with the reason it was taken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 15:32:58 +03:30
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
hamedandClaude Opus 5 e9e61adfee feat(course): show how the course is actually going, not just how it was planned
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>
2026-08-01 13:51:34 +03:30
hamedandClaude Opus 5 a8020e3c20 docs: reconcile checklists with what the last passes actually built
Rows closed by the resource-strategy and blocking work: task 06's picker
interface and four strategies, task 07's ad-hoc blocking and 409 recovery, and
task 12's same-as-previous preference, which had been blocked on task 06's
missing strategies since it was written.

Tasks 13 and 14 both carried the flaky-suite caveat against their final review;
that flake has a diagnosed cause and a fix, so both now say so instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 20:53:02 +03:30
hamedandClaude Opus 5 fc504f4415 feat(course): treatment courses with protocol-driven session planning
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>
2026-07-31 11:33:07 +03:30
hamed 70739691d1 Add checklists for tasks 11 to 14 covering credit ledger, treatment course, cancellation policies, and event utilization
- Created checklist for task 11: Package and Credit Ledger
- Created checklist for task 12: Treatment Course
- Created checklist for task 13: Cancellation Policy, No-Show, and Waitlist
- Created checklist for task 14: Domain Events and Utilization Reports
2026-07-30 12:12:45 +03:30