Commit 158ba901 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Enforce renewal on 1st of month for all participants

New billing rule: everyone renews on the 1st, full program price.
New enrollees pay pro-rata for remainder of first month, then renew
normally on the 1st going forward.

- advanceEnrollmentBillingDate() always sets next_billing_date to 1st
  of next month
- calculateFirstBillingDate() same: 1st of next month
- DB: set 44 un-renewed enrollments to 2026-08-01 (left 207 already-
  renewed at 2026-09-01 untouched)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent db0d81f9
......@@ -442,21 +442,8 @@ private function calculateFirstBillingDate(?TrainingProgram $program): ?string
return null;
}
// Default to monthly day-1 if not set
$cycle = $program->billing_cycle ?? 'monthly';
$billingDay = $program->billing_day ?? 1;
$start = now();
$next = match ($cycle) {
'monthly' => $this->snapToDay($start->copy()->addMonth(), $billingDay),
'quarterly' => $start->copy()->addMonths(3),
'semi_annual' => $start->copy()->addMonths(6),
'annual' => $start->copy()->addYear(),
'per_duration' => $start->copy()->addWeeks($program->program_duration_weeks ?? 4),
default => $this->snapToDay($start->copy()->addMonth(), $billingDay),
};
return $next->toDateString();
// All renewals happen on the 1st of next month
return now()->addMonth()->startOfMonth()->toDateString();
}
private function snapToDay(\Illuminate\Support\Carbon $date, ?int $billingDay): \Illuminate\Support\Carbon
......
......@@ -188,7 +188,7 @@ private function generatePendingRenewals(): void
$enrollments = Enrollment::where('participant_id', $this->selected_participant_id)
->where('status', EnrollmentStatus::Active)
->whereNotNull('next_billing_date')
->where('next_billing_date', '<=', now()->endOfMonth()->toDateString())
->where('next_billing_date', '<=', now()->toDateString())
->whereHas('program', function ($q) {
$q->whereIn('renewal_policy', [
RenewalPolicy::AutoRenew->value,
......@@ -213,22 +213,8 @@ private function generatePendingRenewals(): void
private function advanceEnrollmentBillingDate(Enrollment $enrollment): void
{
$program = $enrollment->program;
$current = $enrollment->next_billing_date;
$next = match ($program->billing_cycle) {
'monthly' => $current->copy()->addMonth(),
'quarterly' => $current->copy()->addMonths(3),
'semi_annual' => $current->copy()->addMonths(6),
'annual' => $current->copy()->addYear(),
'per_duration' => $current->copy()->addWeeks($program->program_duration_weeks ?? 4),
default => $current->copy()->addMonth(),
};
if ($program->billing_cycle === 'monthly' && $program->billing_day) {
$maxDay = $next->daysInMonth;
$next->day = min($program->billing_day, $maxDay);
}
// All renewals are on the 1st of next month — no exceptions
$next = now()->addMonth()->startOfMonth();
$enrollment->update(['next_billing_date' => $next->toDateString()]);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment