387 lines
12 KiB
PHP
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';
|
|
}
|
|
}
|