Require an offering on every lesson booking, with a student-facing picker #56

Merged
thatguygriff merged 1 commits from fix/require-booking-offering into main 2026-07-06 01:14:18 +00:00
Owner

Fixes #55

Problem

Availability slots created without a tied offering could be booked with no offering at all: the lesson was free, confirmed immediately, and asked no intake questions. The booking UI never let the student choose an offering, contradicting the documented flow.

Changes

Server — Booking\BookingEndpoint

  • POST /bookings now rejects bookings with no resolvable offering (400 offering_required).
  • A student-chosen offering (generic slot) must be active, of kind private_lesson, owned by the slot's instructor, and its duration_minutes must match the slot — implementing the duration-matching rule from docs/features/availability-management.md.
  • A slot-tied offering remains authoritative over anything the client sends (existing anti-tamper behaviour unchanged).

Front end — assets/js/booking.js

  • Registration form gains a Lesson type field:
    • Slot tied to an offering → locked select showing title, duration, and price, so the student sees exactly what they are booking.
    • Generic slot → required picker of the instructor's active private-lesson offerings that fit the slot length (catalog cached per instructor).
  • Intake questions now load for whichever offering is selected.
  • Generic slots with no fitting offering show a "cannot be booked online" message instead of a doomed form.

Tests & docs

  • BookingEndpointTest: offering-less booking rejected; slot-tied offering charged when the request omits it; inactive / group-class / duration-mismatched offerings rejected.
  • docs/features/lesson-booking.md updated to match.

Deployment note

Existing generic slots become unbookable until the instructor ties an offering to them or has at least one active private-lesson offering matching their duration — the intended consequence of closing the free-booking hole.

composer test (286 tests), composer lint (PHPStan level 10), and composer cs all pass.

🤖 Generated with Claude Code

Fixes #55 ## Problem Availability slots created without a tied offering could be booked with no offering at all: the lesson was **free, confirmed immediately, and asked no intake questions**. The booking UI never let the student choose an offering, contradicting the documented flow. ## Changes **Server — `Booking\BookingEndpoint`** - `POST /bookings` now rejects bookings with no resolvable offering (`400 offering_required`). - A student-chosen offering (generic slot) must be **active**, of kind **`private_lesson`**, owned by the slot's instructor, and its `duration_minutes` must match the slot — implementing the duration-matching rule from `docs/features/availability-management.md`. - A slot-tied offering remains authoritative over anything the client sends (existing anti-tamper behaviour unchanged). **Front end — `assets/js/booking.js`** - Registration form gains a **Lesson type** field: - Slot tied to an offering → locked select showing title, duration, and price, so the student sees exactly what they are booking. - Generic slot → required picker of the instructor's active private-lesson offerings that fit the slot length (catalog cached per instructor). - Intake questions now load for whichever offering is selected. - Generic slots with no fitting offering show a "cannot be booked online" message instead of a doomed form. **Tests & docs** - `BookingEndpointTest`: offering-less booking rejected; slot-tied offering charged when the request omits it; inactive / group-class / duration-mismatched offerings rejected. - `docs/features/lesson-booking.md` updated to match. ## Deployment note Existing generic slots become unbookable until the instructor ties an offering to them or has at least one active private-lesson offering matching their duration — the intended consequence of closing the free-booking hole. `composer test` (286 tests), `composer lint` (PHPStan level 10), and `composer cs` all pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
thatguygriff added 1 commit 2026-07-06 01:10:13 +00:00
Require an offering on every lesson booking, with a student-facing picker
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m40s
CI / PHPStan (pull_request) Successful in 2m24s
CI / Coding Standards (pull_request) Successful in 2m49s
CI / Build Plugin Zip (pull_request) Has been skipped
c7d5d72c9c
Generic slots (no tied offering) were bookable with no offering at all:
free, instantly confirmed, and with no intake questions. POST /bookings
now rejects offering-less bookings (400 offering_required), and a
student-chosen offering must be an active private-lesson type owned by
the slot's instructor whose duration matches the slot.

The registration form gains a Lesson type field: locked to the slot's
tied offering (title, duration, price) so the student sees what they
are booking, or a required picker of fitting offerings for generic
slots, with intake questions following the selection.

Fixes #55

Co-Authored-By: Claude Fable 5 <[email protected]>
thatguygriff merged commit aaa24e524f into main 2026-07-06 01:14:18 +00:00
thatguygriff deleted branch fix/require-booking-offering 2026-07-06 01:14:18 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Unsupervised/unsupervised-scheduler#56