Files
unsupervised-scheduler/docs/features/offerings.md
T
thatguygriffandClaude Fable 5 cde8704267
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / Tests (PHP 8.1) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 1m14s
CI / PHPStan (pull_request) Successful in 1m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 37s
CI / Build Plugin Zip (pull_request) Has been skipped
Group class term dates, single-class embed mode, and offering editing
Group class offerings now carry real dates: the add/edit form takes a
start date plus a sessions control (one-off, or weekly for N sessions;
the end date is computed as start + (N-1) weeks via
Offering::weeklyTermEnd). Dates are validated strictly (Y-m-d) and shown
in the offerings list and on the student-facing class card, including
the weekly session count.

[us_group_classes offering="<id>"] (block attribute offeringId, chosen
from a dropdown of active classes fetched from the public offerings
endpoint) restricts the page to a single class so the enrolment flow can
be embedded on a page dedicated to that class; a pinned class that is no
longer offered reports itself closed instead of falling back to the
catalog.

Offerings are now editable from the admin screen: an Edit button
prefills the shared add/edit form and saving posts usc_action=update.
Updates always preserve the original owner and currency, and non-admin
instructors can only load and update their own offerings. The form also
gains the previously missing description field and an Active toggle (the
admin-UI counterpart of the REST is_active flag) so an edit cannot wipe
data the form never collected.

Closes #59

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-05 23:18:59 -03:00

5.0 KiB
Raw Blame History

Feature: Offerings

Overview

An offering is anything a student can register for: a private-lesson type (30 or 60 minutes, single or weekly) or a group class. Offerings carry pricing, billing mode, and the intake questions a registrant must answer. They are the catalog the booking calendar and group-class pages are built from.

Data Model — {prefix}us_offerings

Column Type Notes
id BIGINT UNSIGNED Primary key
instructor_id BIGINT UNSIGNED WordPress user ID of the owning instructor
kind VARCHAR(20) private_lesson or group_class
title VARCHAR(191) Display name
description TEXT Optional longer description
duration_minutes SMALLINT Private lessons only (e.g. 30, 60); NULL for group classes
price DECIMAL(10,2) Price in dollars
currency VARCHAR(3) ISO 4217, e.g. CAD
billing_mode VARCHAR(20) one_time (single booking) or full_term (weekly / group)
allow_weekly TINYINT(1) Private only — may be reserved weekly for the term
capacity SMALLINT Group only — max enrolments; NULL for private
term_start DATE Group / term offerings — first day; NULL otherwise
term_end DATE Group / term offerings — last day; NULL otherwise
schedule_note VARCHAR(191) Group only — human-readable schedule, e.g. "Tuesdays 4:00pm"
is_active TINYINT(1) 0 = hidden from registration, 1 = bookable
created_at DATETIME Insertion time

Billing Mode

  • one_time — charged once at booking (a single private lesson).
  • full_term — charged in full upfront at registration (a weekly private reservation or a year-long group class). See payments.md.

Term Dates

Group classes carry a term: term_start is the date of the first class and term_end the last. The add-offering form takes a start date plus a sessions control — one-off (the term ends the day it starts) or weekly for N sessions (term_end = term_start + (N1) weeks, computed by Offering::weeklyTermEnd()). Dates are validated by Offering::normalizeDate() (strict Y-m-d); an invalid start date leaves both term columns NULL. The student-facing class card shows the date (one-off) or the date range with the weekly session count.

Admin Interface

Studio admin and instructors manage offerings under Offerings in wp-admin.

  • Studio admin (manage_offerings) manages offerings for any instructor.
  • Instructor (manage_offerings) manages only their own.
  • Each offering's intake questions are edited from the offering screen (see registration-questions.md).
  • The offerings list shows each offering's ID (needed for [us_group_classes offering="…"]) and its term dates.
  • Edit on a row reloads the page (?usc_edit=<id>) with the form prefilled; saving posts usc_action=update. Owner and currency are always preserved on update, so a form submission can never reassign an offering. Non-admin instructors can only load and update their own offerings.
  • The form includes a description textarea and an Active — open for registration checkbox (unchecking hides the offering from students without deleting it — the admin-UI counterpart of the REST is_active flag).

REST API

Method Endpoint Permission
GET /wp-json/us-scheduler/v1/offerings Public (active offerings only)
POST /wp-json/us-scheduler/v1/offerings manage_offerings
PATCH /wp-json/us-scheduler/v1/offerings/{id} manage_offerings + owner
DELETE /wp-json/us-scheduler/v1/offerings/{id} manage_offerings + owner

GET supports query params: instructor_id, kind.

Implementation

  • Repository: Unsupervised\Schedular\Offering\OfferingRepository
  • Model: Unsupervised\Schedular\Offering\Offering
  • Admin controller: Unsupervised\Schedular\Offering\OfferingController
  • REST endpoint: Unsupervised\Schedular\Offering\OfferingEndpoint

Tests

  • tests/Unit/Offering/OfferingControllerTest.php
  • tests/Unit/Offering/OfferingRepositoryTest.php
  • tests/Unit/Offering/OfferingTest.php