# 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`