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

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]>
This commit is contained in:
2026-07-30 16:35:26 -03:00
co-authored by Claude Opus 5
parent 748478f2f1
commit df3462a8b3
6 changed files with 83 additions and 11 deletions
+18 -4
View File
@@ -17,6 +17,12 @@ use Unsupervised\Schedular\Registration\QuestionRepository;
*
* Scoped to a single lesson (the `lesson` registration type), mirroring the
* per-student history in {@see \Unsupervised\Schedular\Auth\StudentHistory}.
*
* A weekly reservation is answered for and agreed to once, so its answers and
* acceptances hang off the series anchor. Every occurrence therefore reads its
* series' registration, not its own id — otherwise only the first lesson of a
* series showed the intake and the audit trail, and the rest looked as though
* nothing had been accepted.
*/
class LessonDetail {
@@ -33,7 +39,7 @@ class LessonDetail {
*
* @return list<array{question: string, answer: string}>
*/
public function answers( int $lessonId ): array {
public function answers( Lesson $lesson ): array {
return array_map(
function ( Answer $answer ): array {
$question = $this->questions->findById( $answer->questionId );
@@ -44,7 +50,7 @@ class LessonDetail {
'answer' => '' === $value ? '—' : $value,
];
},
$this->answers->findByRegistration( Answer::REG_LESSON, $lessonId )
$this->answers->findByRegistration( Answer::REG_LESSON, $this->registrationId( $lesson ) )
);
}
@@ -54,7 +60,7 @@ class LessonDetail {
*
* @return list<array{policy: string, version: string, accepted_at: string, ip: string}>
*/
public function acceptances( int $lessonId ): array {
public function acceptances( Lesson $lesson ): array {
return array_map(
function ( PolicyAcceptance $acceptance ): array {
$version = $this->versions->findById( $acceptance->policyVersionId );
@@ -67,7 +73,15 @@ class LessonDetail {
'ip' => $acceptance->ipAddress ?? '',
];
},
$this->acceptances->findByRegistration( PolicyAcceptance::REG_LESSON, $lessonId )
$this->acceptances->findByRegistration( PolicyAcceptance::REG_LESSON, $this->registrationId( $lesson ) )
);
}
/**
* The lesson id the booking's answers and acceptances were recorded against:
* the series anchor for a weekly reservation, the lesson itself otherwise.
*/
private function registrationId( Lesson $lesson ): int {
return $lesson->seriesId ?? (int) $lesson->id;
}
}