Add group-class enrolment deadline with instructor late-enrolment override
CI / Tests (PHP 8.1) (pull_request) Successful in 48s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Coding Standards (pull_request) Successful in 3m2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m37s
CI / Build Plugin Zip (pull_request) Skipped

Group classes gain an instructor-set enrolment deadline (new
us_offerings.enrollment_deadline column) that defaults to the first day of
the class (term_start). Past the deadline students can no longer self-enrol:
the enrolment endpoint rejects it (403 enrollment_closed) and the front-end
class list shows "Enrolment has closed." in place of the Enrol button.

Instructors keep a manual path: the "Add students directly" control on each
class's details page now renders for public classes too (not just
invite-only) and deliberately bypasses the deadline and capacity, so a
student can be added as a late enrolment after the class has closed. Past
the deadline the details page labels these as late enrolments.

Bumps USC_VERSION to 1.1.3 for the schema change.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
2026-07-24 10:19:38 -03:00
co-authored by Claude Opus 4.8
parent 991ed2f5ad
commit bf29162587
18 changed files with 338 additions and 54 deletions
@@ -65,6 +65,7 @@ class GroupClassControllerTest extends TestCase
static fn (string $format, string $date) => date($format, (int) strtotime($date))
);
Functions\when('wp_nonce_field')->justReturn('');
Functions\when('current_time')->justReturn('2026-01-01');
$_GET = [];
}
@@ -183,6 +184,49 @@ class GroupClassControllerTest extends TestCase
self::assertStringContainsString('Invite by email', $html);
}
public function testClassDetailOffersDirectAddForPublicClassWithoutInviteControls(): void
{
Functions\when('get_userdata')->justReturn($this->userNamed('Ada Lovelace'));
$_GET = ['class_id' => '8'];
// A plain public group class — the instructor can still add students
// directly (a late enrolment), but the invite-only controls are absent.
$offering = $this->offering(8, 'Choir', 10);
$this->offerings->shouldReceive('findAll')->once()->andReturn([$offering]);
$this->enrollments->shouldReceive('findByInstructor')->once()->with(3)->andReturn([]);
$html = $this->renderInstructor();
self::assertStringContainsString('Add students directly', $html);
self::assertStringContainsString('add_direct', $html);
self::assertStringNotContainsString('Invite by email', $html);
self::assertStringNotContainsString('Make available to students', $html);
}
public function testClassDetailFlagsLateEnrolmentPastTheDeadline(): void
{
Functions\when('get_userdata')->justReturn($this->userNamed('Ada Lovelace'));
// current_time is stubbed to 2026-01-01, which is past this class's deadline.
$_GET = ['class_id' => '8'];
$offering = new Offering(
instructorId: 3,
kind: Offering::KIND_GROUP_CLASS,
title: 'Choir',
termStart: '2025-09-08',
id: 8,
);
$this->offerings->shouldReceive('findAll')->once()->andReturn([$offering]);
$this->enrollments->shouldReceive('findByInstructor')->once()->with(3)->andReturn([]);
$html = $this->renderInstructor();
self::assertStringContainsString('late enrolments', $html);
self::assertStringContainsString('Add students directly', $html);
}
public function testClassDetailEnrolmentCountExcludesCancelledButRosterKeepsThem(): void
{
Functions\when('get_userdata')->justReturn($this->userNamed('Grace Hopper'));