Skip payment step for unpriced bookings, confirm them immediately, show students their lessons
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / PHPStan (pull_request) Successful in 2m46s
CI / Tests (PHP 8.1) (pull_request) Successful in 42s
CI / Coding Standards (pull_request) Successful in 47s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m35s
CI / Build Plugin Zip (pull_request) Has been skipped

Booking a slot with no priced offering created the lesson but no payment,
yet the front end still called POST /payments/intent, which 400ed with
"Could not start payment for this registration" — the student saw an error
while the backend held a claimed slot and a lesson stuck at pending.

- POST /bookings and POST /enrollments now return a `payment` summary
  ({id, method, status}) or null when nothing is owed; the JS only runs
  the payment step when a payment exists.
- Bookings with nothing owed are confirmed at creation — there is no
  payment step that would ever confirm them later.
- The booking page now shows the student's upcoming lessons (GET /bookings,
  now scoped to upcoming non-cancelled lessons with slot start/end times)
  with a pending-payment/confirmed status badge.

Fixes #53

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-05 17:02:38 -03:00
co-authored by Claude Fable 5
parent 93dccd6352
commit 5888032ed7
15 changed files with 433 additions and 25 deletions
@@ -132,6 +132,35 @@ class BookingRepositoryTest extends TestCase
self::assertFalse($this->repo->updateStatus(1, Lesson::STATUS_CONFIRMED));
}
public function testFindUpcomingForStudentJoinsSlotAndExcludesCancelled(): void
{
Functions\when('current_time')->justReturn('2026-06-08 12:00:00');
$this->db->shouldReceive('prepare')
->once()
->with(Mockery::pattern('/SELECT l\.\*.*l.student_id = %d.*l.status != %s.*a.start_dt >= %s.*ORDER BY a.start_dt ASC/s'), 'wp_us_lessons', 'wp_us_availability', 5, Lesson::STATUS_CANCELLED, '2026-06-08 12:00:00')
->andReturn('SELECT ...');
$row = (object) [
'id' => '15',
'slot_id' => '10',
'offering_id' => null,
'student_id' => '5',
'instructor_id' => '3',
'recurrence' => Lesson::RECURRENCE_SINGLE,
'series_id' => null,
'status' => 'confirmed',
'payment_id' => null,
'notes' => null,
];
$this->db->shouldReceive('get_results')->andReturn([$row]);
$lessons = $this->repo->findUpcomingForStudent(5);
self::assertCount(1, $lessons);
self::assertSame(15, $lessons[0]->id);
}
public function testCountUpcomingForStudent(): void
{
Functions\when('current_time')->justReturn('2026-06-08 12:00:00');