whereNotNull('onboarded_at') ->count(); $stuckCount = User::query() ->whereNotNull('onboarded_at') ->whereHas('bankingConnections') ->whereDoesntHave('subscriptions', function (Builder $query): void { $query->where(function (Builder $query): void { $query->whereIn('stripe_status', ['active', 'trialing', 'past_due']) ->orWhere(function (Builder $query): void { $query->where('stripe_status', 'canceled') ->where('ends_at', '>', now()); }); }); }) ->count(); $stuckPct = $onboardedCount > 0 ? round($stuckCount / $onboardedCount * 100, 2) : 0.0; $previous = StuckCohortSnapshot::query() ->whereDate('date', '<', today()) ->orderByDesc('date') ->first(); $snapshot = StuckCohortSnapshot::query()->updateOrCreate( ['date' => today()], [ 'onboarded_count' => $onboardedCount, 'stuck_count' => $stuckCount, 'stuck_pct' => $stuckPct, ], ); return [ 'snapshot' => $snapshot, 'previous' => $previous, 'pctDelta' => $previous !== null ? round($stuckPct - (float) $previous->stuck_pct, 2) : null, 'stuckDelta' => $previous !== null ? $stuckCount - $previous->stuck_count : null, ]; } }