Files
unsupervised-scheduler/docs/features/offerings.md
T
thatguygriff 19e663d6fa
CI / Coding Standards (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 1m2s
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Extend availability (durations, weekly recurrence, calendar); price offerings in dollars
Availability (#2):
- us_availability gains offering_id, duration_minutes (default 60), and
  recurrence_group; AvailabilitySlot carries the new fields.
- AvailabilityRepository::createWeeklySeries() generates N weekly rows
  sharing a recurrence_group; findAvailable() filters by offering and
  duration. Date math uses DateTimeImmutable::modify() (the no-debug CI
  regex `dd\(` matches `->add(`).
- REST GET filters by offering_id/duration_minutes; POST accepts
  duration_minutes, offering_id, recurrence (single|weekly) + weeks.
- Admin form adds duration, an offering picker, and one-off/weekly options
  (OfferingRepository wired into AvailabilityController).
- booking.js renders an agenda calendar (slots grouped by day, with
  duration). The richer booking UX lands with the booking-flow work.

Offering price in dollars:
- Switch us_offerings.price_cents (INT) to price DECIMAL(10,2); Offering
  uses float $price. Admin form and REST take dollars.
- Fix a pre-existing misalignment in the Offering insert/update $wpdb
  format arrays (billing_mode/capacity/is_active were mapped to the wrong
  specifiers, which would corrupt values) via a single COLUMN_FORMATS list.

Also bump PHPStan to --memory-limit=1G in the lint script; 128M now
crashes analysis as the codebase has grown.

Refs #2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 15:43:48 -03:00

3.7 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). See payments.md.

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).

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/OfferingRepositoryTest.php
  • tests/Unit/Offering/OfferingTest.php