feat: conditional & match funding pledges — deeply integrated across entire product
- Schema: isConditional, conditionType, conditionText, conditionThreshold, conditionMet, conditionMetAt on Pledge - Pledge form: 'This is a match pledge' toggle after amount selection - Two modes: threshold (if target is reached) and match (match funding) - Goal amount passed through from event - Auto-trigger: when total raised hits threshold, conditional pledges unlock automatically - WhatsApp notification sent to donor when unlocked - Threshold check runs after every pledge creation AND every status change - Cron: skips conditional pledges until conditionMet=true (no premature reminders) - Dashboard Home: progress bar shows conditional segment (amber), stats grid adds Conditional column - Dashboard Money: conditional/unlocked badge on pledge rows - Dashboard Collect: hero shows conditional total in amber - Dashboard Reports: financial summary shows conditional breakdown - Donor 'My Pledges': conditional card with condition text + activation status - Confirmation step: specialized messaging for match pledges - CRM export: includes is_conditional, condition_type, condition_text, condition_met columns - Status guide: conditional status explained in human language
This commit is contained in:
@@ -82,7 +82,7 @@ class ScheduledGivingDashboard extends Page
|
||||
$currentIds = $this->currentSeasonIds($c->id);
|
||||
$expiredIds = $realIds->diff($currentIds);
|
||||
|
||||
// Current season payment stats
|
||||
// Current season payment stats — separate due vs future
|
||||
$currentPayments = null;
|
||||
if ($currentIds->isNotEmpty()) {
|
||||
$currentPayments = DB::table('scheduled_giving_payments')
|
||||
@@ -91,10 +91,12 @@ class ScheduledGivingDashboard extends Page
|
||||
->selectRaw("
|
||||
COUNT(*) as total,
|
||||
SUM(is_paid = 1) as paid,
|
||||
SUM(is_paid = 0) as pending,
|
||||
SUM(is_paid = 0 AND attempts > 0) as failed,
|
||||
SUM(is_paid = 0 AND expected_at <= NOW()) as failed,
|
||||
SUM(is_paid = 0 AND expected_at > NOW()) as scheduled,
|
||||
SUM(expected_at <= NOW()) as due,
|
||||
SUM(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) as collected,
|
||||
SUM(CASE WHEN is_paid = 0 THEN amount ELSE 0 END) as pending_amount,
|
||||
SUM(CASE WHEN is_paid = 0 AND expected_at <= NOW() THEN amount ELSE 0 END) as failed_amount,
|
||||
SUM(CASE WHEN is_paid = 0 AND expected_at > NOW() THEN amount ELSE 0 END) as scheduled_amount,
|
||||
AVG(CASE WHEN is_paid = 1 THEN amount ELSE NULL END) as avg_amount,
|
||||
MIN(CASE WHEN is_paid = 0 AND expected_at > NOW() THEN expected_at ELSE NULL END) as next_payment
|
||||
")
|
||||
@@ -121,6 +123,9 @@ class ScheduledGivingDashboard extends Page
|
||||
$fullyPaid = $row->cnt ?? 0;
|
||||
}
|
||||
|
||||
$due = (int) ($currentPayments->due ?? 0);
|
||||
$paid = (int) ($currentPayments->paid ?? 0);
|
||||
|
||||
$result[] = [
|
||||
'campaign' => $c,
|
||||
'all_time_subscribers' => $realIds->count(),
|
||||
@@ -130,13 +135,16 @@ class ScheduledGivingDashboard extends Page
|
||||
// Current season
|
||||
'current_subscribers' => $currentIds->count(),
|
||||
'expired_subscribers' => $expiredIds->count(),
|
||||
'current_payments' => (int) ($currentPayments->total ?? 0),
|
||||
'current_paid' => (int) ($currentPayments->paid ?? 0),
|
||||
'current_pending' => (int) ($currentPayments->pending ?? 0),
|
||||
'current_failed' => (int) ($currentPayments->failed ?? 0),
|
||||
'current_collected' => ($currentPayments->collected ?? 0) / 100,
|
||||
'current_pending_amount' => ($currentPayments->pending_amount ?? 0) / 100,
|
||||
'total_payments' => (int) ($currentPayments->total ?? 0),
|
||||
'due_payments' => $due,
|
||||
'paid_payments' => $paid,
|
||||
'failed_payments' => (int) ($currentPayments->failed ?? 0),
|
||||
'scheduled_payments' => (int) ($currentPayments->scheduled ?? 0),
|
||||
'collected' => ($currentPayments->collected ?? 0) / 100,
|
||||
'failed_amount' => ($currentPayments->failed_amount ?? 0) / 100,
|
||||
'scheduled_amount' => ($currentPayments->scheduled_amount ?? 0) / 100,
|
||||
'avg_per_night' => ($currentPayments->avg_amount ?? 0) / 100,
|
||||
'collection_rate' => $due > 0 ? round($paid / $due * 100, 1) : 0,
|
||||
'fully_completed' => $fullyPaid,
|
||||
'dates' => $c->dates ?? [],
|
||||
'total_nights' => $totalNights,
|
||||
@@ -169,25 +177,32 @@ class ScheduledGivingDashboard extends Page
|
||||
->whereNull('deleted_at')
|
||||
->selectRaw("
|
||||
SUM(is_paid = 1) as paid,
|
||||
SUM(is_paid = 0 AND attempts > 0) as failed,
|
||||
SUM(expected_at <= NOW()) as due,
|
||||
SUM(is_paid = 0 AND expected_at <= NOW()) as failed,
|
||||
SUM(is_paid = 0 AND expected_at > NOW()) as scheduled,
|
||||
SUM(CASE WHEN is_paid = 1 THEN amount ELSE 0 END) / 100 as collected,
|
||||
SUM(CASE WHEN is_paid = 0 THEN amount ELSE 0 END) / 100 as pending,
|
||||
COUNT(*) as total
|
||||
SUM(CASE WHEN is_paid = 0 AND expected_at <= NOW() THEN amount ELSE 0 END) / 100 as failed_amount,
|
||||
SUM(CASE WHEN is_paid = 0 AND expected_at > NOW() THEN amount ELSE 0 END) / 100 as scheduled_amount
|
||||
")
|
||||
->first();
|
||||
}
|
||||
|
||||
$due = (int) ($currentStats->due ?? 0);
|
||||
$paid = (int) ($currentStats->paid ?? 0);
|
||||
|
||||
return [
|
||||
'total_subscribers' => $realIds->count(),
|
||||
'current_subscribers' => $currentIds->count(),
|
||||
'expired_subscribers' => $realIds->count() - $currentIds->count(),
|
||||
'all_time_collected' => (float) ($allTime->collected ?? 0),
|
||||
'current_collected' => (float) ($currentStats->collected ?? 0),
|
||||
'current_pending' => (float) ($currentStats->pending ?? 0),
|
||||
'current_failed' => (int) ($currentStats->failed ?? 0),
|
||||
'collection_rate' => ($currentStats->total ?? 0) > 0
|
||||
? round($currentStats->paid / $currentStats->total * 100, 1)
|
||||
: 0,
|
||||
'collected' => (float) ($currentStats->collected ?? 0),
|
||||
'failed_amount' => (float) ($currentStats->failed_amount ?? 0),
|
||||
'scheduled_amount' => (float) ($currentStats->scheduled_amount ?? 0),
|
||||
'failed_count' => (int) ($currentStats->failed ?? 0),
|
||||
'scheduled_count' => (int) ($currentStats->scheduled ?? 0),
|
||||
'due_payments' => $due,
|
||||
'paid_payments' => $paid,
|
||||
'collection_rate' => $due > 0 ? round($paid / $due * 100, 1) : 0,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user