The student-administration spec deferred three detail-view sections until Payments landed, and shipped the view read-only. This PR completes both follow-ups on Students → student detail (manage_students).
Policy acceptances — newest first: policy title, version (v3), context (Account signup / Lesson #n / Enrolment #n), and acceptance date. Falls back gracefully (#id / —) if a version or policy was deleted.
Intake answers — question label, answer, and the registration it was given for.
Payment history — gated on manage_billing (matching the billing-method section): date, context, method, status, subtotal, HST, total + currency, receipt number.
Edit account — display name + email form in the Account section, with validation (valid email, not owned by another user) and success/error notices.
Cancel lesson — per-row button on non-cancelled upcoming lessons, with a JS confirm. Same semantics as student-initiated cancellation: lesson cancelled, slot freed for rebooking, still-pending payment voided; paid lessons keep their payment (refunds stay manual — #72).
Withdraw — per-row button on active group-class enrolments: enrolment cancelled (capacity seat freed), pending payment voided.
How
New per-student queries: AcceptanceRepository::findByStudent, AnswerRepository::findByStudent, PaymentRepository::findByStudent (all newest-first).
New Auth\StudentHistory builds the history display rows and resolves policy/version titles and question labels; new Auth\StudentActions holds the mutation logic (both mockable units in the spirit of StudentSchedule, since controllers aren't unit-tested here). Cancel/withdraw refuse records that don't belong to the student being viewed.
The controller routes nonce-protected POSTs (usc_action read for routing only; each branch calls check_admin_referer) — restructured so the existing billing form's nonce no longer wp_dies other forms on the page.
The Payment model now maps created_at (appended as a named-arg-only constructor parameter, so no call sites change), giving unpaid e-transfer rows a date in the history.
Wired through AdminMenu (now also takes AnswerRepository + AcceptanceRepository) and Plugin.
Tests
New tests/Unit/Auth/StudentHistoryTest.php — row building for all three sections plus missing-version/missing-question/undated fallbacks.
New tests/Unit/Auth/StudentActionsTest.php — cancel/withdraw side effects (status, slot release, payment voiding) and guards (wrong student, already cancelled, missing), account validation (empty name, invalid/taken email, WP_Error pass-through).
findByStudent coverage added to the three repository test suites; created_at mapping asserted in PaymentTest.
composer test — 387 tests, 1119 assertions, all passing
composer lint — PHPStan clean
composer cs — PHPCS clean
As with prior features, no local WP runtime exists in this repo (tests stub WP via Brain\Monkey); worth a manual smoke on staging: open a student who has booked and paid — confirm the three history sections render, cancel an upcoming lesson and check the slot is rebookable and the pending payment left the confirmation queue, withdraw an enrolment, edit the account email, and view the page as a non-billing admin to confirm the payment section is hidden.
Closes #69
Closes #70
## What & why
The student-administration spec deferred three detail-view sections until Payments landed, and shipped the view read-only. This PR completes both follow-ups on **Students → student detail** (`manage_students`).
### History sections (#69)
- **Policy acceptances** — newest first: policy title, version (`v3`), context (Account signup / Lesson #n / Enrolment #n), and acceptance date. Falls back gracefully (`#id` / `—`) if a version or policy was deleted.
- **Intake answers** — question label, answer, and the registration it was given for.
- **Payment history** — gated on `manage_billing` (matching the billing-method section): date, context, method, status, subtotal, HST, total + currency, receipt number.
### Admin actions (#70)
- **Edit account** — display name + email form in the Account section, with validation (valid email, not owned by another user) and success/error notices.
- **Cancel lesson** — per-row button on non-cancelled upcoming lessons, with a JS confirm. Same semantics as student-initiated cancellation: lesson `cancelled`, slot freed for rebooking, still-pending payment voided; paid lessons keep their payment (refunds stay manual — #72).
- **Withdraw** — per-row button on active group-class enrolments: enrolment `cancelled` (capacity seat freed), pending payment voided.
## How
- New per-student queries: `AcceptanceRepository::findByStudent`, `AnswerRepository::findByStudent`, `PaymentRepository::findByStudent` (all newest-first).
- New `Auth\StudentHistory` builds the history display rows and resolves policy/version titles and question labels; new `Auth\StudentActions` holds the mutation logic (both mockable units in the spirit of `StudentSchedule`, since controllers aren't unit-tested here). Cancel/withdraw refuse records that don't belong to the student being viewed.
- The controller routes nonce-protected POSTs (`usc_action` read for routing only; each branch calls `check_admin_referer`) — restructured so the existing billing form's nonce no longer `wp_die`s other forms on the page.
- The `Payment` model now maps `created_at` (appended as a named-arg-only constructor parameter, so no call sites change), giving unpaid e-transfer rows a date in the history.
- Wired through `AdminMenu` (now also takes `AnswerRepository` + `AcceptanceRepository`) and `Plugin`.
## Tests
- New `tests/Unit/Auth/StudentHistoryTest.php` — row building for all three sections plus missing-version/missing-question/undated fallbacks.
- New `tests/Unit/Auth/StudentActionsTest.php` — cancel/withdraw side effects (status, slot release, payment voiding) and guards (wrong student, already cancelled, missing), account validation (empty name, invalid/taken email, `WP_Error` pass-through).
- `findByStudent` coverage added to the three repository test suites; `created_at` mapping asserted in `PaymentTest`.
- `composer test` — 387 tests, 1119 assertions, all passing
- `composer lint` — PHPStan clean
- `composer cs` — PHPCS clean
> As with prior features, no local WP runtime exists in this repo (tests stub WP via Brain\Monkey); worth a manual smoke on staging: open a student who has booked and paid — confirm the three history sections render, cancel an upcoming lesson and check the slot is rebookable and the pending payment left the confirmation queue, withdraw an enrolment, edit the account email, and view the page as a non-billing admin to confirm the payment section is hidden.
Docs updated: `docs/features/student-administration.md`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The student-administration spec deferred three detail-view sections until
Payments landed. Adds them now: policy-acceptance history (title, version,
context, date), intake answers (label, answer, context), and — gated on
manage_billing — payment history with HST breakdown and receipt numbers.
New Auth\StudentHistory builds the display rows from per-student queries
added to AcceptanceRepository, AnswerRepository, and PaymentRepository;
the Payment model now carries created_at so unpaid rows still have a date.
Closes#69
Co-Authored-By: Claude Fable 5 <[email protected]>
Adds the #70 follow-up onto the student detail page: studio admins can now
cancel an upcoming lesson (same path as student cancellation — slot freed,
pending payment voided), withdraw an active group-class enrolment (seat
freed, pending payment voided), and edit the student's display name and
email with validation and uniqueness checks.
Action logic lives in the new Auth\StudentActions (unit-tested with mocked
repositories); the controller routes nonce-protected POSTs to it and shows
success/error notices.
Closes#70
Co-Authored-By: Claude Fable 5 <[email protected]>
thatguygriff
changed title from Add policy, intake, and payment history to the admin student detail view to Student detail: history sections and admin actions (cancel, withdraw, edit account)2026-07-18 21:25:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #69
Closes #70
What & why
The student-administration spec deferred three detail-view sections until Payments landed, and shipped the view read-only. This PR completes both follow-ups on Students → student detail (
manage_students).History sections (#69)
v3), context (Account signup / Lesson #n / Enrolment #n), and acceptance date. Falls back gracefully (#id/—) if a version or policy was deleted.manage_billing(matching the billing-method section): date, context, method, status, subtotal, HST, total + currency, receipt number.Admin actions (#70)
cancelled, slot freed for rebooking, still-pending payment voided; paid lessons keep their payment (refunds stay manual — #72).cancelled(capacity seat freed), pending payment voided.How
AcceptanceRepository::findByStudent,AnswerRepository::findByStudent,PaymentRepository::findByStudent(all newest-first).Auth\StudentHistorybuilds the history display rows and resolves policy/version titles and question labels; newAuth\StudentActionsholds the mutation logic (both mockable units in the spirit ofStudentSchedule, since controllers aren't unit-tested here). Cancel/withdraw refuse records that don't belong to the student being viewed.usc_actionread for routing only; each branch callscheck_admin_referer) — restructured so the existing billing form's nonce no longerwp_dies other forms on the page.Paymentmodel now mapscreated_at(appended as a named-arg-only constructor parameter, so no call sites change), giving unpaid e-transfer rows a date in the history.AdminMenu(now also takesAnswerRepository+AcceptanceRepository) andPlugin.Tests
tests/Unit/Auth/StudentHistoryTest.php— row building for all three sections plus missing-version/missing-question/undated fallbacks.tests/Unit/Auth/StudentActionsTest.php— cancel/withdraw side effects (status, slot release, payment voiding) and guards (wrong student, already cancelled, missing), account validation (empty name, invalid/taken email,WP_Errorpass-through).findByStudentcoverage added to the three repository test suites;created_atmapping asserted inPaymentTest.composer test— 387 tests, 1119 assertions, all passingcomposer lint— PHPStan cleancomposer cs— PHPCS cleanDocs updated:
docs/features/student-administration.md.🤖 Generated with Claude Code
Add policy, intake, and payment history to the admin student detail viewto Student detail: history sections and admin actions (cancel, withdraw, edit account)