Show a recurring lesson's policy acceptances and intake answers on every week of it #168

Merged
thatguygriff merged 1 commits from fix/167-series-policy-acceptances into main 2026-07-30 19:41:03 +00:00
Owner

Closes #167. Found on a live deployment, on the studio's first real booking.

The bug

A weekly booking reserves a series of lessons, but the student answers the intake and ticks the policy boxes once, for the whole reservation — so BookingEndpoint::book() records both against the anchor lesson alone:

$this->gate->record( PolicyAcceptance::REG_LESSON, $anchorId, ... );

LessonDetail then looked them up by whichever lesson id was being viewed, so every occurrence after the first showed no intake answers and no policy acceptances — reading as though the student had agreed to nothing.

The fix

LessonDetail now takes the Lesson rather than a bare id and resolves the registration to series_id ?? id, so each occurrence reads the anchor's records. LessonController already had the Lesson loaded and passes it straight through.

This is the same seam PaymentService::creditForCancelledLesson() already uses to find a series lesson's payment on the anchor — series members carry no payment_id, and by the same token no registration of their own.

The intake answers had the identical bug from the same call site, so both are fixed together.

Upgrade safety

Read-path only. The acceptance rows were always written correctly, so existing bookings — including the customer's — display properly the moment this deploys. No schema change, no version bump, no migration, and nothing to re-collect from anyone.

Tests

  • composer test — 908 tests, 2576 assertions, all passing. New LessonDetailTest case covers a series occurrence reading the anchor's answers and acceptances; LessonControllerTest updated for the changed signature.
  • composer lint — no errors.
  • composer cs — clean.

Not included

Only the admin lesson detail view read the registration by lesson id, so nothing else needed the same treatment. Group-class enrolments record against the enrolment itself and were never affected.

🤖 Generated with Claude Code

Closes #167. Found on a live deployment, on the studio's first real booking. ## The bug A weekly booking reserves a series of lessons, but the student answers the intake and ticks the policy boxes **once**, for the whole reservation — so `BookingEndpoint::book()` records both against the anchor lesson alone: ```php $this->gate->record( PolicyAcceptance::REG_LESSON, $anchorId, ... ); ``` `LessonDetail` then looked them up by whichever lesson id was being viewed, so every occurrence after the first showed no intake answers and no policy acceptances — reading as though the student had agreed to nothing. ## The fix `LessonDetail` now takes the `Lesson` rather than a bare id and resolves the registration to `series_id ?? id`, so each occurrence reads the anchor's records. `LessonController` already had the `Lesson` loaded and passes it straight through. This is the same seam `PaymentService::creditForCancelledLesson()` already uses to find a series lesson's payment on the anchor — series members carry no `payment_id`, and by the same token no registration of their own. The intake answers had the identical bug from the same call site, so both are fixed together. ## Upgrade safety Read-path only. The acceptance rows were always written correctly, so existing bookings — including the customer's — display properly the moment this deploys. No schema change, no version bump, no migration, and nothing to re-collect from anyone. ## Tests - `composer test` — 908 tests, 2576 assertions, all passing. New `LessonDetailTest` case covers a series occurrence reading the anchor's answers and acceptances; `LessonControllerTest` updated for the changed signature. - `composer lint` — no errors. - `composer cs` — clean. ## Not included Only the admin lesson detail view read the registration by lesson id, so nothing else needed the same treatment. Group-class enrolments record against the enrolment itself and were never affected. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
thatguygriff added 1 commit 2026-07-30 19:36:11 +00:00
Show a series' policy acceptances on every occurrence
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m54s
CI / Coding Standards (pull_request) Successful in 2m59s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
df3462a8b3
A weekly booking reserves a series of lessons, but the student answers
the intake and ticks the policy boxes once — so BookingEndpoint records
both against the anchor lesson alone. The admin detail view looked them
up by whichever lesson id was being viewed, so every occurrence after
the first showed no answers and no acceptances at all.

LessonDetail now takes the Lesson rather than a bare id and resolves the
registration to `series_id ?? id`, so each occurrence reads the anchor's
records. This is the same seam PaymentService already uses to find a
series lesson's payment on the anchor.

Nothing was ever missing from the database, so existing bookings read
correctly with no migration and no schema change.

Closes #167

Co-Authored-By: Claude Opus 5 <[email protected]>
thatguygriff merged commit 1e4e21e8d3 into main 2026-07-30 19:41:03 +00:00
thatguygriff deleted branch fix/167-series-policy-acceptances 2026-07-30 19:41:03 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Unsupervised/unsupervised-scheduler#168