Files
unsupervised-scheduler/docs/features/lesson-booking.md
T
thatguygriff 6d163e5d0e
CI / Coding Standards (pull_request) Successful in 1m51s
CI / PHPStan (pull_request) Successful in 2m17s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m24s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / Build Plugin Zip (pull_request) Has been skipped
Add lesson booking registration flow (offering, questions, policies)
Implements #3: students register for a private lesson by picking a slot,
answering the offering's intake questions, and accepting booking-scoped
policies. Payment is a clean seam for #7 (lessons land pending; payment_id
null; instructor confirms via PATCH /bookings/{id}/status).

- Schema: us_lessons += offering_id, recurrence, series_id, payment_id.
- Lesson: new fields + recurrence constants.
- BookingRepository::insertSeries() builds a weekly series sharing a
  series_id; AvailabilityRepository::findUnbookedInGroup() reserves a group.
- RegistrationGate (src/Registration/): validate + record intake answers and
  booking-scoped policy acceptances. Reused by group enrolment (#4).
- BookingEndpoint::book(): offering_id, recurrence, answers,
  accepted_policy_version_ids; single or weekly; records answers/acceptances
  (type lesson).
- GET /policies?scope=booking filter.
- Front-end booking.js: slot -> questions + policies -> submit.
- Wiring: RegistrationGate built in Plugin, passed via RestRegistrar.
- Test-only WP_Error stub in tests/bootstrap.php for gate testing.

Refs #3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 11:25:30 -03:00

5.3 KiB

Feature: Lesson Booking

Overview

Students register for a private lesson by choosing an offering, picking a time (or reserving a weekly slot for the term), answering the offering's intake questions, accepting current policies, and paying. Instructors confirm or cancel from wp-admin or via the REST API. A lesson becomes confirmed only once its payment is paid (or the student is comp'd).

Data Model — {prefix}us_lessons

Column Type Notes
id BIGINT UNSIGNED Primary key
slot_id BIGINT UNSIGNED FK → us_availability.id
offering_id BIGINT UNSIGNED FK → us_offerings.id (the private-lesson type booked)
student_id BIGINT UNSIGNED WordPress user ID
instructor_id BIGINT UNSIGNED WordPress user ID (denormalised for fast queries)
recurrence VARCHAR(10) single or weekly
series_id BIGINT UNSIGNED Nullable — groups the lesson rows of one weekly reservation
status VARCHAR(20) pending / confirmed / cancelled
payment_id BIGINT UNSIGNED Nullable FK → us_payments.id
notes TEXT Optional student notes
created_at DATETIME Insertion time

Registration Flow

  1. Student opens the page with the [us_booking] shortcode and browses the calendar.
  2. Student picks an offering (a 30 or 60-minute private-lesson type) and a slot.
  3. For a weekly reservation, the same weekday/time is held for the rest of the offering's term.
  4. Student answers the offering's questions (GET /offerings/{id}/questions).
  5. Student accepts the current published policy versions (GET /policies) — required to continue.
  6. Payment is taken per the student's billing method (card by default; pending for e-transfer; skipped for comp). See payments.md.
  7. POST /bookings creates the lesson row(s) (status = pending), records answers and policy acceptances, marks us_availability.is_booked = 1, and links the payment.
  8. On successful payment (or comp) the lesson is confirmed and a receipt is emailed.
  9. Instructor sees the booking under My Lessons and may update status via PATCH /bookings/{id}/status.

Weekly Reservations

A weekly reservation creates one series_id shared across N lesson rows (one per week in the term) and reserves the matching availability windows. It is billed full-term upfront as a single payment (billing_mode = full_term on the offering).

REST API

Method Endpoint Permission
GET /wp-json/us-scheduler/v1/bookings Any logged-in user
POST /wp-json/us-scheduler/v1/bookings book_lesson
PATCH /wp-json/us-scheduler/v1/bookings/{id}/status manage_availability or admin

POST /bookings body: offering_id, slot_id, recurrence, answers[] (question_id → value), accepted_policy_version_ids[], and payment data (see payments.md).

GET /bookings returns the caller's own lessons (student view) or upcoming lessons for the instructor if the caller has manage_availability.

Group classes follow the same registration flow but enrol against an offering of kind group_class; see group-classes.md.

Admin Interface

  • Scheduler (view_all_lessons — studio admin / administrators): all upcoming lessons across all instructors
  • My Lessons (view_own_lessons): upcoming lessons for the logged-in instructor

Frontend Shortcodes

  • [us_booking] — student calendar + registration flow; requires book_lesson capability
  • [us_student_login] — front-end login form for students

Implementation

  • Repository: Unsupervised\Schedular\Booking\BookingRepository (insertSeries() builds a weekly series sharing a series_id)
  • Model: Unsupervised\Schedular\Booking\Lesson
  • Registration gate: Unsupervised\Schedular\Registration\RegistrationGate — validates and records intake answers + booking-scoped policy acceptances; shared with group enrolment
  • Admin controller: Unsupervised\Schedular\Booking\LessonController
  • REST endpoint: Unsupervised\Schedular\Booking\BookingEndpoint
  • Frontend: Unsupervised\Schedular\Booking\BookingPage, Unsupervised\Schedular\Auth\LoginPage

Payment seam: payment is deferred to the Payments feature (#7). For now a booking is created with status = pending and payment_id = null; the instructor confirms via PATCH /bookings/{id}/status. When payments land, the pay→confirm + receipt step plugs into this seam. GET /policies?scope=booking returns just the booking-gate policies the form must collect.

Tests

  • tests/Unit/Booking/BookingRepositoryTest.php
  • tests/Unit/Booking/LessonTest.php