mein-sterntours/app/Console/Commands/SyncNewsletterFerienwohnungen.php
2026-01-23 17:34:40 +01:00

254 lines
9.3 KiB
PHP

<?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;
}
}