Quote a monthly private lesson per lesson, a group class monthly
CI / Tests (PHP 8.1) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 2m52s
CI / PHPStan (pull_request) Successful in 3m0s

A monthly charge covers every lesson that falls in the month, so a private
lesson's fee reads "50.00 CAD per lesson monthly" — the figure on its own would
suggest the whole month costs 50.00. A group class is enrolled in once, as a
single schedule, so its price is quoted as the monthly figure it is.

The pay agreement follows the same split: per-lesson for a monthly private
lesson, the monthly figure for a monthly group class. Weekly, full-term and
at-booking wording is unchanged.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-07-28 15:07:46 -03:00
co-authored by Claude Opus 5
parent 9344ab7193
commit a276d53c1b
4 changed files with 57 additions and 15 deletions
+22 -6
View File
@@ -102,16 +102,30 @@ After booking, the destination on a payment can be corrected per booking:
Every price a student is shown on the front end carries its **cadence** — the
offering's `billing_mode` in the words the student needs:
| `billing_mode` | Shown as | Explained beneath as |
|----------------|--------------|------------------------------------------------------------|
| `one_time` | `at booking` | Charged once, when you book. |
| `full_term` | `up front` | Charged once, up front, for the whole term. |
| `weekly` | `weekly` | Charged for each lesson, 24 hours before it starts. |
| `monthly` | `monthly` | Charged on the 1st of each month, for that month's lessons.|
| `billing_mode` | Shown as | Explained beneath as |
|----------------|-----------------------------------|------------------------------------------------------------|
| `one_time` | `at booking` | Charged once, when you book. |
| `full_term` | `up front` | Charged once, up front, for the whole term. |
| `weekly` | `weekly` | Charged for each lesson, 24 hours before it starts. |
| `monthly` | `per lesson monthly` / `monthly` | Charged on the 1st of each month, for that month's lessons.|
So a lesson type reads `50.00 CAD at booking` in the booking form's type picker,
and a group class card reads `120.00 CAD up front`. A free offering shows `Free`.
**`monthly` reads differently per offering kind.** A private lesson's monthly
charge is that month's lessons × the fee, so the fee is quoted **per lesson**
(`50.00 CAD per lesson monthly`). A group class is enrolled in once, as one
schedule, so its figure is quoted as it stands (`120.00 CAD monthly`) — see
`isPerLessonMonthly()` in `assets/js/pricing.js`.
> **Caveat.** `ScheduledBillingRunner::billGroupMonthly()` bills a monthly group
> class `price × sessions in the month`, the same per-session multiplication used
> for private lessons — so a class meeting weekly is charged roughly four times
> the quoted `120.00 CAD monthly` figure. The display deliberately quotes the
> price as a monthly figure; closing the gap means either billing a monthly group
> class once per month regardless of session count, or restoring the per-lesson
> wording for group classes too.
Before a booking or enrolment can be submitted, the form shows the price again as
a summary block with a **required agreement checkbox** — the second confirmation,
distinct from the policy acceptances above it:
@@ -133,6 +147,8 @@ Cadence-specific wording:
simply not claimed, so the real charge can come in under it.
- **`weekly` / `monthly`** — nothing is taken at registration, so the agreement is
to the recurring charge: "I agree to pay 56.50 CAD per lesson, billed monthly."
A monthly **group class** agrees to its monthly figure instead ("I agree to pay
138.00 CAD monthly."), matching how its price is quoted on the card.
All of this lives in `assets/js/pricing.js` (`window.usPricing`), shared by the
booking and group-class flows so a price reads the same wherever it is met. The