Files
Kydoimosandanthropic/claude-opus-4-8 12765d8f13
CI / Coding Standards (pull_request) Successful in 27s
CI / Tests (PHP 8.1) (pull_request) Successful in 37s
CI / No Debug Code (pull_request) Successful in 9s
CI / Tests (PHP 8.3) (pull_request) Successful in 36s
CI / Tests (PHP 8.5) (pull_request) Successful in 40s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Static Analysis (pull_request) Successful in 48s
CI / Build Plugin Zip (pull_request) Skipped
Notify instructors on new bookings and enrolments
Add an opt-in, per-instructor email notice sent when someone books one of
their lessons or enrols in one of their group classes. Off by default and
set from My Availability → Notifications; covers both the student/guardian
REST flows and the studio's wp-admin "book/add for a student" forms.

The opt-in check lives in InstructorNotificationMailer so no booking path
can drift on who is mailed; lessons fire from LessonBooker::settle (the one
step both booking paths reach), enrolments from EnrollmentEndpoint::enroll
and GroupClassController::addDirect. The notice is a courtesy and never
fails a booking or enrolment that otherwise succeeded.

Co-authored-by: anthropic/claude-opus-4-8
2026-09-18 16:18:43 -03:00

85 lines
4.4 KiB
Markdown

# Feature: Instructor Booking Notifications
## Overview
An instructor can ask to be emailed whenever someone books one of their private
lessons or enrols in one of their group classes. It is **off by default** and set
per instructor — the people it emails decide whether they want it.
The notice fires whichever way the booking or enrolment was made: a student
(or a guardian for their child) doing it themselves through the front-end, or the
studio doing it on their behalf from wp-admin. There is no separate "the studio
booked it" case to forget — every path funnels through one place per registration
type, and the opt-in check lives in the mailer, not at each call site, so no path
can drift on who gets mailed.
## Preference `us_notify_on_booking` (user meta)
`'1'` or `'0'`, stored against the instructor's WordPress user. Absent — the
default for every account — reads as off. Stored as `'0'` rather than deleted when
an instructor turns it off, so a deliberate "no" is told apart from never having
chosen.
Default off because the notification is a new capability: turning it on for every
instructor on an existing site the day it ships would mail people who never asked.
## Admin Interface
**My Availability → Notifications** (`manage_availability` — every instructor sees
their own availability page):
- **Email me when someone books a lesson or enrols in one of my group classes** —
a single checkbox, off by default, saved on its own form (`usc_action=save_notify`).
The page an instructor sets availability on is the one they already visit to shape
their teaching schedule, so the preference about that schedule lives beside it.
## What triggers a notice
| Registration | Path | Where the notice fires |
|---|---|---|
| Private lesson | student/guardian REST **and** studio wp-admin form | `Booking\LessonBooker::settle()` — the single step both paths reach once a slot is claimed |
| Group class | student/guardian REST | `GroupClass\EnrollmentEndpoint::enroll()`, after the roster row is written |
| Group class | studio "Add students directly" (wp-admin) | `GroupClass\GroupClassController::addDirect()`, per student added |
A weekly lesson reservation reports the number of occurrences claimed, so a single
booking and a term booked at once each read correctly.
The notice is an opt-in courtesy, never a step a booking or enrolment depends on:
a missing or failed send can never fail a booking that otherwise succeeded, and
the mailer returns false (without sending) when the instructor has not opted in,
their account is gone, or it carries no email.
## Implementation
- `Unsupervised\Schedular\Auth\InstructorNotificationPref` — the per-instructor
user-meta preference (`wants()` / `set()`), default off
- `Unsupervised\Schedular\Auth\InstructorNotificationMailer``notifyLessonBooked()`
and `notifyEnrollment()`; the opt-in check and recipient resolution live here
- `Unsupervised\Schedular\Booking\LessonBooker::settle()` — fires the lesson notice
for both booking paths
- `Unsupervised\Schedular\GroupClass\EnrollmentEndpoint::enroll()` — student/guardian
enrolment notice
- `Unsupervised\Schedular\GroupClass\GroupClassController::addDirect()` — studio
enrolment notice
- `Unsupervised\Schedular\Availability\AvailabilityController` — reads the preference
for the page and saves the toggle (`save_notify`)
- `templates/admin/availability.php` — the Notifications checkbox
All three consumers take the mailer (and the controller its preference) as a
constructor dependency defaulting to a fresh instance, so existing wiring in
`Plugin`, `RestRegistrar` and `AdminMenu` is unchanged.
## Tests
- `tests/Unit/Auth/InstructorNotificationPrefTest.php` — default-off, opt-in read,
string-boolean write, non-user guards
- `tests/Unit/Auth/InstructorNotificationMailerTest.php` — sends to an opted-in
instructor, weekly occurrence count, and sends nothing when opted out / account
gone / no email
- `tests/Unit/GroupClass/EnrollmentEndpointTest.php` — a successful enrolment
notifies the class instructor
- `tests/Unit/Availability/AvailabilityControllerTest.php` — the toggle saves an
opt-in and an opt-out
- The booking-path tests (`BookingEndpointTest`, `AdminBookingTest`) inject a mock
mailer, keeping them about booking
## Related
- `lesson-booking.md` — the booking core the lesson notice hangs off
- `group-classes.md` — the enrolment paths the class notice hangs off
- `user-roles.md` — the instructor role and `manage_availability` capability