addDays($windowDays); $dryRun = (bool) $this->option('dry-run'); $abos = UserAbo::query() ->with(['user', 'shopping_user']) ->where('active', true) ->where('status', 2) ->whereIn('is_for', ['me', 'ot']) ->whereDate('next_date', $targetDate->toDateString()) ->where(function ($query) use ($targetDate): void { $query->whereNull('onetime_reminder_sent_for') ->orWhereDate('onetime_reminder_sent_for', '!=', $targetDate->toDateString()); }) ->get(); $sent = 0; $skipped = 0; foreach ($abos as $abo) { $email = $this->recipientEmail($abo); if (! $email) { $skipped++; continue; } // Symfony Mailer lehnt Nicht-ASCII im local-part ab. Vorab pruefen, damit // im Log die Adresse statt einer Transport-Exception steht. if (! filter_var($email, FILTER_VALIDATE_EMAIL)) { $skipped++; Log::channel('abo_order')->error('AboSendExecutionReminder: Empfaengeradresse nicht versandfaehig', [ 'abo_id' => $abo->id, 'email' => $email, ]); continue; } if ($dryRun) { $sent++; continue; } try { Mail::to($email) ->locale($this->recipientLocale($abo)) ->send(new MailAboExecutionReminder($abo)); $abo->onetime_reminder_sent_for = $abo->next_date; $abo->save(); $sent++; } catch (\Throwable $e) { $skipped++; Log::channel('abo_order')->error('AboSendExecutionReminder: Versand fehlgeschlagen', [ 'abo_id' => $abo->id, 'error' => $e->getMessage(), ]); } } $this->info(sprintf( 'Reminder %s: %d versendet, %d übersprungen (Zieldatum %s, Fenster %d Tage).', $dryRun ? '(dry-run)' : '', $sent, $skipped, $targetDate->toDateString(), $windowDays )); return self::SUCCESS; } private function recipientEmail(UserAbo $abo): ?string { if ($abo->email) { return $abo->email; } if ($abo->is_for === 'me') { return $abo->user?->email; } return $abo->shopping_user?->billing_email; } private function recipientLocale(UserAbo $abo): string { $locale = $abo->is_for === 'me' ? $abo->user?->getLocale() : $abo->shopping_user?->getLocale(); return $locale ?: 'de'; } }