timeStart = microtime(true); $this->info('=== Test: UserMakeAboOrder ==='); $this->newLine(); try { $aboId = $this->option('abo_id'); $testDate = $this->option('date') ? Carbon::parse($this->option('date'))->format('Y-m-d') : Carbon::now()->format('Y-m-d'); $dryRun = $this->option('dry-run'); $force = $this->option('force'); $this->info("Test-Datum: {$testDate}"); if ($dryRun) { $this->warn('DRY-RUN Modus: Es werden keine Bestellungen erstellt!'); } if ($force) { $this->warn('FORCE Modus: Duplikatsprüfung wird überschrieben!'); } $this->newLine(); if ($aboId) { // Test für spezifisches Abo $userAbo = UserAbo::find($aboId); if (! $userAbo) { $this->error("Abo mit ID {$aboId} nicht gefunden!"); return 1; } $this->testSingleAbo($userAbo, $testDate, $dryRun, $force); } else { // Test für alle fälligen Abos $this->testAllAbos($testDate, $dryRun, $force); } $executionTime = $this->getExecutionTime(); $this->newLine(); $this->info("Test erfolgreich abgeschlossen in {$executionTime}"); return 0; } catch (\Exception $e) { $this->error('Fehler beim Testen: '.$e->getMessage()); $this->error($e->getTraceAsString()); return 1; } } /** * Testet ein einzelnes Abo * * @param UserAbo $userAbo * @param string $testDate * @param bool $dryRun * @param bool $force * @return void */ private function testSingleAbo($userAbo, $testDate, $dryRun, $force) { $this->info("Teste Abo ID: {$userAbo->id}"); $this->displayAboInfo($userAbo); // Prüfe ob Abo für Test-Datum fällig ist if ($userAbo->next_date != $testDate && ! $force) { $this->warn("Abo ist nicht für {$testDate} fällig (next_date: {$userAbo->next_date})"); if (! $this->confirm('Trotzdem fortfahren?', false)) { return; } } // Prüfe auf Duplikate if (! $force) { $existingOrder = UserAboOrder::where('user_abo_id', $userAbo->id) ->whereDate('created_at', $testDate) ->first(); if ($existingOrder) { $this->warn("Es existiert bereits eine Bestellung für dieses Abo am {$testDate}"); $this->info("Bestell-ID: {$existingOrder->shopping_order_id}"); if (! $this->confirm('Trotzdem fortfahren?', false)) { return; } } } $this->newLine(); $this->info('Starte Bestellungserstellung...'); if ($dryRun) { $this->info('[DRY-RUN] Bestellung würde erstellt werden'); $this->displayOrderPreview($userAbo); } else { // Temporär next_date setzen für Test $originalNextDate = $userAbo->next_date; if ($userAbo->next_date != $testDate) { $userAbo->next_date = $testDate; $userAbo->save(); $this->info("Temporär next_date auf {$testDate} gesetzt"); } try { $shoppingOrder = $this->makeOrder($userAbo, $dryRun); if ($shoppingOrder) { $this->info("✓ Bestellung erfolgreich erstellt: ID {$shoppingOrder->id}"); } else { $this->error('✗ Bestellung konnte nicht erstellt werden'); } } finally { // next_date zurücksetzen falls geändert if ($originalNextDate != $testDate) { $userAbo->next_date = $originalNextDate; $userAbo->save(); $this->info("next_date zurückgesetzt auf {$originalNextDate}"); } } } } /** * Testet alle fälligen Abos * * @param string $testDate * @param bool $dryRun * @param bool $force * @return void */ private function testAllAbos($testDate, $dryRun, $force) { $query = UserAbo::where('next_date', '=', $testDate) ->where('active', true); if (! $force) { $query->whereDoesntHave('user_abo_orders', function ($q) use ($testDate) { $q->whereDate('created_at', $testDate); }); } $userAbos = $query->get(); $count = $userAbos->count(); $this->info("Gefundene fällige Abos: {$count}"); $this->newLine(); if ($count === 0) { $this->warn('Keine fälligen Abos gefunden!'); return; } if (! $this->confirm("Möchten Sie {$count} Abo(s) testen?", true)) { return; } $this->newLine(); foreach ($userAbos as $userAbo) { $this->info("--- Abo ID: {$userAbo->id} ---"); $this->testSingleAbo($userAbo, $testDate, $dryRun, $force); $this->newLine(); } } /** * Zeigt Informationen über ein Abo an * * @param UserAbo $userAbo * @return void */ private function displayAboInfo($userAbo) { $this->table( ['Feld', 'Wert'], [ ['ID', $userAbo->id], ['User ID', $userAbo->user_id], ['Payone UserID', $userAbo->payone_userid], ['Aktiv', $userAbo->active ? 'Ja' : 'Nein'], ['Status', $userAbo->status.' ('.($userAbo->getStatusType() ?? 'unbekannt').')'], ['Intervall', $userAbo->abo_interval], ['Next Date', $userAbo->next_date], ['Last Date', $userAbo->last_date ?? 'Nie'], ['Amount', number_format($userAbo->amount / 100, 2, ',', '.').' €'], ['is_for', $userAbo->is_for], ['Clearing Type', $userAbo->clearingtype], ['Items', $userAbo->user_abo_items->count()], ] ); // Zeige Abo-Items if ($userAbo->user_abo_items->count() > 0) { $this->info('Abo-Items:'); $items = []; foreach ($userAbo->user_abo_items as $item) { $items[] = [ 'Product ID' => $item->product_id, 'Qty' => $item->qty, 'Comp' => $item->comp ?? '-', 'Price' => number_format($item->price / 100, 2, ',', '.').' €', ]; } $this->table(['Product ID', 'Qty', 'Comp', 'Price'], $items); } } /** * Zeigt eine Vorschau der Bestellung an * * @param UserAbo $userAbo * @return void */ private function displayOrderPreview($userAbo) { $this->info('Bestell-Vorschau:'); $this->info('- Shopping-User würde erstellt/aktualisiert'); $this->info('- Bestellung würde mit folgenden Items erstellt:'); foreach ($userAbo->user_abo_items as $item) { $product = $item->product; $this->info(" • Product ID {$item->product_id}: {$item->qty}x"); } $this->info('- Zahlung würde durchgeführt'); } /** * Erstellt eine Bestellung für ein Abo (vereinfachte Version für Test) * * @param UserAbo $userAbo * @param bool $dryRun * @return mixed */ private function makeOrder($userAbo, $dryRun = false) { $this->info('Erstelle Shopping-User...'); $userOrder = new UserMakeOrder($userAbo); if (! $userOrder->createShoppingUser()) { $this->error('Konnte Shopping-User nicht erstellen'); return null; } $this->info('✓ Shopping-User erstellt'); $this->info('Erstelle Bestellung...'); $shoppingOrder = $userOrder->makeShoppingOrder(); $shoppingOrder->mode = 'test'; // immer im test mode testen $shoppingOrder->save(); if (! $shoppingOrder) { $this->error('Konnte Bestellung nicht erstellen'); return null; } $this->info("✓ Bestellung erstellt: ID {$shoppingOrder->id}"); if ($dryRun) { $this->info('[DRY-RUN] Zahlung würde durchgeführt'); $this->info('[DRY-RUN] Abo würde aktualisiert'); return $shoppingOrder; } $this->info('Starte Zahlungsvorgang...'); try { $response = $userOrder->makePayment(); if (is_object($response)) { $response = (array) $response; } $this->info('Zahlungsantwort: '.json_encode($response, JSON_PRETTY_PRINT)); if (! isset($response['status'])) { $this->warn('⚠ Kein Status in Zahlungsantwort'); return $shoppingOrder; } if ($response['status'] === 'APPROVED') { $this->info('✓ Zahlung erfolgreich'); $this->info('Aktualisiere Abo...'); $this->updateAbo($userAbo, $shoppingOrder, 1); $this->info('✓ Abo aktualisiert'); } elseif ($response['status'] === 'ERROR') { $this->error('✗ Zahlungsfehler'); $this->warn('Abo wird beim nächsten Cron-Lauf erneut versucht'); } else { $this->warn("⚠ Zahlungsstatus: {$response['status']}"); } } catch (\Exception $e) { $this->error('Fehler bei Zahlung: '.$e->getMessage()); } return $shoppingOrder; } /** * Aktualisiert das Abo nach erfolgreicher Bestellung (vereinfachte Version) * * @param UserAbo $userAbo * @param mixed $shoppingOrder * @param int $status * @return void */ private function updateAbo($userAbo, $shoppingOrder, $status = 1) { try { DB::transaction(function () use ($userAbo, $shoppingOrder, $status) { $updateData = [ 'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval), 'last_date' => now(), ]; if ($status !== 1) { $updateData['status'] = $status; } $userAbo->update($updateData); UserAboOrder::create([ 'user_abo_id' => $userAbo->id, 'shopping_order_id' => $shoppingOrder->id, 'status' => $status, 'paid' => true, ]); }); } catch (\Exception $e) { $this->error('Fehler beim Aktualisieren des Abos: '.$e->getMessage()); throw $e; } } /** * Berechnet die Ausführungszeit * * @return string */ private function getExecutionTime() { $diff = microtime(true) - $this->timeStart; $sec = intval($diff); $micro = $diff - $sec; return $sec.' Sekunden und '.round($micro * 1000, 2).' ms'; } }