Let a guardian enrol every child in the same group class
CI / No Debug Code (pull_request) Successful in 26s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m5s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Coding Standards (pull_request) Successful in 3m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 6m9s
CI / Build Plugin Zip (pull_request) Skipped

The group-class page matched enrolments to the account instead of to the
student: the lookup it built from GET /enrollments was keyed by offering id
alone, so the first household enrolment marked the class as "yours" and took
the Enrol button away from everyone else on the account. A parent could enrol
one child and was then offered nothing but Withdraw.

The server was never the constraint — hasActiveEnrollment() checks the
(offering, student) pair and GET /enrollments deliberately returns the whole
household — so the fix is to stop discarding student_id on the way in. Active
enrolments are now grouped per class as a list, each student gets their own
"… is enrolled in this class." line and their own named Withdraw button, and
the Enrol button stays (as "Enrol another student") while anyone the account
may enrol is still out.

The enrolment form offers only the students not yet enrolled. When exactly one
of them is left the picker collapses, and that case needed care: an omitted
student_id reads as "enrol the account holder" server-side, so a hidden field
carries the id rather than posting nothing and signing up the parent instead of
the last child.

Closes #170

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Jy9UPhmpLUAfimsyecHN2Z
This commit is contained in:
2026-08-11 17:36:01 -03:00
co-authored by Claude Opus 5
parent c73b10d779
commit 8017dbb9ff
3 changed files with 134 additions and 45 deletions
+16 -6
View File
@@ -81,11 +81,20 @@ student detail page. Only *upcoming* sessions are added there — the
term's worth of past dates would bury the lessons under "Past lessons".
## Enrolment Flow
The class list is loaded together with the student's own enrolments
(`GET /enrollments`); a class the student already has an `active` enrolment in
shows "You are enrolled in this class." instead of the Enrol button (the
server would reject the duplicate with `409 already_enrolled` regardless — a
cancelled enrolment does not block re-enrolling).
The class list is loaded together with the household's enrolments
(`GET /enrollments`), and the two are matched up **per student**, not per account.
Each active enrolment in a class adds its own line to the card — "Ada is enrolled
in this class." — with its own **Withdraw** button, and the Enrol button stays
(reading "Enrol another student") for as long as anyone the account may enrol is
still out of the class. The enrolment form then offers only those students; when
exactly one is left the picker collapses to a hidden field carrying that student's
id, because an omitted `student_id` reads as "enrol the account holder" and would
sign up the parent instead of the last child. Only when the whole household is
enrolled does the Enrol button disappear.
The per-student matching mirrors the server, which rejects a duplicate with
`409 already_enrolled` for that `(offering, student)` pair alone — a sibling is
never a duplicate, and a cancelled enrolment does not block re-enrolling.
1. Student opens a group class from the offering catalog. Each class card shows its price with the **cadence** it is billed on — `120.00 CAD up front`, `40.00 CAD monthly`, and so on.
2. Student answers the offering's questions (`GET /offerings/{id}/questions`).
@@ -114,7 +123,8 @@ closed. Past the deadline the details page labels these as late enrolments. See
## Withdrawal Flow
A student may withdraw themselves from a class they are enrolled in through the same
group-class page: an active enrolment shows a **Withdraw** button.
group-class page: an active enrolment shows a **Withdraw** button. A guardian sees one
per enrolled child, labelled with the child's name, so the right seat is the one released.
`POST /enrollments/{id}/withdraw` marks the enrolment `cancelled` (freeing its
capacity seat) and voids any still-pending payment. It **never issues an account
credit** — a timely withdrawal is a clean exit, not a refund (credits are reserved