Credit students for cancelled paid lessons #107

Merged
thatguygriff merged 1 commits from feature/credit-cancelled-paid-lessons into main 2026-07-24 18:40:10 +00:00
Owner

What

When a lesson that was already paid for is cancelled, the student is now credited that money instead of it being a manual refund, and the daily scheduled-billing scan applies any available credit against their upcoming charges before emailing the notice.

How it works

  • Credit on cancelPaymentService::creditForCancelledLesson issues one lesson's share of the covering payment's total (whole amount for a single/weekly-scheduled lesson; total ÷ N for a monthly charge or a full-term series). The original payment is left untouched. Wired into all three cancel paths: student self-cancel (BookingEndpoint::cancel), instructor status update (BookingEndpoint::updateStatus), and admin student-detail cancel (StudentActions::cancelLesson). Guards prevent double-crediting and only credit genuinely-paid payments.
  • Apply at billingPaymentService::applyCredits draws the balance down FIFO across the run's charges; each payment's new credit_applied column reduces Payment::netDue(), a fully-covered charge is marked paid-by-credit and leaves the admin queue, and the notice email shows the credit applied and reduced total.
  • Admin visibility — the student detail page shows the student's available credit balance and a table of every credit (date, reason, amount, remaining, status), gated by manage_billing.

Data model

  • New us_credits table and us_payments.credit_applied column. Ships as part of the unreleased 1.2.0 (same release as scheduled billing) — version intentionally not bumped.

Notes

  • Reporting counts gross (documented in docs/features/credits.md): the original paid lesson and a later credit-covered charge both count as paid, a direct consequence of leaving the original payment intact rather than representing a partial refund.
  • Group-enrolment cancellations are not auto-credited (scope was private lessons).

Tests

  • composer test — 585 tests pass
  • composer lint (PHPStan level 10) — clean
  • composer cs (PHPCS) — clean

🤖 Generated with Claude Code

## What When a lesson that was **already paid for** is cancelled, the student is now credited that money instead of it being a manual refund, and the daily scheduled-billing scan applies any available credit against their upcoming charges before emailing the notice. ## How it works - **Credit on cancel** — `PaymentService::creditForCancelledLesson` issues one lesson's share of the covering payment's total (whole amount for a single/weekly-scheduled lesson; `total ÷ N` for a monthly charge or a full-term series). The original payment is left untouched. Wired into all three cancel paths: student self-cancel (`BookingEndpoint::cancel`), instructor status update (`BookingEndpoint::updateStatus`), and admin student-detail cancel (`StudentActions::cancelLesson`). Guards prevent double-crediting and only credit genuinely-paid payments. - **Apply at billing** — `PaymentService::applyCredits` draws the balance down FIFO across the run's charges; each payment's new `credit_applied` column reduces `Payment::netDue()`, a fully-covered charge is marked paid-by-credit and leaves the admin queue, and the notice email shows the credit applied and reduced total. - **Admin visibility** — the student detail page shows the student's available credit balance and a table of every credit (date, reason, amount, remaining, status), gated by `manage_billing`. ## Data model - New `us_credits` table and `us_payments.credit_applied` column. Ships as part of the **unreleased 1.2.0** (same release as scheduled billing) — version intentionally not bumped. ## Notes - Reporting counts gross (documented in `docs/features/credits.md`): the original paid lesson and a later credit-covered charge both count as `paid`, a direct consequence of leaving the original payment intact rather than representing a partial refund. - Group-enrolment cancellations are not auto-credited (scope was private lessons). ## Tests - `composer test` — 585 tests pass - `composer lint` (PHPStan level 10) — clean - `composer cs` (PHPCS) — clean 🤖 Generated with [Claude Code](https://claude.com/claude-code)
thatguygriff added 1 commit 2026-07-24 18:33:51 +00:00
Credit students for cancelled paid lessons
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / PHPStan (pull_request) Successful in 3m12s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 2m52s
e8e66eef3c
Cancelling a lesson that was already paid for now credits the student
that money instead of leaving it as a manual refund, and the daily
scheduled-billing scan applies any available credit against their due
charges before emailing the notice.

- New us_credits ledger + us_payments.credit_applied column (Payment::netDue).
- PaymentService::creditForCancelledLesson issues a per-lesson share of the
  covering payment's total; wired into all three cancel paths (student
  self-cancel, instructor status update, admin student-detail cancel).
- PaymentService::applyCredits draws credit down FIFO across a run's charges,
  marking a fully-covered charge paid-by-credit; the notice shows the credit
  applied and reduced total, and the admin queue shows net due.
- Student detail page shows a student's credit balance and history.

Ships as part of the unreleased 1.2.0 (same release as scheduled billing).

Tests: composer test (585), composer lint, composer cs all pass.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
thatguygriff merged commit f552c3952a into main 2026-07-24 18:40:10 +00:00
thatguygriff deleted branch feature/credit-cancelled-paid-lessons 2026-07-24 18:40:11 +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#107