mivita/app/Console/Commands/TestUserMakeAboOrder.php
2026-04-10 17:15:27 +02:00

387 lines
12 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Cron\UserMakeOrder;
use App\Models\UserAbo;
use App\Models\UserAboOrder;
use App\Services\AboHelper;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class TestUserMakeAboOrder extends Command
{
/**
* php artisan test:user-make-abo-order {--abo_id=} {--date=} {--dry-run} {--force}
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test:user-make-abo-order
{--abo_id= : Test für spezifisches Abo (ID)}
{--date= : Test-Datum im Format Y-m-d (Standard: heute)}
{--dry-run : Nur anzeigen, keine Bestellung erstellen}
{--force : Überschreibt Duplikatsprüfung}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Testet die Abo-Bestellungserstellung für ein oder mehrere Abos';
private $timeStart;
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->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';
}
}