Apply account credit on same-month rebook, surface credit on student page
CI / Coding Standards (pull_request) Successful in 27s
CI / Tests (PHP 8.2) (pull_request) Successful in 28s
CI / No Debug Code (pull_request) Successful in 9s
CI / Tests (PHP 8.5) (pull_request) Successful in 35s
CI / Tests (PHP 8.1) (pull_request) Successful in 37s
CI / Tests (PHP 8.3) (pull_request) Successful in 43s
CI / Static Analysis (pull_request) Successful in 51s
CI / Build Plugin Zip (pull_request) Skipped
CI / Coding Standards (pull_request) Successful in 27s
CI / Tests (PHP 8.2) (pull_request) Successful in 28s
CI / No Debug Code (pull_request) Successful in 9s
CI / Tests (PHP 8.5) (pull_request) Successful in 35s
CI / Tests (PHP 8.1) (pull_request) Successful in 37s
CI / Tests (PHP 8.3) (pull_request) Successful in 43s
CI / Static Analysis (pull_request) Successful in 51s
CI / Build Plugin Zip (pull_request) Skipped
A monthly/weekly lesson booked into a month whose billing date has already passed is charged at booking time by LessonBooker::settle, bypassing the daily scan where account credit is otherwise applied. So rebooking a cancelled paid lesson within the same month charged the family in full while their cancellation credit sat unused — billed twice for the same slot. Apply the payer's credit to that charge-at-booking payment for scheduled offerings, mirroring the daily scan: a payment fully covered by credit settles and confirms its lesson. Add PaymentService::findPayment so settle can re-read the row after applyCredits writes to it. Also surface the student's total account credit at the top of their detail page when they hold a balance, so the studio sees it at a glance. Co-authored-by: anthropic/claude-opus-4-8
This commit is contained in:
co-authored by
anthropic/claude-opus-4-8
parent
d8d842b1ef
commit
e7de627752
@@ -184,6 +184,7 @@ class LessonBooker {
|
||||
? $offering->price
|
||||
: $offering->price * count( $ids );
|
||||
|
||||
$payerId = $this->guardians->payerFor( $studentId );
|
||||
$payment = $this->payments->createForRegistration(
|
||||
Payment::REG_LESSON,
|
||||
$anchorId,
|
||||
@@ -192,9 +193,23 @@ class LessonBooker {
|
||||
$amount,
|
||||
$offering->currency,
|
||||
$offering->etransferEmail,
|
||||
payerId: $this->guardians->payerFor( $studentId )
|
||||
payerId: $payerId
|
||||
);
|
||||
|
||||
// A scheduled lesson charged at booking — the extra lesson added to (or a
|
||||
// cancelled one rebooked within) an already-billed month — skips the daily
|
||||
// scan, which is the only place credit is otherwise applied. Apply the
|
||||
// payer's account credit here too, so a cancellation credit settles the
|
||||
// rebooking instead of the family being charged twice for the same slot. A
|
||||
// pending payment fully covered by credit is settled and its lesson
|
||||
// confirmed; applyCredits is a no-op when the payer holds no balance. The
|
||||
// one-time pay-now flow is left alone: its payment step already fronts the
|
||||
// student, and credit there is a manual studio decision.
|
||||
if ( $offering->isScheduledBilling() && null !== $payment && null !== $payment->id && Payment::STATUS_PENDING === $payment->status ) {
|
||||
$this->payments->applyCredits( $payerId, [ $payment ] );
|
||||
$payment = $this->payments->findPayment( (int) $payment->id ) ?? $payment;
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => null !== $payment && $payment->isPaid() ? Lesson::STATUS_CONFIRMED : Lesson::STATUS_PENDING,
|
||||
'payment' => $payment,
|
||||
|
||||
@@ -116,6 +116,15 @@ class PaymentService {
|
||||
return $this->payments->markNoticed( $paymentId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-read a payment from the ledger — the caller's way to pick up a status or
|
||||
* credit change {@see applyCredits} wrote straight to the row, since the
|
||||
* Payment object it holds is immutable. Delegates to the ledger.
|
||||
*/
|
||||
public function findPayment( int $paymentId ): ?Payment {
|
||||
return $this->payments->findById( $paymentId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Studio-admin confirmation that a pending payment (e-transfer) was received.
|
||||
* Marks it paid, confirms the registration, and emails the receipt.
|
||||
|
||||
Reference in New Issue
Block a user