Notify instructors on new bookings and enrolments
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

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
This commit is contained in:
2026-09-18 16:18:43 -03:00
co-authored by anthropic/claude-opus-4-8
parent 56fc0bd57d
commit 12765d8f13
16 changed files with 583 additions and 3 deletions
+13 -1
View File
@@ -5,6 +5,7 @@ namespace Unsupervised\Schedular\Tests\Unit\Booking;
use Brain\Monkey\Functions;
use Mockery;
use Unsupervised\Schedular\Auth\InstructorNotificationMailer;
use Unsupervised\Schedular\Availability\AvailabilityRepository;
use Unsupervised\Schedular\Availability\AvailabilitySlot;
use Unsupervised\Schedular\Booking\BookingEndpoint;
@@ -46,6 +47,12 @@ class BookingEndpointTest extends TestCase
// Fixed "now" well before the fixture slot start (2026-07-01 10:00), so
// the cancellation cutoff never trips unless a test moves it.
Functions\when('current_time')->justReturn('2026-06-01 10:00:00');
// A successful booking resolves the student's name and the lesson date for
// the (mocked) instructor notice; neither shapes what these tests assert.
Functions\when('get_userdata')->justReturn(false);
Functions\when('mysql2date')->alias(
static fn (string $format, string $date): string => date($format, (int) strtotime($date))
);
$this->availability = Mockery::mock(AvailabilityRepository::class);
$this->bookings = Mockery::mock(BookingRepository::class);
@@ -78,6 +85,11 @@ class BookingEndpointTest extends TestCase
$this->sessions->shouldReceive('upcomingForStudent')->andReturn([])->byDefault();
$this->sessions->shouldReceive('upcomingForInstructor')->andReturn([])->byDefault();
// The opt-in instructor notice is tested on its own; a mock keeps these
// tests about booking, not about who gets emailed.
$instructorMailer = Mockery::mock(InstructorNotificationMailer::class);
$instructorMailer->shouldReceive('notifyLessonBooked')->andReturn(true)->byDefault();
$this->endpoint = new BookingEndpoint(
$this->availability,
$this->bookings,
@@ -87,7 +99,7 @@ class BookingEndpointTest extends TestCase
// The real booker over the same mocked repositories: these tests are
// about what a booking does end to end, and the booker is where most
// of that now lives.
new LessonBooker($this->availability, $this->bookings, $this->offerings, $this->payments, $this->guardians),
new LessonBooker($this->availability, $this->bookings, $this->offerings, $this->payments, $this->guardians, $instructorMailer),
new CancellationPolicy($this->settings),
$this->guardians,
$this->sessions,