*/ public function allowedMilestones(): array { return array_keys((array) config('transfer.recipient_email_milestones', [])); } /** @return array */ public function milestoneOptions(): array { return (array) config('transfer.recipient_email_milestones', []); } /** @param list $milestones */ public function normalizeMilestones(array $milestones): array { $allowed = $this->allowedMilestones(); return array_values(array_unique(array_filter( $milestones, static fn ($milestone) => in_array((string) $milestone, $allowed, true), ))); } public function notifyRecipient(Transfer $transfer, string $milestone): bool { $email = trim((string) $transfer->recipient_email); if ($email === '' || ! $transfer->wantsRecipientMilestone($milestone)) { return false; } if ($transfer->recipientMilestoneSent($milestone)) { return false; } Notification::route('mail', $email)->notify( new TransferRecipientNotification($transfer->loadMissing(['files', 'qrCode', 'user']), $milestone), ); $transfer->markRecipientMilestoneSent($milestone); return true; } public function processDueMilestones(): int { $sent = 0; Transfer::query() ->accessible() ->whereNotNull('recipient_email') ->whereNotNull('recipient_email_milestones') ->with(['files', 'qrCode', 'user']) ->chunkById(100, function ($transfers) use (&$sent) { foreach ($transfers as $transfer) { foreach ($this->dueDayMilestones($transfer) as $milestone) { if ($this->notifyRecipient($transfer, $milestone)) { $sent++; } } } }); return $sent; } /** @return list */ private function dueDayMilestones(Transfer $transfer): array { if (! $transfer->paid_until instanceof CarbonInterface || $transfer->paid_until->isPast()) { return []; } $daysRemaining = (int) now()->startOfDay()->diffInDays( $transfer->paid_until->copy()->startOfDay(), false, ); $due = []; foreach (['7', '3', '1'] as $milestone) { if ($daysRemaining === (int) $milestone && $transfer->wantsRecipientMilestone($milestone)) { $due[] = $milestone; } } return $due; } }