23-01-2026
This commit is contained in:
parent
8fd1f4d451
commit
389d5d1820
59 changed files with 9642 additions and 883 deletions
122
app/Console/Commands/CleanupNewsletterBlockedEmails.php
Normal file
122
app/Console/Commands/CleanupNewsletterBlockedEmails.php
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\NewsletterContact;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CleanupNewsletterBlockedEmails extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'newsletter:cleanup-blocked-emails {--dry-run : Nur anzeigen, ohne zu löschen}';
|
||||
|
||||
/**
|
||||
* The console description of the command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Entfernt Newsletter-Kontakte mit blockierten E-Mail-Adressen (Alias/Proxy von Buchungsplattformen)';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Suche nach blockierten E-Mail-Adressen...');
|
||||
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
// Liste der blockierten Domains
|
||||
$blockedDomains = [
|
||||
'@guest.booking.com',
|
||||
'@messages.homeaway.com',
|
||||
'@fewo.check24.de',
|
||||
'@booking.com',
|
||||
'@homeaway.com',
|
||||
'@check24.de',
|
||||
'@partner.booking.com',
|
||||
];
|
||||
|
||||
$stats = [
|
||||
'found' => 0,
|
||||
'deleted' => 0,
|
||||
];
|
||||
|
||||
// Hole alle Newsletter-Kontakte
|
||||
$contacts = NewsletterContact::all();
|
||||
|
||||
$this->info("Prüfe {$contacts->count()} Kontakte...");
|
||||
|
||||
$bar = $this->output->createProgressBar($contacts->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
$emailLower = strtolower($contact->email);
|
||||
$isBlockedEmail = false;
|
||||
|
||||
foreach ($blockedDomains as $domain) {
|
||||
if (str_ends_with($emailLower, strtolower($domain))) {
|
||||
$isBlockedEmail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isBlockedEmail) {
|
||||
$stats['found']++;
|
||||
|
||||
if ($dryRun) {
|
||||
$this->newLine();
|
||||
$this->warn("Würde löschen: {$contact->email} (ID: {$contact->id})");
|
||||
} else {
|
||||
// Log erstellen vor dem Löschen
|
||||
$contact->logs()->create([
|
||||
'action' => 'deleted',
|
||||
'description' => 'Kontakt entfernt - blockierte E-Mail-Domain (Buchungsplattform-Alias)',
|
||||
]);
|
||||
|
||||
$contact->delete();
|
||||
$stats['deleted']++;
|
||||
}
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
// Statistiken ausgeben
|
||||
if ($dryRun) {
|
||||
$this->info('Dry-Run abgeschlossen!');
|
||||
$this->table(
|
||||
['Statistik', 'Anzahl'],
|
||||
[
|
||||
['Gefundene blockierte E-Mails', $stats['found']],
|
||||
]
|
||||
);
|
||||
|
||||
if ($stats['found'] > 0) {
|
||||
$this->newLine();
|
||||
$this->info('Führe den Befehl ohne --dry-run aus, um die Kontakte zu löschen:');
|
||||
$this->comment('php artisan newsletter:cleanup-blocked-emails');
|
||||
}
|
||||
} else {
|
||||
$this->info('Bereinigung abgeschlossen!');
|
||||
$this->table(
|
||||
['Statistik', 'Anzahl'],
|
||||
[
|
||||
['Gefundene blockierte E-Mails', $stats['found']],
|
||||
['Gelöschte Kontakte', $stats['deleted']],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
254
app/Console/Commands/SyncNewsletterFerienwohnungen.php
Normal file
254
app/Console/Commands/SyncNewsletterFerienwohnungen.php
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\TravelUserBookingFewo;
|
||||
use App\Models\TravelUser;
|
||||
use App\Models\NewsletterContact;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncNewsletterFerienwohnungen extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'newsletter:sync-ferienwohnungen {--force : Force full sync}';
|
||||
|
||||
/**
|
||||
* The console description of the command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Synchronisiert Ferienwohnungs-Buchungen mit Newsletter-Kontakten';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starte Synchronisation von Ferienwohnungs-Buchungen...');
|
||||
|
||||
$force = $this->option('force');
|
||||
|
||||
// Statistiken
|
||||
$stats = [
|
||||
'processed' => 0,
|
||||
'created' => 0,
|
||||
'updated' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
];
|
||||
|
||||
// Hole alle Buchungen mit TravelUser und invoice_number
|
||||
$query = TravelUserBookingFewo::with(['travel_user'])
|
||||
->whereNotNull('travel_user_id')
|
||||
->whereNotNull('invoice_number')
|
||||
->where('invoice_number', '!=', '')
|
||||
// Nur wenn invoice_number eine reine Nummer ist (keine Storno etc.)
|
||||
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
|
||||
->whereHas('travel_user', function ($q) {
|
||||
$q->whereNotNull('email')
|
||||
->where('email', '!=', '');
|
||||
})
|
||||
// Nur Buchungen, bei denen die Reise bereits beendet ist (to_date in der Vergangenheit)
|
||||
->whereNotNull('to_date')
|
||||
->where('to_date', '<', now());
|
||||
|
||||
if (!$force) {
|
||||
// Nur Buchungen der letzten 30 Tage (basierend auf Rückreisedatum) wenn nicht --force
|
||||
$query->where('to_date', '>=', now()->subDays(30));
|
||||
}
|
||||
|
||||
$bookings = $query->get();
|
||||
|
||||
$this->info("Verarbeite {$bookings->count()} Buchungen...");
|
||||
|
||||
$bar = $this->output->createProgressBar($bookings->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
try {
|
||||
$stats['processed']++;
|
||||
|
||||
$travelUser = $booking->travel_user;
|
||||
|
||||
// Validiere E-Mail
|
||||
if (!$travelUser || !$travelUser->email || !filter_var($travelUser->email, FILTER_VALIDATE_EMAIL)) {
|
||||
$stats['skipped']++;
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filtere Alias/Proxy E-Mail-Adressen von Buchungsplattformen
|
||||
$blockedDomains = [
|
||||
'@guest.booking.com',
|
||||
'@messages.homeaway.com',
|
||||
'@fewo.check24.de',
|
||||
'@booking.com',
|
||||
'@homeaway.com',
|
||||
'@check24.de',
|
||||
'@partner.booking.com',
|
||||
];
|
||||
|
||||
$emailLower = strtolower($travelUser->email);
|
||||
$isBlockedEmail = false;
|
||||
foreach ($blockedDomains as $domain) {
|
||||
if (str_ends_with($emailLower, strtolower($domain))) {
|
||||
$isBlockedEmail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isBlockedEmail) {
|
||||
$stats['skipped']++;
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prüfe ob invoice_number wirklich eine reine Zahl ist
|
||||
if (!preg_match('/^[0-9]+$/', $booking->invoice_number)) {
|
||||
$stats['skipped']++;
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generiere Hash für Duplikat-Erkennung
|
||||
$syncHash = NewsletterContact::generateSyncHash(
|
||||
$travelUser->email,
|
||||
NewsletterContact::SOURCE_BOOKING_FERIENWOHNUNGEN
|
||||
);
|
||||
|
||||
// Suche oder erstelle Kontakt
|
||||
$contact = NewsletterContact::withTrashed()
|
||||
->where('email', strtolower(trim($travelUser->email)))
|
||||
->first();
|
||||
|
||||
$isNew = false;
|
||||
|
||||
if (!$contact) {
|
||||
// Neuer Kontakt
|
||||
$contact = new NewsletterContact();
|
||||
$isNew = true;
|
||||
$stats['created']++;
|
||||
} else {
|
||||
// Wenn gelöscht, wiederherstellen
|
||||
if ($contact->trashed()) {
|
||||
$contact->restore();
|
||||
}
|
||||
$stats['updated']++;
|
||||
}
|
||||
|
||||
// Aktualisiere Kontaktdaten
|
||||
$contact->email = strtolower(trim($travelUser->email));
|
||||
$contact->firstname = $travelUser->first_name ?: $contact->firstname;
|
||||
$contact->lastname = $travelUser->last_name ?: $contact->lastname;
|
||||
|
||||
// Setze Gruppe Ferienwohnungen
|
||||
$contact->group_ferienwohnungen = true;
|
||||
|
||||
// Source nur bei neuem Kontakt setzen (wenn noch nicht aus Kulturreisen)
|
||||
if ($isNew) {
|
||||
$contact->source = NewsletterContact::SOURCE_BOOKING_FERIENWOHNUNGEN;
|
||||
$contact->subscribed_at = $booking->booking_date ?
|
||||
\Carbon\Carbon::parse($booking->booking_date) :
|
||||
$booking->created_at;
|
||||
}
|
||||
|
||||
// Referenz zum TravelUser
|
||||
$contact->travel_user_id = $travelUser->id;
|
||||
|
||||
// Aktualisiere Buchungsstatistiken
|
||||
// Nur Buchungen mit invoice_number (reine Nummer) zählen
|
||||
$userBookings = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
|
||||
->whereNotNull('invoice_number')
|
||||
->where('invoice_number', '!=', '')
|
||||
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
|
||||
->count();
|
||||
|
||||
$contact->total_bookings_ferienwohnungen = $userBookings;
|
||||
|
||||
// Letztes Buchungsdatum
|
||||
$lastBooking = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
|
||||
->whereNotNull('invoice_number')
|
||||
->where('invoice_number', '!=', '')
|
||||
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
|
||||
->orderBy('booking_date', 'DESC')
|
||||
->first();
|
||||
|
||||
if ($lastBooking && $lastBooking->booking_date) {
|
||||
$lastBookingDate = \Carbon\Carbon::parse($lastBooking->booking_date);
|
||||
if (!$contact->last_booking_at || $lastBookingDate->gt($contact->last_booking_at)) {
|
||||
$contact->last_booking_at = $lastBookingDate;
|
||||
}
|
||||
}
|
||||
|
||||
// Letztes Reiseenddatum (to_date) - nur abgeschlossene Reisen
|
||||
$lastTravelEndBooking = TravelUserBookingFewo::where('travel_user_id', $travelUser->id)
|
||||
->whereNotNull('invoice_number')
|
||||
->where('invoice_number', '!=', '')
|
||||
->whereRaw('invoice_number REGEXP "^[0-9]+$"')
|
||||
->whereNotNull('to_date')
|
||||
->where('to_date', '<', now())
|
||||
->orderBy('to_date', 'DESC')
|
||||
->first();
|
||||
|
||||
if ($lastTravelEndBooking && $lastTravelEndBooking->to_date) {
|
||||
$lastTravelEndDate = \Carbon\Carbon::parse($lastTravelEndBooking->to_date);
|
||||
if (!$contact->last_travel_end_date || $lastTravelEndDate->gt($contact->last_travel_end_date)) {
|
||||
$contact->last_travel_end_date = $lastTravelEndDate;
|
||||
}
|
||||
}
|
||||
|
||||
// Status
|
||||
if ($isNew || $contact->status === NewsletterContact::STATUS_INACTIVE) {
|
||||
$contact->status = NewsletterContact::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
$contact->sync_hash = $syncHash;
|
||||
$contact->last_synced_at = now();
|
||||
|
||||
$contact->save();
|
||||
|
||||
// Log erstellen
|
||||
if ($isNew) {
|
||||
$contact->logs()->create([
|
||||
'action' => 'booking_added',
|
||||
'description' => 'Kontakt durch Ferienwohnungs-Buchung erstellt',
|
||||
'metadata' => [
|
||||
'booking_id' => $booking->id,
|
||||
'invoice_number' => $booking->invoice_number,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$stats['errors']++;
|
||||
$this->error("Fehler bei Buchung {$booking->id}: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
// Statistiken ausgeben
|
||||
$this->info('Synchronisation abgeschlossen!');
|
||||
$this->table(
|
||||
['Statistik', 'Anzahl'],
|
||||
[
|
||||
['Verarbeitet', $stats['processed']],
|
||||
['Neu erstellt', $stats['created']],
|
||||
['Aktualisiert', $stats['updated']],
|
||||
['Übersprungen', $stats['skipped']],
|
||||
['Fehler', $stats['errors']],
|
||||
]
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
226
app/Console/Commands/SyncNewsletterKulturreisen.php
Normal file
226
app/Console/Commands/SyncNewsletterKulturreisen.php
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Booking;
|
||||
use App\Models\Customer;
|
||||
use App\Models\NewsletterContact;
|
||||
use App\Models\Status;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SyncNewsletterKulturreisen extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'newsletter:sync-kulturreisen {--force : Force full sync}';
|
||||
|
||||
/**
|
||||
* The console description of the command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Synchronisiert Kulturreisen-Buchungen mit Newsletter-Kontakten';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starte Synchronisation von Kulturreisen-Buchungen...');
|
||||
|
||||
$force = $this->option('force');
|
||||
|
||||
// Statistiken
|
||||
$stats = [
|
||||
'processed' => 0,
|
||||
'created' => 0,
|
||||
'updated' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
];
|
||||
|
||||
// Hole alle Buchungen mit Kunden
|
||||
$query = Booking::with(['customer', 'lead.status'])
|
||||
->whereNotNull('customer_id')
|
||||
->whereHas('customer', function ($q) {
|
||||
$q->whereNotNull('email')
|
||||
->where('email', '!=', '');
|
||||
})
|
||||
// Nur Buchungen, bei denen die Reise bereits beendet ist (end_date in der Vergangenheit)
|
||||
->whereNotNull('end_date')
|
||||
->where('end_date', '<', now());
|
||||
|
||||
if (!$force) {
|
||||
// Nur Buchungen der letzten 30 Tage (basierend auf Rückreisedatum) wenn nicht --force
|
||||
$query->where('end_date', '>=', now()->subDays(30));
|
||||
}
|
||||
|
||||
$bookings = $query->get();
|
||||
|
||||
$this->info("Verarbeite {$bookings->count()} Buchungen...");
|
||||
|
||||
$bar = $this->output->createProgressBar($bookings->count());
|
||||
$bar->start();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
try {
|
||||
$stats['processed']++;
|
||||
|
||||
$customer = $booking->customer;
|
||||
|
||||
// Validiere E-Mail
|
||||
if (!$customer || !$customer->email || !filter_var($customer->email, FILTER_VALIDATE_EMAIL)) {
|
||||
$stats['skipped']++;
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filtere Alias/Proxy E-Mail-Adressen von Buchungsplattformen
|
||||
$blockedDomains = [
|
||||
'@guest.booking.com',
|
||||
'@messages.homeaway.com',
|
||||
'@fewo.check24.de',
|
||||
'@booking.com',
|
||||
'@homeaway.com',
|
||||
'@check24.de',
|
||||
'@partner.booking.com',
|
||||
];
|
||||
|
||||
$emailLower = strtolower($customer->email);
|
||||
$isBlockedEmail = false;
|
||||
foreach ($blockedDomains as $domain) {
|
||||
if (str_ends_with($emailLower, strtolower($domain))) {
|
||||
$isBlockedEmail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isBlockedEmail) {
|
||||
$stats['skipped']++;
|
||||
$bar->advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generiere Hash für Duplikat-Erkennung
|
||||
$syncHash = NewsletterContact::generateSyncHash(
|
||||
$customer->email,
|
||||
NewsletterContact::SOURCE_BOOKING_KULTURREISEN
|
||||
);
|
||||
|
||||
// Suche oder erstelle Kontakt
|
||||
$contact = NewsletterContact::withTrashed()
|
||||
->where('email', strtolower(trim($customer->email)))
|
||||
->first();
|
||||
|
||||
$isNew = false;
|
||||
|
||||
if (!$contact) {
|
||||
// Neuer Kontakt
|
||||
$contact = new NewsletterContact();
|
||||
$isNew = true;
|
||||
$stats['created']++;
|
||||
} else {
|
||||
// Wenn gelöscht, wiederherstellen
|
||||
if ($contact->trashed()) {
|
||||
$contact->restore();
|
||||
}
|
||||
$stats['updated']++;
|
||||
}
|
||||
|
||||
// Aktualisiere Kontaktdaten
|
||||
$contact->email = strtolower(trim($customer->email));
|
||||
$contact->firstname = $customer->firstname ?: $contact->firstname;
|
||||
$contact->lastname = $customer->name ?: $contact->lastname;
|
||||
|
||||
// Setze Gruppe Kulturreisen
|
||||
$contact->group_kulturreisen = true;
|
||||
|
||||
// Source nur bei neuem Kontakt setzen
|
||||
if ($isNew) {
|
||||
$contact->source = NewsletterContact::SOURCE_BOOKING_KULTURREISEN;
|
||||
$contact->subscribed_at = $booking->booking_date ?: $booking->created_at;
|
||||
}
|
||||
|
||||
// Referenz zum Customer
|
||||
$contact->customer_id = $customer->id;
|
||||
|
||||
// Aktualisiere Buchungsstatistiken
|
||||
$customerBookings = Booking::where('customer_id', $customer->id)->count();
|
||||
$contact->total_bookings_kulturreisen = $customerBookings;
|
||||
|
||||
// Letztes Buchungsdatum
|
||||
$lastBooking = Booking::where('customer_id', $customer->id)
|
||||
->orderBy('booking_date', 'DESC')
|
||||
->first();
|
||||
|
||||
if ($lastBooking && $lastBooking->booking_date) {
|
||||
$contact->last_booking_at = $lastBooking->booking_date;
|
||||
}
|
||||
|
||||
// Letztes Reiseenddatum (end_date) - nur abgeschlossene Reisen
|
||||
$lastTravelEndBooking = Booking::where('customer_id', $customer->id)
|
||||
->whereNotNull('end_date')
|
||||
->where('end_date', '<', now())
|
||||
->orderBy('end_date', 'DESC')
|
||||
->first();
|
||||
|
||||
if ($lastTravelEndBooking && $lastTravelEndBooking->end_date) {
|
||||
if (!$contact->last_travel_end_date || $lastTravelEndBooking->end_date->gt($contact->last_travel_end_date)) {
|
||||
$contact->last_travel_end_date = $lastTravelEndBooking->end_date;
|
||||
}
|
||||
}
|
||||
|
||||
// Status
|
||||
if ($isNew || $contact->status === NewsletterContact::STATUS_INACTIVE) {
|
||||
$contact->status = NewsletterContact::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
$contact->sync_hash = $syncHash;
|
||||
$contact->last_synced_at = now();
|
||||
|
||||
$contact->save();
|
||||
|
||||
// Log erstellen
|
||||
if ($isNew) {
|
||||
$contact->logs()->create([
|
||||
'action' => 'booking_added',
|
||||
'description' => 'Kontakt durch Kulturreisen-Buchung erstellt',
|
||||
'metadata' => [
|
||||
'booking_id' => $booking->id,
|
||||
'lead_id' => $booking->lead_id,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$stats['errors']++;
|
||||
$this->error("Fehler bei Buchung {$booking->id}: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$bar->advance();
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->newLine(2);
|
||||
|
||||
// Statistiken ausgeben
|
||||
$this->info('Synchronisation abgeschlossen!');
|
||||
$this->table(
|
||||
['Statistik', 'Anzahl'],
|
||||
[
|
||||
['Verarbeitet', $stats['processed']],
|
||||
['Neu erstellt', $stats['created']],
|
||||
['Aktualisiert', $stats['updated']],
|
||||
['Übersprungen', $stats['skipped']],
|
||||
['Fehler', $stats['errors']],
|
||||
]
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
18
app/Console/Commands/readme.md
Normal file
18
app/Console/Commands/readme.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Newsletter-Synchronisation (wie gewohnt):
|
||||
|
||||
# Normale Synchronisation (letzte 30 Tage)
|
||||
|
||||
php artisan newsletter:sync-ferienwohnungenphp artisan newsletter:sync-kulturreisen
|
||||
|
||||
# Vollständige Synchronisation
|
||||
|
||||
php artisan newsletter:sync-ferienwohnungen --forcephp artisan newsletter:sync-kulturreisen --force
|
||||
#Bereinigung bestehender blockierter E-Mails:
|
||||
|
||||
# Erst testen (zeigt nur an, was gelöscht würde)
|
||||
|
||||
php artisan newsletter:cleanup-blocked-emails --dry-run
|
||||
|
||||
# Tatsächlich löschen
|
||||
|
||||
php artisan newsletter:cleanup-blocked-emails
|
||||
Loading…
Add table
Add a link
Reference in a new issue