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]>
5.0 KiB
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). Seepayments.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 + (N−1) 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 postsusc_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_activeflag).
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.phptests/Unit/Offering/OfferingRepositoryTest.phptests/Unit/Offering/OfferingTest.php