clearingtypes = PaymentReminder::returnClearingtypes(); } /** * Erstellt einen Log-Eintrag für Payment Reminder Aktivitäten */ private function createLog($action, $message, $model = null, $modelId = null, $level = 2) { return Logger::create([ 'user_id' => null, // System-Aktion 'model_id' => $modelId, 'model' => $model, 'action' => $action, 'channel' => 'payment_reminder', 'message' => $message, 'level' => $level ]); } /** * Hole alle aktiven Intervalle für Zahlungserinnerungen */ public function getActiveIntervals() { $intervals = []; $payment_reminders = PaymentReminder::where('active', true)->get(); foreach($payment_reminders as $reminder) { $intervals[$reminder->clearingtype] = $reminder->interval; } return $intervals; } /** * Hole alle offenen Zahlungen für einen bestimmten clearingtype */ public function getOpenPaymentsForClearingType($clearingtype, $interval) { $date = Carbon::now()->subDays($interval); $payments = ShoppingPayment::join('shopping_orders', 'shopping_payments.shopping_order_id', '=', 'shopping_orders.id') ->where('shopping_payments.clearingtype', '=', $clearingtype) ->where('shopping_payments.txaction', '=', 'open') ->where('shopping_payments.mode', '=', 'live') ->where('shopping_payments.created_at', '<', $date) ->where('shopping_payments.amount', '>', 0) ->whereNull('shopping_orders.deleted_at') ->whereIn('shopping_payments.id', function($query) use ($clearingtype, $date) { $query->selectRaw('MAX(shopping_payments.id)') ->from('shopping_payments') ->join('shopping_orders', 'shopping_payments.shopping_order_id', '=', 'shopping_orders.id') ->where('shopping_payments.clearingtype', '=', $clearingtype) ->where('shopping_payments.txaction', '=', 'open') ->where('shopping_payments.mode', '=', 'live') ->where('shopping_payments.created_at', '<', $date) ->where('shopping_payments.amount', '>', 0) ->whereNull('shopping_orders.deleted_at') ->groupBy('shopping_payments.shopping_order_id'); }) ->select('shopping_payments.*') ->get(); return $payments; } /** * Hole alle offenen Zahlungen für alle clearingtypes */ public function getAllOpenPayments() { $intervals = $this->getActiveIntervals(); $results = []; foreach($intervals as $clearingtype => $interval){ $date = Carbon::now()->subDays($interval); $payments = $this->getOpenPaymentsForClearingType($clearingtype, $interval); $results[$clearingtype] = [ 'interval' => $interval, 'date_limit' => $date, 'payments' => $payments, 'count' => $payments->count() ]; } return $results; } /** * Hole detaillierte Daten für Tabellen-Ansicht */ public function getDetailedPaymentsData() { $intervals = $this->getActiveIntervals(); $detailedData = []; foreach($intervals as $clearingtype => $interval){ $date = Carbon::now()->subDays($interval); $payments = $this->getOpenPaymentsForClearingType($clearingtype, $interval); foreach($payments as $payment){ $name = !isset($payment->shopping_order->shopping_user) ? 'Kein Name' : $payment->shopping_order->shopping_user->billing_firstname.' '.$payment->shopping_order->shopping_user->billing_lastname; $email = !isset($payment->shopping_order->shopping_user) ? 'Keine Email' : $payment->shopping_order->shopping_user->billing_email; $shipped = ''.$payment->shopping_order->getShippedType().''; // Countdown für nächste Erinnerung berechnen $countdown = $this->getNextReminderCountdown($payment); $detailedData[] = [ 'clearingtype' => $clearingtype, 'clearingtype_name' => $this->getClearingtype($clearingtype), 'interval_days' => $interval, 'date_limit' => $date->format('d.m.Y H:i:s'), 'order_id' => $payment->shopping_order_id, 'payment_id' => $payment->id, 'amount' => $payment->amount, 'created_at' => $payment->created_at->format('d.m.Y H:i:s'), 'days_old' => $payment->created_at->diffInDays(now()), 'payment' => $payment, // Vollständiges Payment-Objekt für weitere Verarbeitung 'name' => $name, 'email' => $email, 'shipped' => $shipped, 'reminder' => $payment->reminder, 'reminder_date' => $payment->reminder_date ? $payment->reminder_date->format('d.m.Y H:i:s') : null, 'countdown' => $countdown, ]; } } return $detailedData; } /** * Sende die nächste Zahlungserinnerung * noch kein reminder gesendet = 1. Zahlungserinnerung * reminder > 0 die nächste zahlungserinnerung aus der liste holen */ public function sendReminder($payment) { //holen der nächsten zahlungserinnerung $payment_reminder = $this->getReminder((int) $payment->reminder, $payment->clearingtype); if(!$payment_reminder){ return false; } //zahlungserinnerung Platzhalter ersetzen. $payment_reminder = $this->replacePlaceholder($payment, $payment_reminder); //zahlungserinnerung senden $emailSent = $this->sendReminderEmail($payment, $payment_reminder); if ($emailSent) { $this->createLog( 'email_sent', "Zahlungserinnerung E-Mail gesendet an: {$payment->shopping_order->shopping_user->billing_email}, Subject: {$payment_reminder->subject}", 'ShoppingOrder', $payment->shopping_order_id, 3 ); } //action ausführen if($payment_reminder->action === 'set_order_status_cancelled'){ $this->setNoNPayment($payment); $payment->shopping_order->shipped = 10; $payment->shopping_order->save(); $this->createLog( 'action_completed', "Action abgeschlossen: Bestellung auf 'Storniert' gesetzt, Payment auf 'non' gesetzt", 'ShoppingOrder', $payment->shopping_order_id, 3 ); } //reminder setzen +1 $payment->reminder = (int) $payment->reminder + 1; $payment->reminder_date = Carbon::now(); $payment->save(); $this->createLog( 'reminder_completed', "Zahlungserinnerung für Payment ID: {$payment->id}, Order ID: {$payment->shopping_order_id}", 'ShoppingOrder', $payment->shopping_order_id, 4 ); return true; } public function setNoNPayment($payment) { $this->createLog( 'set_non_payment', "Setze Payment ID: {$payment->id} auf 'non' Status", 'ShoppingOrder', $payment->shopping_order_id, 4 ); PaymentService::updateTransactionStatus($payment->shopping_order_id, 'non', $payment->id); } public function getClearingtype($clearingtype) { return isset($this->clearingtypes[$clearingtype]) ? $this->clearingtypes[$clearingtype] : $clearingtype; } public function getReminder($reminder, $clearingtype) { $payment_reminders = PaymentReminder::where('active', true) ->where('clearingtype', $clearingtype) ->orderBy('interval', 'asc') ->get(); if($payment_reminders->isEmpty()) { return false; } // Wenn reminder größer ist als Anzahl der Erinnerungen if($reminder >= $payment_reminders->count()) { return false; } // Hole die Erinnerung an Position $reminder (0,1,2,3...) return $payment_reminders[$reminder]; } public function replacePlaceholder($payment, $payment_reminder) { $shopping_order = $payment->shopping_order; $shopping_user = $shopping_order->shopping_user; $replacements = [ '{billing_first_name}' => $shopping_user->billing_firstname, '{billing_last_name}' => $shopping_user->billing_lastname, '{order_number}' => ''.$shopping_order->getLastShoppingPayment('reference').'', '{order_date}' => ''.$shopping_order->created_at->format('d.m.Y').'', '{order_total}' => ''.$shopping_order->getFormattedTotalShipping().'' ]; $payment_reminder->subject = str_replace( array_keys($replacements), array_values($replacements), $payment_reminder->subject ); $payment_reminder->message = str_replace( array_keys($replacements), array_values($replacements), $payment_reminder->message ); return $payment_reminder; } public function sendReminderEmail($payment, $payment_reminder) { try { $email = $payment->shopping_order->shopping_user->billing_email; $subject = $payment_reminder->subject; $message = $payment_reminder->message; if(Util::isTestSystem()){ $email = config('app.checkout_test_mail'); } if($payment->shopping_order->mode === 'test' || Util::isTestSystem()){ $bcc[] = config('app.checkout_test_mail'); }else{ $bcc[] = config('app.checkout_mail'); } Mail::to($email)->bcc($bcc)->send(new PaymentReminderEmail($subject, $message, $payment->shopping_order)); return true; } catch (\Exception $e) { \Log::error('Fehler beim E-Mail-Versand: ' . $e->getMessage()); $this->createLog( 'email_exception', "E-Mail Exception: " . $e->getMessage() . " für Payment ID: {$payment->id}", 'ShoppingOrder', $payment->shopping_order_id, 5 ); return false; } } /** * Berechnet den Countdown bis zur nächsten Zahlungserinnerung */ public function getNextReminderCountdown($payment) { // Wenn noch keine Erinnerung gesendet wurde if ($payment->reminder == 0) { return null; } // Hole alle aktiven Erinnerungen für diesen Clearingtype $payment_reminders = PaymentReminder::where('active', true) ->where('clearingtype', $payment->clearingtype) ->orderBy('interval', 'asc') ->get(); if ($payment_reminders->isEmpty()) { return null; } // Wenn alle Erinnerungen bereits gesendet wurden if ($payment->reminder >= $payment_reminders->count()) { return [ 'type' => 'completed', 'message' => 'Alle Erinnerungen gesendet', 'days_left' => 0 ]; } // Hole die nächste Erinnerung $next_reminder = $payment_reminders[$payment->reminder]; // Berechne die Differenz zwischen aktuellem und nächstem Reminder $current_reminder = $payment_reminders[$payment->reminder - 1]; $interval_difference = $next_reminder->interval - $current_reminder->interval; // Berechne das Datum der nächsten Erinnerung $next_reminder_date = Carbon::parse($payment->reminder_date)->addDays($interval_difference); // Berechne die verbleibenden Tage $days_left = Carbon::now()->diffInDays($next_reminder_date, false); // Wenn die nächste Erinnerung bereits fällig ist if ($days_left <= 0) { return [ 'type' => 'overdue', 'message' => 'Nächste Erinnerung fällig', 'days_left' => 0, 'next_reminder_date' => $next_reminder_date ]; } return [ 'type' => 'countdown', 'message' => 'Nächste Erinnerung in ' . $days_left . ' Tagen', 'days_left' => $days_left, 'next_reminder_date' => $next_reminder_date, 'next_reminder_interval' => $interval_difference ]; } /** * Hole alle Logs für Payment Reminder */ public function getPaymentReminderLogs($limit = 100, $paymentId = null, $action = null) { $query = Logger::where('channel', 'payment_reminder') ->orderBy('created_at', 'desc'); if ($paymentId) { $query->where('model_id', $paymentId); } if ($action) { $query->where('action', $action); } return $query->limit($limit)->get(); } /** * Hole Logs für einen spezifischen Payment */ public function getLogsForPayment($orderId) { return Logger::where('channel', 'payment_reminder') ->where('model_id', $orderId) ->orderBy('created_at', 'desc') ->get(); } /** * Hole Logs für einen spezifischen Zeitraum */ public function getLogsForDateRange($startDate, $endDate) { return Logger::where('channel', 'payment_reminder') ->whereBetween('created_at', [$startDate, $endDate]) ->orderBy('created_at', 'desc') ->get(); } /** * Hole Statistiken für Payment Reminder Logs */ public function getLogStatistics($days = 30) { $startDate = Carbon::now()->subDays($days); $stats = Logger::where('channel', 'payment_reminder') ->where('created_at', '>=', $startDate) ->selectRaw('action, level, COUNT(*) as count') ->groupBy('action', 'level') ->get(); $summary = [ 'total_logs' => Logger::where('channel', 'payment_reminder') ->where('created_at', '>=', $startDate) ->count(), 'emails_sent' => Logger::where('channel', 'payment_reminder') ->where('action', 'email_sent') ->where('created_at', '>=', $startDate) ->count(), 'emails_failed' => Logger::where('channel', 'payment_reminder') ->where('action', 'email_exception') ->where('created_at', '>=', $startDate) ->count(), 'reminders_completed' => Logger::where('channel', 'payment_reminder') ->where('action', 'reminder_completed') ->where('created_at', '>=', $startDate) ->count(), 'actions_executed' => Logger::where('channel', 'payment_reminder') ->where('action', 'action_completed') ->where('created_at', '>=', $startDate) ->count(), ]; return [ 'summary' => $summary, 'detailed_stats' => $stats ]; } }