update 20.10.2025

This commit is contained in:
Kevin Adametz 2025-10-20 17:42:08 +02:00
parent 8c11130b5d
commit a939cd51ef
616 changed files with 84821 additions and 4121 deletions

View file

@ -0,0 +1,136 @@
<?php
namespace App\Console\Commands;
use App\Models\UserBusinessStructure;
use App\Models\UserBusiness;
use Illuminate\Console\Command;
class BusinessClearData extends Command
{
/**
* php artisan business:clear-data {month} {year}
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'business:clear-data {month} {year} {--force : Force deletion without confirmation}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear stored business structure data for a specific month/year';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
$month = (int) $this->argument('month');
$year = (int) $this->argument('year');
// Validierung
if ($month < 1 || $month > 12) {
$this->error('Invalid month. Must be between 1 and 12.');
return 1;
}
$currentYear = (int) date('Y');
if ($year < 2020 || $year > $currentYear + 1) {
$this->error('Invalid year. Must be between 2020 and ' . ($currentYear + 1));
return 1;
}
$this->info("Preparing to clear business data for month: {$month} | year: {$year}");
// Finde bestehende Struktur
$existingStructure = UserBusinessStructure::where('year', $year)
->where('month', $month)
->first();
if (!$existingStructure) {
$this->info('No stored business structure found for the specified month/year');
return 0;
}
$structureId = $existingStructure->id;
$userBusinessCount = UserBusiness::where('b_structure_id', $structureId)->count();
$userCount = is_array($existingStructure->users) ? count($existingStructure->users) : 0;
$this->info("Found structure ID: {$structureId}");
$this->info("- UserBusiness records: {$userBusinessCount}");
$this->info("- Users in structure: {$userCount}");
$this->info("- Completed: " . ($existingStructure->completed ? 'Yes' : 'No'));
// Bestätigung (außer bei --force)
if (!$this->option('force')) {
if (!$this->confirm('Are you sure you want to delete this business structure data?')) {
$this->info('Operation cancelled by user');
return 0;
}
}
$startTime = microtime(true);
// Lösche UserBusiness Einträge
if ($userBusinessCount > 0) {
$this->info("Deleting {$userBusinessCount} UserBusiness records...");
UserBusiness::where('b_structure_id', $structureId)->delete();
$this->info('✓ UserBusiness records deleted');
}
// Lösche UserBusinessStructure
$this->info('Deleting UserBusinessStructure...');
$existingStructure->delete();
$this->info('✓ UserBusinessStructure deleted');
// Garbage Collection
gc_collect_cycles();
$endTime = microtime(true);
$duration = round(($endTime - $startTime) * 1000, 2);
$this->info("✅ Successfully cleared all business data in {$duration}ms");
$this->logMemoryUsage();
return 0;
} catch (\Exception $e) {
$this->error('Error clearing business data: ' . $e->getMessage());
$this->error('Stack trace: ' . $e->getTraceAsString());
return 1;
}
}
/**
* Loggt aktuelle Memory-Nutzung
*/
private function logMemoryUsage(): void
{
$currentMemory = memory_get_usage();
$peakMemory = memory_get_peak_usage();
$currentFormatted = $this->formatBytes($currentMemory);
$peakFormatted = $this->formatBytes($peakMemory);
$this->info("Memory - Current: {$currentFormatted} | Peak: {$peakFormatted}");
}
/**
* Formatiert Bytes in lesbare Einheiten
*/
private function formatBytes(int $bytes, int $precision = 2): string
{
$units = array('B', 'KB', 'MB', 'GB', 'TB');
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision) . ' ' . $units[$i];
}
}

View file

@ -0,0 +1,149 @@
<?php
namespace App\Console\Commands;
use App\Services\LevelReportService;
use Illuminate\Console\Command;
class BusinessLevelReports extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'business:level-reports {--month= : Filter by specific month} {--year= : Filter by specific year} {--user-id= : Filter by specific user ID} {--csv : Export as CSV file} {--not-updated : Show only users not yet updated to their new level}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate reports showing who achieved new career levels and when';
private $levelReportService;
public function __construct(LevelReportService $levelReportService)
{
parent::__construct();
$this->levelReportService = $levelReportService;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
$this->info('Generiere Level-Aufstieg-Report...');
// Filter Parameter
$filters = [
'month' => $this->option('month'),
'year' => $this->option('year'),
'user_id' => $this->option('user-id'),
'only_not_updated' => $this->option('not-updated')
];
$exportCsv = $this->option('csv');
// Lade Level-Aufstiege über Service
$levelPromotions = $this->levelReportService->getLevelPromotions($filters);
if ($levelPromotions->isEmpty()) {
$this->info('Keine Level-Aufstiege gefunden.');
return 0;
}
if ($exportCsv) {
$filepath = $this->levelReportService->exportToCsv($levelPromotions);
$this->info('');
$this->info('CSV-Export erstellt: ' . $filepath);
$this->info('Anzahl Datensätze: ' . $levelPromotions->count());
} else {
$this->displayReport($levelPromotions);
}
$this->info('Report erfolgreich generiert.');
return 0;
} catch (\Exception $e) {
$this->error('Fehler beim Generieren des Reports: ' . $e->getMessage());
return 1;
}
}
private function displayReport($promotions)
{
$statistics = $this->levelReportService->getStatistics($promotions);
$this->info('');
$this->info('=== LEVEL-AUFSTIEG REPORT ===');
$this->info('');
if ($promotions->isEmpty()) {
$this->info('Keine Level-Aufstiege gefunden.');
return;
}
$headers = [
'Datum',
'User ID',
'Name',
'E-Mail',
'Von Level',
'Zu Level',
'Aktueller Level',
'Margin',
'KP Req',
'PP Req',
'Growth Bonus',
'User PP',
'User KP',
'Level Update',
'Aktiv'
];
$rows = [];
foreach ($promotions->toArray() as $promotion) {
$rows[] = [
$promotion['date'],
$promotion['user_id'],
$promotion['first_name'] . ' ' . $promotion['last_name'],
$promotion['email'],
$promotion['from_level_name'] . ' (ID:' . $promotion['from_level_id'] . ')',
$promotion['to_level_name'] . ' (ID:' . $promotion['to_level_id'] . ')',
$promotion['current_user_level_name'] . ' (ID:' . ($promotion['current_user_level_id'] ?? 'N/A') . ')',
$promotion['to_level_margin'] . '%',
number_format($promotion['to_level_qual_kp'], 0, ',', '.'),
number_format($promotion['to_level_qual_pp'], 0, ',', '.'),
$promotion['to_level_growth_bonus'] . '%',
number_format($promotion['total_pp'], 0, ',', '.'),
number_format($promotion['sales_volume_points_sum'], 0, ',', '.'),
$promotion['level_updated'],
$promotion['active_account'],
];
}
$this->table($headers, $rows);
// Zusammenfassung
$this->info('');
$this->info('=== ZUSAMMENFASSUNG ===');
$this->info('Anzahl Level-Aufstiege: ' . $statistics['total_count']);
$this->info('');
$this->info('Aufstiege nach Ziel-Level:');
foreach ($statistics['level_stats'] as $level => $count) {
$this->info(" - {$level}: {$count}");
}
$this->info('');
$this->info('Aufstiege nach Zeitraum:');
foreach ($statistics['period_stats'] as $period => $count) {
$this->info(" - {$period}: {$count}");
}
}
}

View file

@ -0,0 +1,191 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
use App\Cron\BusinessUsersStore;
use App\Cron\UserLevelUpdate;
use App\Cron\UserPaymentCredits;
class BusinessStore extends Command
{
/**
* ln -sfv /usr/bin/php73 /usr/bin/php
* php artisan business:store month year
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'business:store {month} {year}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create Business Structure and UserDetails with optimized performance';
private $timeStart;
private $month;
private $year;
private $sendCreditMail = false;
private $sendUpdateMail = false;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
$executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur');
$presentDay = (int) date('d');
$this->info('RUN Command BusinessStore on Day: ' . $executeDay);
$this->info('RUN Command BusinessStore present Day: ' . $presentDay);
\Log::channel('cron')->info('RUN Command BusinessStore on Day: ' . $executeDay);
\Log::channel('cron')->info('RUN Command BusinessStore present Day: ' . $presentDay);
$this->logMemoryUsage('Command Start');
if ($executeDay !== $presentDay) {
$this->info('NOT RUN Command BusinessStore is not present Day: ' . $presentDay);
\Log::channel('cron')->info('NOT RUN Command BusinessStore is not present Day: ' . $presentDay);
return 0;
}
$this->timeStart = microtime(true);
// Argumente mit Standardwerten für den Vormonat
$this->month = $this->argument('month') ?: (int) date("m", strtotime("-1 month"));
$this->year = $this->argument('year') ?: (int) date("Y", strtotime("-1 month"));
$this->info('RUN Command BusinessStore on month: ' . $this->month . ' | year: ' . $this->year);
\Log::channel('cron')->info('RUN Command BusinessStore on month: ' . $this->month . ' | year: ' . $this->year);
$this->logMemoryUsage('Parameters initialized');
// Prozesse ausführen mit Fehlerbehandlung
$this->executeWithErrorHandling('Business Structure Storage', function () {
$this->storeBusinessStructureUsersDetailMonth();
});
$this->executeWithErrorHandling('Commission Calculation', function () {
$this->userBusinessCommissionsToCredit();
});
// Auskommentierte Prozesse bleiben inaktiv
// $this->userCreatePaymentCreditsPDF();
// $this->userLevelUpdate();
// $this->storeBusinessStructureUsersDetailPeriod(1, 6);
$this->logExecutionTime('COMMAND COMPLETED SUCCESSFULLY');
$this->logMemoryUsage('Command End');
\Log::channel('cron')->info('COMMAND COMPLETED SUCCESSFULLY');
return 0;
} catch (\Exception $e) {
$this->error('Command failed with error: ' . $e->getMessage());
$this->error('Stack trace: ' . $e->getTraceAsString());
$this->logExecutionTime('COMMAND FAILED');
return 1;
}
}
private function storeBusinessStructureUsersDetailMonth()
{
$this->info('storeBusinessStructureUsersDetailMonth month: ' . $this->month . ' year:' . $this->year);
$businessUsersStore = new BusinessUsersStore($this->month, $this->year);
$businessUsersStore->storeUserBusinessStructure();
$businessUsersStore->storeBusinessUsersDetail();
$bool = $businessUsersStore->storeBusinessCompleted();
$this->logExecutionTime('END Command storeBusinessStructureUsersDetailMonth: ' . $bool);
}
private function userBusinessCommissionsToCredit()
{
$this->info('userBusinessCommissionsToCredit month: ' . $this->month . ' year:' . $this->year);
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
$userBusinesses = $userPaymentCredits->getUserBusinessByMonthYear();
foreach ($userBusinesses as $userBusiness) {
$ret = $userPaymentCredits->addUserCreditItem($userBusiness);
$this->info('userBusinessCredit: ' . $ret->user_id . ' : Team: ' . $ret->commission_pp_total . ' | Shop: ' . $ret->commission_shop_sales);
}
$this->logExecutionTime('END Command userBusinessCommissionsToCredit:');
}
private function userCreatePaymentCreditsPDF()
{
$this->info('userCreatePaymentCreditsPDF month: ' . $this->month . ' year:' . $this->year);
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
$creditItemUsers = $userPaymentCredits->getUserCreditItemUsersByMonthYear();
foreach ($creditItemUsers as $creditItemUser) {
$bool = $userPaymentCredits->makeCreditPaymentPDF($creditItemUser->user_id, $this->sendCreditMail);
$this->info('creditsPDF: ' . $bool . ' user_id: ' . $creditItemUser->user_id);
}
$this->logExecutionTime('END Command userCreatePaymentCreditsPDF:');
}
private function userLevelUpdate()
{
$this->info('userLevelUpdate month: ' . $this->month . ' year:' . $this->year);
$userLevelUpdate = new UserLevelUpdate($this->month, $this->year);
$levelUpdateUsers = $userLevelUpdate->getUserBusinessByMonthYear();
foreach ($levelUpdateUsers as $userBusiness) {
$ret = $userLevelUpdate->makeUserLevelUpdate($userBusiness, $this->sendUpdateMail);
if ($ret) {
$this->info('updateLevel: ' . $userBusiness->user->id . ' | ' . $userBusiness->user->email . ' | ' .
'from: ' . $userBusiness->m_level_id . ' ' . $userBusiness->user_level_name . ' | ' .
'to: ' . $ret);
}
}
$this->logExecutionTime('END Command userLevelUpdate:');
}
private function storeBusinessStructureUsersDetailPeriod($from, $to)
{
for ($i = $from; $i <= $to; $i++) {
$this->info('Store Business Structure Users Detail month: ' . $i . ' year:' . $this->year);
$businessUsersStore = new BusinessUsersStore($i, $this->year);
$businessUsersStore->storeUserBusinessStructure();
$businessUsersStore->storeBusinessUsersDetail();
$bool = $businessUsersStore->storeBusinessCompleted();
$this->logExecutionTime('Period BusinessStore: ' . $bool);
}
}
private function logExecutionTime($message)
{
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info($message . ' | Time: ' . $sec . 'sec :' . round($micro * 1000, 4) . " ms");
}
}

View file

@ -0,0 +1,368 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use App\Models\UserBusinessStructure;
use App\Models\UserBusiness;
use Illuminate\Console\Command;
use App\Cron\BusinessUsersStoreOptimized;
use App\Cron\UserLevelUpdate;
use App\Cron\UserPaymentCredits;
class BusinessStoreOptimized extends Command
{
/**
* ln -sfv /usr/bin/php73 /usr/bin/php
* php artisan business:store-optimized month year
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'business:store-optimized {month} {year} {--clear : Clear stored data before processing}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create Business Structure and UserDetails with optimized performance and monitoring';
private $timeStart;
private $month;
private $year;
private $sendCreditMail = false;
private $sendUpdateMail = false;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
$executeDay = (int) Setting::getContentBySlug('day-exectute-business-structur');
$presentDay = (int) date('d');
$this->info('RUN Command BusinessStoreOptimized on Day: ' . $executeDay);
$this->info('RUN Command BusinessStoreOptimized present Day: ' . $presentDay);
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized on Day: ' . $executeDay);
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized present Day: ' . $presentDay);
$this->logMemoryUsage('Command Start');
if ($executeDay !== $presentDay) {
$this->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay);
\Log::channel('cron')->info('NOT RUN Command BusinessStoreOptimized is not present Day: ' . $presentDay);
return 0;
}
$this->timeStart = microtime(true);
// Argumente mit Standardwerten für den Vormonat
$this->month = $this->argument('month') ?: (int) date("m", strtotime("-1 month"));
$this->year = $this->argument('year') ?: (int) date("Y", strtotime("-1 month"));
$this->info('RUN Command BusinessStoreOptimized on month: ' . $this->month . ' | year: ' . $this->year);
$this->logMemoryUsage('Parameters initialized');
// Prüfe --clear Option und lösche gespeicherte Daten falls gewünscht
if ($this->option('clear')) {
$this->executeWithErrorHandling('Clear Stored Data', function () {
$this->clearStoredData();
});
}
// Prozesse ausführen mit optimierter Fehlerbehandlung
$this->executeWithErrorHandling('Business Structure Storage', function () {
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized Business Structure Storage');
$this->storeBusinessStructureUsersDetailMonth();
});
$this->executeWithErrorHandling('Commission Calculation', function () {
\Log::channel('cron')->info('RUN Command BusinessStoreOptimized Commission Calculation');
$this->userBusinessCommissionsToCredit();
});
// Auskommentierte Prozesse bleiben inaktiv
// $this->userCreatePaymentCreditsPDF();
// $this->userLevelUpdate();
// $this->storeBusinessStructureUsersDetailPeriod(1, 6);
$this->logExecutionTime('COMMAND COMPLETED SUCCESSFULLY');
$this->logMemoryUsage('Command End');
\Log::channel('cron')->info('COMMAND COMPLETED SUCCESSFULLY');
return 0;
} catch (\Exception $e) {
$this->error('Command failed with error: ' . $e->getMessage());
$this->error('Stack trace: ' . $e->getTraceAsString());
$this->logExecutionTime('COMMAND FAILED');
\Log::channel('cron')->info('COMMAND FAILED');
return 1;
}
}
private function storeBusinessStructureUsersDetailMonth()
{
$this->info('storeBusinessStructureUsersDetailMonth month: ' . $this->month . ' year:' . $this->year);
try {
$businessUsersStore = new BusinessUsersStoreOptimized($this->month, $this->year);
$businessUsersStore->storeUserBusinessStructure();
$businessUsersStore->storeBusinessUsersDetail();
$bool = $businessUsersStore->storeBusinessCompleted();
$this->logExecutionTime('END Command storeBusinessStructureUsersDetailMonth: ' . $bool);
} catch (\Exception $e) {
$this->error('Error in storeBusinessStructureUsersDetailMonth: ' . $e->getMessage());
throw $e;
}
}
private function userBusinessCommissionsToCredit()
{
$this->info('userBusinessCommissionsToCredit month: ' . $this->month . ' year:' . $this->year);
try {
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
$userBusinesses = $userPaymentCredits->getUserBusinessByMonthYear();
$processedCount = 0;
foreach ($userBusinesses as $userBusiness) {
$ret = $userPaymentCredits->addUserCreditItem($userBusiness);
$this->info('userBusinessCredit: ' . $ret->user_id . ' : Team: ' . $ret->commission_pp_total . ' | Shop: ' . $ret->commission_shop_sales);
$processedCount++;
// Memory-Check alle 100 User
if ($processedCount % 100 === 0) {
$this->logMemoryUsage("After processing {$processedCount} users");
}
}
$this->info("Processed {$processedCount} user businesses total");
$this->logExecutionTime('END Command userBusinessCommissionsToCredit:');
} catch (\Exception $e) {
$this->error('Error in userBusinessCommissionsToCredit: ' . $e->getMessage());
throw $e;
}
}
private function userCreatePaymentCreditsPDF()
{
$this->info('userCreatePaymentCreditsPDF month: ' . $this->month . ' year:' . $this->year);
try {
$userPaymentCredits = new UserPaymentCredits($this->month, $this->year);
$creditItemUsers = $userPaymentCredits->getUserCreditItemUsersByMonthYear();
$processedCount = 0;
foreach ($creditItemUsers as $creditItemUser) {
$bool = $userPaymentCredits->makeCreditPaymentPDF($creditItemUser->user_id, $this->sendCreditMail);
$this->info('creditsPDF: ' . $bool . ' user_id: ' . $creditItemUser->user_id);
$processedCount++;
// Memory-Check alle 50 PDFs
if ($processedCount % 50 === 0) {
$this->logMemoryUsage("After processing {$processedCount} PDFs");
}
}
$this->info("Created {$processedCount} PDF files total");
$this->logExecutionTime('END Command userCreatePaymentCreditsPDF:');
} catch (\Exception $e) {
$this->error('Error in userCreatePaymentCreditsPDF: ' . $e->getMessage());
throw $e;
}
}
private function userLevelUpdate()
{
$this->info('userLevelUpdate month: ' . $this->month . ' year:' . $this->year);
try {
$userLevelUpdate = new UserLevelUpdate($this->month, $this->year);
$levelUpdateUsers = $userLevelUpdate->getUserBusinessByMonthYear();
$updatedCount = 0;
foreach ($levelUpdateUsers as $userBusiness) {
$ret = $userLevelUpdate->makeUserLevelUpdate($userBusiness, $this->sendUpdateMail);
if ($ret) {
$this->info('updateLevel: ' . $userBusiness->user->id . ' | ' . $userBusiness->user->email . ' | ' .
'from: ' . $userBusiness->m_level_id . ' ' . $userBusiness->user_level_name . ' | ' .
'to: ' . $ret);
$updatedCount++;
}
}
$this->info("Updated {$updatedCount} user levels total");
$this->logExecutionTime('END Command userLevelUpdate:');
} catch (\Exception $e) {
$this->error('Error in userLevelUpdate: ' . $e->getMessage());
throw $e;
}
}
private function storeBusinessStructureUsersDetailPeriod($from, $to)
{
try {
for ($i = $from; $i <= $to; $i++) {
$this->info('Store Business Structure Users Detail month: ' . $i . ' year:' . $this->year);
$this->logMemoryUsage("Before month {$i}");
$businessUsersStore = new BusinessUsersStoreOptimized($i, $this->year);
$businessUsersStore->storeUserBusinessStructure();
$businessUsersStore->storeBusinessUsersDetail();
$bool = $businessUsersStore->storeBusinessCompleted();
$this->logExecutionTime('Period BusinessStore: ' . $bool);
$this->logMemoryUsage("After month {$i}");
}
} catch (\Exception $e) {
$this->error('Error in storeBusinessStructureUsersDetailPeriod: ' . $e->getMessage());
throw $e;
}
}
/**
* Löscht gespeicherte Business Structure Daten für den angegebenen Monat/Jahr
*/
private function clearStoredData()
{
try {
$this->info("Clearing stored business data for month: {$this->month} | year: {$this->year}");
// Finde bestehende UserBusinessStructure
$existingStructure = UserBusinessStructure::where('year', $this->year)
->where('month', $this->month)
->first();
if (!$existingStructure) {
$this->info('No stored business structure found to clear');
return;
}
$structureId = $existingStructure->id;
$this->info("Found existing structure with ID: {$structureId}");
// Lösche zugehörige UserBusiness Einträge
$deletedUserBusinesses = UserBusiness::where('b_structure_id', $structureId)->count();
if ($deletedUserBusinesses > 0) {
UserBusiness::where('b_structure_id', $structureId)->delete();
$this->info("Deleted {$deletedUserBusinesses} UserBusiness records");
}
// Lösche die UserBusinessStructure
$existingStructure->delete();
$this->info("Deleted UserBusinessStructure with ID: {$structureId}");
// Garbage Collection nach dem Löschen
gc_collect_cycles();
$this->info('Successfully cleared all stored business data');
$this->logMemoryUsage('After clearing data');
} catch (\Exception $e) {
$this->error('Error clearing stored data: ' . $e->getMessage());
throw $e;
}
}
private function logExecutionTime($message)
{
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info($message . ' | Time: ' . $sec . 'sec :' . round($micro * 1000, 4) . " ms");
}
/**
* Führt eine Funktion mit Fehlerbehandlung aus
*/
private function executeWithErrorHandling(string $processName, callable $callback): void
{
try {
$startTime = microtime(true);
$this->info("Starting: {$processName}");
$this->logMemoryUsage("Before {$processName}");
$callback();
$endTime = microtime(true);
$duration = round(($endTime - $startTime) * 1000, 2);
$this->info("Completed: {$processName} in {$duration}ms");
$this->logMemoryUsage("After {$processName}");
} catch (\Exception $e) {
$this->error("Error in {$processName}: " . $e->getMessage());
$this->error("Stack trace: " . $e->getTraceAsString());
throw $e;
}
}
/**
* Loggt aktuelle Memory-Nutzung
*/
private function logMemoryUsage(string $checkpoint): void
{
$currentMemory = memory_get_usage();
$peakMemory = memory_get_peak_usage();
$memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
$currentFormatted = $this->formatBytes($currentMemory);
$peakFormatted = $this->formatBytes($peakMemory);
$limitFormatted = $this->formatBytes($memoryLimit);
$usagePercent = round(($currentMemory / $memoryLimit) * 100, 2);
$this->info("[{$checkpoint}] Memory: {$currentFormatted} / {$limitFormatted} ({$usagePercent}%) | Peak: {$peakFormatted}");
if ($usagePercent > 80) {
$this->warn("High memory usage detected at {$checkpoint}: {$usagePercent}%");
}
}
/**
* Konvertiert Memory-Limit String zu Bytes
*/
private function parseMemoryLimit(string $limit): int
{
$limit = trim($limit);
$last = strtolower($limit[strlen($limit) - 1]);
$number = (int) $limit;
switch ($last) {
case 'g':
$number *= 1024;
case 'm':
$number *= 1024;
case 'k':
$number *= 1024;
}
return $number;
}
/**
* Formatiert Bytes in lesbare Einheiten
*/
private function formatBytes(int $bytes, int $precision = 2): string
{
$units = array('B', 'KB', 'MB', 'GB', 'TB');
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision) . ' ' . $units[$i];
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace App\Console\Commands;
use App\User;
use App\Models\UserBusiness;
use App\Services\BusinessPlan\BusinessUserItemOptimized;
use Illuminate\Console\Command;
use stdClass;
class BusinessTestAccount extends Command
{
/**
* php artisan business:test-account {user_id} {month} {year}
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'business:test-account {user_id} {month} {year}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Test account data loading for a specific user in business calculations';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
try {
$userId = (int) $this->argument('user_id');
$month = (int) $this->argument('month');
$year = (int) $this->argument('year');
$this->info("Testing account data for User ID: {$userId}, Month: {$month}, Year: {$year}");
$this->line('');
// Lade User mit Account
$user = User::with('account', 'user_level')->find($userId);
if (!$user) {
$this->error("User {$userId} not found");
return 1;
}
$this->info("User found: {$user->email}");
$this->info("Account ID: " . ($user->account_id ?? 'NULL'));
if ($user->account) {
$this->info("Account loaded: YES");
$this->info("Account m_account: " . ($user->account->m_account ?? 'NULL'));
$this->info("Account first_name: " . ($user->account->first_name ?? 'NULL'));
$this->info("Account last_name: " . ($user->account->last_name ?? 'NULL'));
$this->info("Account birthday: " . ($user->account->birthday ?? 'NULL'));
$this->info("Account phone: " . ($user->account->getPhoneNumber() ?? 'NULL'));
} else {
$this->warn("Account loaded: NO");
}
$this->line('');
$this->info('Testing BusinessUserItemOptimized...');
// Erstelle Date Object
$date = new stdClass();
$date->month = $month;
$date->year = $year;
$date->start_date = "{$year}-{$month}-01 00:00:00";
$date->end_date = date('Y-m-t 23:59:59', strtotime("{$year}-{$month}-01"));
// Teste BusinessUserItemOptimized
$businessUserItem = new BusinessUserItemOptimized($date);
$businessUserItem->makeUserFromModel($user, true);
$bUser = $businessUserItem->getBUser();
$this->line('');
$this->info('Results from BusinessUserItemOptimized:');
$this->info("m_account: " . ($bUser->m_account ?? 'NULL'));
$this->info("first_name: " . ($bUser->first_name ?? 'NULL'));
$this->info("last_name: " . ($bUser->last_name ?? 'NULL'));
$this->info("user_birthday: " . ($bUser->user_birthday ?? 'NULL'));
$this->info("user_phone: " . ($bUser->user_phone ?? 'NULL'));
$this->info("email: " . ($bUser->email ?? 'NULL'));
$this->line('');
$this->info('Sales Volume Fields:');
$this->info("sales_volume_KP_points: " . ($bUser->sales_volume_KP_points ?? 'NULL'));
$this->info("sales_volume_TP_points: " . ($bUser->sales_volume_TP_points ?? 'NULL'));
$this->info("sales_volume_points_shop: " . ($bUser->sales_volume_points_shop ?? 'NULL'));
$this->info("sales_volume_points_KP_sum: " . ($bUser->sales_volume_points_KP_sum ?? 'NULL'));
$this->info("sales_volume_points_TP_sum: " . ($bUser->sales_volume_points_TP_sum ?? 'NULL'));
$this->info("sales_volume_total: " . ($bUser->sales_volume_total ?? 'NULL'));
$this->info("sales_volume_total_shop: " . ($bUser->sales_volume_total_shop ?? 'NULL'));
$this->info("sales_volume_total_sum: " . ($bUser->sales_volume_total_sum ?? 'NULL'));
$this->line('');
$this->info('Commission Fields:');
$this->info("payline_points: " . ($bUser->payline_points ?? 'NULL'));
$this->info("commission_pp_total: " . ($bUser->commission_pp_total ?? 'NULL'));
$this->info("commission_shop_sales: " . ($bUser->commission_shop_sales ?? 'NULL'));
$this->info("commission_growth_total: " . ($bUser->commission_growth_total ?? 'NULL'));
// Test UserSalesVolume directly
$this->line('');
$this->info('Testing UserSalesVolume data directly:');
$userSalesVolume = $user->getUserSalesVolume($month, $year, 'first');
if ($userSalesVolume) {
$this->info("UserSalesVolume found: ID {$userSalesVolume->id}");
$this->info("month_KP_points: " . ($userSalesVolume->month_KP_points ?? 'NULL'));
$this->info("month_TP_points: " . ($userSalesVolume->month_TP_points ?? 'NULL'));
$this->info("month_shop_points: " . ($userSalesVolume->month_shop_points ?? 'NULL'));
$this->info("month_total_net: " . ($userSalesVolume->month_total_net ?? 'NULL'));
$this->info("month_shop_total_net: " . ($userSalesVolume->month_shop_total_net ?? 'NULL'));
} else {
$this->warn("No UserSalesVolume found for month {$month}/{$year}");
// Check if any UserSalesVolume exists for this user
$anyVolume = \App\Models\UserSalesVolume::where('user_id', $userId)->orderBy('year', 'desc')->orderBy('month', 'desc')->first();
if ($anyVolume) {
$this->info("Latest UserSalesVolume found: {$anyVolume->month}/{$anyVolume->year}");
} else {
$this->warn("No UserSalesVolume records found for this user at all");
}
}
$this->line('');
// Prüfe ob UserBusiness bereits gespeichert ist
$existingUserBusiness = UserBusiness::where('user_id', $userId)
->where('month', $month)
->where('year', $year)
->first();
if ($existingUserBusiness) {
$this->info('Existing UserBusiness found:');
$this->info("m_account: " . ($existingUserBusiness->m_account ?? 'NULL'));
$this->info("first_name: " . ($existingUserBusiness->first_name ?? 'NULL'));
$this->info("last_name: " . ($existingUserBusiness->last_name ?? 'NULL'));
$this->info("user_birthday: " . ($existingUserBusiness->user_birthday ?? 'NULL'));
$this->info("user_phone: " . ($existingUserBusiness->user_phone ?? 'NULL'));
$this->info("email: " . ($existingUserBusiness->email ?? 'NULL'));
} else {
$this->info('No existing UserBusiness found for this period');
}
$this->line('');
$this->info('✅ Test completed successfully');
return 0;
} catch (\Exception $e) {
$this->error('Test failed with error: ' . $e->getMessage());
$this->error('Stack trace: ' . $e->getTraceAsString());
return 1;
}
}
}

View file

@ -0,0 +1,213 @@
<?php
namespace App\Console\Commands;
use Carbon\Carbon;
use App\User;
use App\Services\Util;
use App\Models\UserHistory;
use App\Models\UserMessage;
use App\Mail\MailCustomMessage;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
class CheckPaymentsAccount extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'payments:check-accounts';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Überprüft Benutzer-Zahlungskonten und sendet Erinnerungen basierend auf Erneuerungsdaten.';
/**
* Execute the console command.
*/
public function handle()
{
Log::channel('cron')->info('COMMAND [payments:check-accounts] started.');
$this->info('COMMAND [payments:check-accounts] started.');
// Die Logik wurde 1:1 aus der checkPaymentsAccounts-Methode übernommen
$renewalDate = Carbon::now()->modify('+' . (config('mivita.remind_first_days') + 1) . ' days');
Log::channel('cron')->info('Erneuerungsdatum für Zahlungen: ' . $renewalDate->format('Y-m-d H:i:s'));
$users = User::where('payment_account', '!=', NULL)
->where('active', '=', 1)
->where('blocked', '!=', 1)
->where('payment_account', '<', $renewalDate)
->get();
Log::channel('cron')->info('Found ' . $users->count() . ' users for payment reminders.');
$this->info('Found ' . $users->count() . ' users for payment reminders.');
foreach ($users as $user) {
Log::channel('cron')->info('Prüfe Zahlungserinnerungen für Benutzer: ' . $user->email);
$this->checkReminderPayments($user);
}
Log::channel('cron')->info('COMMAND [payments:check-accounts] finished.');
$this->info('COMMAND [payments:check-accounts] finished.');
return 0; // Success
}
/**
* Überprüft und sendet Zahlungserinnerungen basierend auf Benutzerkontostand
*
* RULES:
* > 21 remind_first_days = 31 reminder_first
* > 21 remind_first_days + sepa = 32 reminder_first_sepa
* > 14 remind_sec_days = 33 reminder_sec
* > 2 remind_last_days = 34 reminder_last
* > 0 deaktiv = 35 reminder_deaktiv
* > 0 deaktiv + sepa = 36 reminder_deaktiv_sepa
* == 7 abo_booking_days + sepa + cron = 37 reminder_collect_sepa
*
* @param User $user Benutzer
* @return void
*/
private function checkReminderPayments(User $user)
{
//35 reminder_deaktiv, 36 reminder_deaktiv_sepa
if (!$user->isActiveAccount()) {
Log::channel('cron')->info('Inaktives Konto für Benutzer: ' . $user->email);
$this->checkIsReminderSend($user, 35);
return;
}
//34 reminder_last
if ($user->daysActiveAccount() <= config('mivita.remind_last_days')) {
Log::channel('cron')->info('Letzte Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
$this->checkIsReminderSend($user, 34);
return;
}
//33 reminder_sec
if ($user->daysActiveAccount() <= config('mivita.remind_sec_days')) {
Log::channel('cron')->info('Zweite Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
$this->checkIsReminderSend($user, 33);
return;
}
//31 reminder_first
if ($user->daysActiveAccount() > config('mivita.remind_sec_days')) {
Log::channel('cron')->info('Erste Erinnerung für Benutzer: ' . $user->email . ' (Tage aktiv: ' . $user->daysActiveAccount() . ')');
$this->checkIsReminderSend($user, 31);
return;
}
}
/**
* Überprüft, ob eine Erinnerung bereits gesendet wurde
*
* @param User $user Benutzer
* @param int $status Status-Code der Erinnerung
* @return bool
*/
private function checkIsReminderSend(User $user, $status)
{
$isSend = UserHistory::whereUserId($user->id)
->whereAction('reminder_payments')
->whereIdentifier($user->payment_account)
->whereStatus($status)
->latest()
->first();
if ($isSend) {
Log::channel('cron')->info('Erinnerung bereits gesendet für Benutzer: ' . $user->email . ' (Status: ' . $status . ')');
return true;
}
Log::channel('cron')->info('Sende neue Erinnerung für Benutzer: ' . $user->email . ' (Status: ' . $status . ')');
$referenz = $this->sendReminderMail($user, $status);
UserHistory::create([
'user_id' => $user->id,
'action' => 'reminder_payments',
'referenz' => $referenz,
'identifier' => $user->payment_account,
'status' => $status
]);
return false;
}
/**
* Sendet eine Erinnerungs-E-Mail an den Benutzer
*
* @param User $user Benutzer
* @param int $status Status-Code der Erinnerung
* @return int
*/
private function sendReminderMail(User $user, $status)
{
$days = abs($user->daysActiveAccount());
$pay_date = Carbon::parse($user->payment_account)->modify('- ' . config('mivita.abo_booking_days') . ' days')->format('d.m.Y');
$datetime = $user->getPaymentAccountDateFormat();
$price = "";
if ($user->payment_order_id && isset($user->payment_order_product->price)) {
$price = 'von ' . $user->payment_order_product->getFormattedPrice() . ' EUR';
}
$message = __('reminder.copy_first_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]);
$message_last = __('reminder.copy_last_' . $status, ['days' => $days, 'datetime' => $datetime, 'price' => $price, 'pay_date' => $pay_date]);
$button = __('reminder.button_' . $status);
$message = preg_replace("/[\n\r]/", "", $message);
$message_last = preg_replace("/[\n\r]/", "", $message_last);
$data = [
'subject' => __('reminder.subject') . " | ID: " . $status,
'message' => $message,
'message_last' => $message_last,
'url' => config('app.url') . '/user/membership',
'button' => $button,
];
$sender = User::find(1);
$customer_mail = UserMessage::create(['user_id' => $user->id, 'send_user_id' => $sender->id, 'email' => $user->email, 'subject' => $data['subject'], 'message' => $data['message'] . " " . $data['message_last']]);
try {
if (!Util::isTestSystem()) {
if ($status >= 34) {
Log::channel('cron')->info('Sende kritische Erinnerung mit BCC an: ' . $user->email);
Mail::to($user->email)
->locale($user->getLocale())
->bcc(config('app.default_mail'))
->send(new MailCustomMessage($user, $data, $sender, false));
} else {
Log::channel('cron')->info('Sende normale Erinnerung an: ' . $user->email);
Mail::to($user->email)
->locale($user->getLocale())
->send(new MailCustomMessage($user, $data, $sender, false));
}
} else {
Log::channel('cron')->info('Testsystem: E-Mail-Versand simuliert für: ' . $user->email);
}
} catch (\Exception $e) {
Log::channel('cron')->error('Mail-Fehler für Benutzer ' . $user->email . ': ' . $e->getMessage());
$customer_mail->fail = true;
$customer_mail->error = $e->getMessage();
$customer_mail->save();
return 0;
}
$customer_mail->send = true;
$customer_mail->sent_at = now();
$customer_mail->save();
Log::channel('cron')->info('Erinnerungsmail erfolgreich gesendet an: ' . $user->email);
return 1;
}
}

View file

@ -0,0 +1,664 @@
<?php
namespace App\Console\Commands;
use App\Http\Controllers\Api\KasController;
use App\Http\Controllers\Api\KasSLLController;
use App\Models\UserShop;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class SubDomains extends Command
{
/**
* Die Signatur des Konsolenbefehls.
*
* Aufruf: php artisan subdomains:action --force --start=4 --debug
* /usr/bin/php82 artisan subdomains:action --force --start=4 --create-missing --debug
* /usr/bin/php82 artisan subdomains:action --force --debug
* @var string
*/
protected $signature = 'subdomains:action {user_id?} {--force} {--start=1} {--create-missing} {--debug}';
/**
* Die Beschreibung des Konsolenbefehls.
*
* @var string
*/
protected $description = 'Passt Parameter für die User-Subdomains an (PHP-Version, SSL)';
/**
* Zeitstempel für die Ausführungszeit-Messung
*
* @var float
*/
private $timeStart;
/**
* Standard-Domain für alle Subdomains
*
* @var string
*/
private $domain = 'mivita.care';
/**
* Zu überspringende Subdomain-Präfixe
*
* @var array
*/
private $skipPrefixes = ['www.', 'api.', 'checkout.', 'preview.'];
/**
* Erstellt eine neue Befehlsinstanz.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Führt den Konsolenbefehl aus.
*
* @return int
*/
public function handle()
{
$this->timeStart = microtime(true);
$userId = $this->argument('user_id');
$force = $this->option('force');
$startId = $this->option('start');
$createMissing = $this->option('create-missing');
$debug = $this->option('debug');
if ($debug) {
$this->warn("DEBUG-MODUS (DRY-RUN): Es werden keine tatsächlichen Änderungen vorgenommen!");
}
$this->info("Starte Subdomain-Verwaltung" . ($force ? " (erzwungener Modus)" : ""));
try {
if ($userId) {
$this->info("Verarbeite einzelnen Benutzer mit ID: {$userId}");
$result = $this->syncSingleUser($userId, $force, $createMissing, $debug);
if ($result) {
$this->info("Benutzer {$userId} erfolgreich synchronisiert" . ($debug ? " (simuliert)" : ""));
} else {
$this->warn("Benutzer {$userId} konnte nicht vollständig synchronisiert werden" . ($debug ? " (simuliert)" : ""));
}
} else {
$this->info("Verarbeite alle Benutzer ab ID: {$startId}");
$result = $this->syncAllUsers($force, $startId, $createMissing, $debug);
// Zusammenfassung der Ergebnisse
$this->displaySummary($result, $debug);
}
$this->logExecutionTime("Subdomain-Verwaltung abgeschlossen" . ($debug ? " (DEBUG-MODUS)" : ""));
return 0;
} catch (Exception $e) {
$this->error("Ein Fehler ist aufgetreten: " . $e->getMessage());
Log::error("Shopping User Sync Fehler: ", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'user_id' => $userId,
'force' => $force,
'start_id' => $startId,
'debug' => $debug
]);
return 1;
}
}
/**
* Zeigt eine Zusammenfassung der Synchronisationsergebnisse an
*
* @param array $result Ergebnisdaten der Synchronisation
* @param bool $debug Debug-Modus (Dry-Run)
* @return void
*/
private function displaySummary($result, $debug = false)
{
$this->line("");
$this->info("=== Zusammenfassung " . ($debug ? "(DEBUG-MODUS)" : "") . " ===");
$this->info("Verarbeitete Shops: " . count($result['shops']));
$this->info("Aktualisierte PHP-Versionen: " . $result['updatedCount'] . ($debug ? " (simuliert)" : ""));
$this->info("Aktivierte SSL-Zertifikate: " . $result['sslEnabledCount'] . ($debug ? " (simuliert)" : ""));
$this->info("Aktualisierte SSL-Konfigurationen: " . $result['sslConfiguredCount'] . ($debug ? " (simuliert)" : ""));
if (!empty($result['createdSubdomains'])) {
$this->info("Neu erstellte Subdomains: " . count($result['createdSubdomains']) . ($debug ? " (simuliert)" : ""));
}
if (!empty($result['missingSubdomains'])) {
$this->warn("Fehlende Subdomains: " . count($result['missingSubdomains']));
}
if (!empty($result['unusedSubdomains'])) {
$this->warn("Ungenutzte Subdomains: " . count($result['unusedSubdomains']));
}
if (!empty($result['doubleDomains'])) {
$this->warn("Benutzer mit mehreren Shops: " . count($result['doubleDomains']));
}
}
/**
* Synchronisiert einen einzelnen Benutzer und seine Shops
*
* @param int $userId Benutzer-ID
* @param bool $force Erzwingt die Aktualisierung aller Subdomains
* @param bool $createMissing Erstellt fehlende Subdomains
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function syncSingleUser($userId, $force = false, $createMissing = false, $debug = false)
{
$this->info("Synchronisiere Benutzer mit ID: {$userId}");
// Benutzer-Shops abrufen
$userShops = UserShop::where('user_id', $userId)->get();
if ($userShops->isEmpty()) {
$this->warn("Keine Shops für Benutzer {$userId} gefunden");
return false;
}
$this->info("Gefundene Shops für Benutzer {$userId}: " . $userShops->count());
// Subdomains abrufen und filtern
$subdomains = $this->getFilteredSubdomains();
$success = true;
// Benutzer-Shops durchlaufen und mit Subdomains abgleichen
foreach ($userShops as $userShop) {
$fullDomainName = $userShop->slug . '.' . $this->domain;
$this->info("Verarbeite Shop: {$fullDomainName}");
// Prüfen, ob Subdomain existiert
if (array_key_exists($fullDomainName, $subdomains)) {
$success = $this->processExistingSubdomain($userShop, $subdomains[$fullDomainName], $force, $debug) && $success;
} else {
// Subdomain fehlt
$this->warn("Shop {$userShop->slug}: Keine Subdomain gefunden");
// Optional: Neue Subdomain erstellen
if ($createMissing) {
$this->info("Erstelle fehlende Subdomain für Shop {$userShop->slug}" . ($debug ? " (simuliert)" : ""));
$success = $this->createSubdomain($userShop->slug, $debug) && $success;
} else {
$success = false;
}
}
}
return $success;
}
/**
* Verarbeitet eine existierende Subdomain und aktualisiert sie bei Bedarf
*
* @param UserShop $userShop Shop-Objekt
* @param array $subdomainInfo Subdomain-Informationen
* @param bool $force Erzwingt die Aktualisierung
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function processExistingSubdomain($userShop, $subdomainInfo, $force, $debug = false)
{
$success = true;
$hasSSL = ($subdomainInfo['ssl_certificate_sni'] === 'Y' ||
$subdomainInfo['ssl_proxy'] === 'Y');
$sslActive = ($subdomainInfo['ssl_certificate_sni_is_active'] ?? 'N') === 'Y';
$phpVersion = $subdomainInfo['php_version'];
$this->info("Shop {$userShop->slug}: PHP-Version: {$phpVersion}, SSL: " .
($hasSSL ? "Aktiviert" : "Nicht aktiviert") .
($hasSSL ? ", SSL aktiv: " . ($sslActive ? "Ja" : "Nein") : ""));
// Prüfen, ob PHP-Version aktualisiert werden muss
$requiredPhpVersion = config('app.php_version');
if ($force || $phpVersion !== $requiredPhpVersion) {
$this->info("Shop {$userShop->slug}: PHP-Version aktualisieren von {$phpVersion} auf {$requiredPhpVersion}" . ($debug ? " (simuliert)" : ""));
if (!$debug && !$this->updateSubdomainParams($userShop->slug, $requiredPhpVersion)) {
$this->error("PHP-Version für {$userShop->slug}.{$this->domain} konnte nicht aktualisiert werden");
$success = false;
}
}
// Prüfen, ob SSL aktiviert werden muss
if ($force || !$hasSSL) {
$this->info("Shop {$userShop->slug}: SSL aktivieren" . ($debug ? " (simuliert)" : ""));
if (!$debug && !$this->enableSSL($userShop->slug)) {
$this->error("SSL für {$userShop->slug}.{$this->domain} konnte nicht aktiviert werden");
$success = false;
}
}
// Prüfen, ob SSL-Konfiguration aktualisiert werden muss
else if ($force || ($hasSSL && !$sslActive)) {
$this->info("Shop {$userShop->slug}: SSL-Konfiguration aktualisieren" . ($debug ? " (simuliert)" : ""));
if (!$debug && !$this->updateSSL($userShop->slug . '.' . $this->domain)) {
$this->error("SSL-Konfiguration für {$userShop->slug}.{$this->domain} konnte nicht aktualisiert werden");
$success = false;
}
}
return $success;
}
/**
* Synchronisiert alle Benutzer-Shops
*
* @param bool $force Erzwingt die Aktualisierung aller Subdomains
* @param int $startId Beginnt die Synchronisation ab dieser ID
* @param bool $createMissing Erstellt fehlende Subdomains
* @param bool $debug Debug-Modus (Dry-Run)
* @return array Ergebnisdaten der Synchronisation
*/
private function syncAllUsers($force, $startId, $createMissing = false, $debug = false)
{
$this->info("Starte Synchronisation aller Benutzer-Shops ab ID: {$startId}");
// Benutzer-Shops abrufen
$userShopsQuery = UserShop::query();
if ($startId > 1) {
$userShopsQuery->where('id', '>=', $startId);
}
$userShops = $userShopsQuery->limit(1000)->get();
$this->info("Gefundene Benutzer-Shops: " . $userShops->count());
// Subdomains abrufen und filtern
$subdomains = $this->getFilteredSubdomains();
$this->info("Gefilterte Subdomains: " . count($subdomains));
// Ergebnis-Arrays initialisieren
$doubleDomains = [];
$missingSubdomains = [];
$outdatedPhpVersions = [];
$missingSSL = [];
$sslConfigurationNeeded = [];
$createdSubdomains = [];
$updatedCount = 0;
$sslEnabledCount = 0;
$sslConfiguredCount = 0;
// Benutzer-Shops durchlaufen und mit Subdomains abgleichen
foreach ($userShops as $userShop) {
$fullDomainName = $userShop->slug . '.' . $this->domain;
// Status der Subdomain setzen
$userShop->hasSubdomain = false;
$userShop->hasSSL = false;
$userShop->sslActive = false;
$userShop->PHPversion = "";
// Prüfen, ob Subdomain existiert
if (array_key_exists($fullDomainName, $subdomains)) {
$userShop->hasSubdomain = true;
$userShop->hasSSL = ($subdomains[$fullDomainName]['ssl_certificate_sni'] === 'Y' ||
$subdomains[$fullDomainName]['ssl_proxy'] === 'Y');
$userShop->sslActive = ($subdomains[$fullDomainName]['ssl_certificate_sni_is_active'] ?? 'N') === 'Y';
$userShop->PHPversion = $subdomains[$fullDomainName]['php_version'];
// Prüfen, ob PHP-Version aktualisiert werden muss
$requiredPhpVersion = config('app.php_version');
if ($force || $userShop->PHPversion !== $requiredPhpVersion) {
$this->info("Shop {$userShop->slug}: PHP-Version aktualisieren von {$userShop->PHPversion} auf {$requiredPhpVersion}" . ($debug ? " (simuliert)" : ""));
$outdatedPhpVersions[] = $userShop->slug;
if (!$debug && $this->updateSubdomainParams($userShop->slug, $requiredPhpVersion)) {
$updatedCount++;
} else if ($debug) {
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
$updatedCount++;
}
}else{
$this->info("Shop {$userShop->slug}: PHP-Version ist aktuell: {$userShop->PHPversion}");
}
// Prüfen, ob SSL aktiviert werden muss
/* if ($force || !$userShop->hasSSL) {
$this->info("Shop {$userShop->slug}: SSL aktivieren" . ($debug ? " (simuliert)" : ""));
$missingSSL[] = $userShop->slug;
if (!$debug && $this->enableSSL($userShop->slug)) {
$sslEnabledCount++;
} else if ($debug) {
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
$sslEnabledCount++;
}
}
// Prüfen, ob SSL-Konfiguration aktualisiert werden muss
else if ($force || ($userShop->hasSSL && !$userShop->sslActive)) {
$this->info("Shop {$userShop->slug}: SSL-Konfiguration aktualisieren" . ($debug ? " (simuliert)" : ""));
$sslConfigurationNeeded[] = $userShop->slug;
if (!$debug && $this->updateSSL($fullDomainName)) {
$sslConfiguredCount++;
} else if ($debug) {
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
$sslConfiguredCount++;
}
}*/
// Subdomain aus der Liste entfernen, um später ungenutzte zu identifizieren
unset($subdomains[$fullDomainName]);
} else {
// Subdomain fehlt
$missingSubdomains[] = $userShop->slug;
$this->warn("Shop {$userShop->slug}: Keine Subdomain gefunden");
// Optional: Neue Subdomain erstellen
if ($createMissing) {
$this->info("Erstelle fehlende Subdomain für Shop {$userShop->slug}" . ($debug ? " (simuliert)" : ""));
if (!$debug && $this->createSubdomain($userShop->slug)) {
$createdSubdomains[] = $userShop->slug;
} else if ($debug) {
// Im Debug-Modus zählen wir trotzdem, als ob es erfolgreich wäre
$createdSubdomains[] = $userShop->slug;
}
}
}
// Doppelte Domains pro Benutzer erfassen
$doubleDomains[$userShop->user_id][$userShop->id] = $fullDomainName;
}
// Bereinigen der doppelten Domains (nur Benutzer mit mehreren Shops behalten)
foreach ($doubleDomains as $userId => $domains) {
if (count($domains) === 1) {
unset($doubleDomains[$userId]);
}
}
$this->logExecutionTime("Synchronisation abgeschlossen" . ($debug ? " (DEBUG-MODUS)" : ""));
// Ergebnisdaten zurückgeben
return [
'shops' => $userShops,
'unusedSubdomains' => $subdomains,
'doubleDomains' => $doubleDomains,
'missingSubdomains' => $missingSubdomains,
'outdatedPhpVersions' => $outdatedPhpVersions,
'missingSSL' => $missingSSL,
'sslConfigurationNeeded' => $sslConfigurationNeeded,
'createdSubdomains' => $createdSubdomains,
'updatedCount' => $updatedCount,
'sslEnabledCount' => $sslEnabledCount,
'sslConfiguredCount' => $sslConfiguredCount
];
}
/**
* Ruft alle Subdomains ab und filtert sie
*
* @return array Gefilterte Subdomains
*/
private function getFilteredSubdomains()
{
$kas = new KasController();
$subdomains = [];
// Alle Subdomains abrufen
$this->info("Rufe Subdomains von KAS ab...");
$getSubdomains = $kas->action('get_subdomains');
// Subdomains filtern und in ein leicht zugängliches Array umwandeln
foreach ($getSubdomains as $subdomain) {
if (!isset($subdomain['subdomain_name'])) {
continue;
}
// Spezielle Subdomains überspringen
$skip = false;
foreach ($this->skipPrefixes as $prefix) {
if (strpos($subdomain['subdomain_name'], $prefix) !== false) {
$skip = true;
break;
}
}
if ($skip) {
continue;
}
// Subdomain-Informationen speichern
$subdomains[$subdomain['subdomain_name']] = [
'ssl_certificate_sni' => $subdomain['ssl_certificate_sni'] ?? 'N',
'php_version' => $subdomain['php_version'] ?? '',
'ssl_proxy' => $subdomain['ssl_proxy'] ?? 'N',
'ssl_certificate_sni_is_active' => $subdomain['ssl_certificate_sni_is_active'] ?? 'N',
];
}
return $subdomains;
}
/**
* Ändert Parameter einer Subdomain, insbesondere die PHP-Version
*
* @param string $subdomain Name der Subdomain ohne Domain
* @param string $phpVersion Neue PHP-Version (z.B. '8.2')
* @param array $additionalParams Weitere zu ändernde Parameter
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function updateSubdomainParams($subdomain, $phpVersion, $additionalParams = [], $debug = false)
{
$this->info("Aktualisiere Parameter für: {$subdomain}.{$this->domain}" . ($debug ? " (simuliert)" : ""));
if ($debug) {
$this->line(" - PHP-Version: {$phpVersion}");
if (!empty($additionalParams)) {
$this->line(" - Zusätzliche Parameter: " . json_encode($additionalParams));
}
return true;
}
try {
$kas = new KasController();
// Standardparameter
$params = [
'subdomain_name' => $subdomain . '.' . $this->domain, // Vollständigen Domainnamen verwenden
'php_version' => $phpVersion
];
// Oder alternativ, falls die API die Subdomain und Domain getrennt erwartet:
// $params = [
// 'subdomain_name' => $subdomain,
// 'domain_name' => $this->domain,
// 'php_version' => $phpVersion
// ];
// Zusätzliche Parameter hinzufügen
$params = array_merge($params, $additionalParams);
// Subdomain aktualisieren
$result = $kas->action('update_subdomain', $params);
$this->info("Parameter: ".json_encode($params));
if ($result) {
$this->info("Parameter für {$subdomain}.{$this->domain} erfolgreich aktualisiert " . json_encode($result));
return true;
} else {
$this->error("Fehler beim Aktualisieren der Parameter für {$subdomain}.{$this->domain}: " . json_encode($result));
return false;
}
} catch (Exception $e) {
$this->error("Fehler beim Aktualisieren der Parameter für {$subdomain}.{$this->domain}: " . $e->getMessage());
Log::error("Subdomain Parameter Update Fehler", [
'subdomain' => $subdomain,
'domain' => $this->domain,
'php_version' => $phpVersion,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return false;
}
}
/**
* Aktualisiert die SSL-Konfiguration einer Subdomain mit erweiterten Parametern
*
* @param string $subdomainName Vollständiger Domainname (z.B. 'shop.mivita.care')
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function updateSSL($subdomainName, $debug = false)
{
$this->info("Aktualisiere SSL-Konfiguration für: {$subdomainName}" . ($debug ? " (simuliert)" : ""));
if ($debug) {
$this->line(" - SSL-Parameter werden aktualisiert");
return true;
}
try {
$kas = new KasController();
$ssl = KasSLLController::getApiSSLParameter();
$params = array_merge(['hostname' => $subdomainName], $ssl);
$result = $kas->action('update_ssl', $params);
if ($result === "TRUE" || $result === true) {
$this->info("SSL-Konfiguration für {$subdomainName} erfolgreich aktualisiert");
return true;
} else {
$this->warn("SSL-Konfiguration für {$subdomainName} nicht vollständig aktualisiert: " . json_encode($result));
return false;
}
} catch (Exception $e) {
$this->error("Fehler bei der SSL-Konfiguration für {$subdomainName}: " . $e->getMessage());
Log::error("SSL Update Fehler", [
'subdomain' => $subdomainName,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return false;
}
}
/**
* Aktiviert SSL für eine Subdomain mit vollständiger Konfiguration
*
* @param string $subdomain Name der Subdomain ohne Domain
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function enableSSL($subdomain, $debug = false)
{
$fullDomainName = $subdomain . '.' . $this->domain;
$this->info("Aktiviere SSL für: {$fullDomainName}" . ($debug ? " (simuliert)" : ""));
if ($debug) {
$this->line(" - SSL-Proxy wird aktiviert");
$this->line(" - HTTP zu HTTPS Weiterleitung wird eingerichtet");
$this->line(" - SSL-Konfiguration wird aktualisiert");
return true;
}
// Schritt 1: Subdomain-Parameter aktualisieren (ssl_proxy aktivieren)
$subdomainResult = $this->updateSubdomainParams($subdomain, config('app.php_version'), [
'ssl_proxy' => 'Y',
'redirect_status' => 301 // Weiterleitung von HTTP auf HTTPS
]);
if (!$subdomainResult) {
$this->error("SSL-Aktivierung für {$fullDomainName} fehlgeschlagen: Subdomain-Parameter konnten nicht aktualisiert werden");
return false;
}
// Schritt 2: SSL-Konfiguration aktualisieren
$sslResult = $this->updateSSL($fullDomainName);
if (!$sslResult) {
$this->warn("SSL-Aktivierung für {$fullDomainName} teilweise erfolgreich: SSL-Konfiguration konnte nicht aktualisiert werden");
return false;
}
$this->info("SSL für {$fullDomainName} vollständig aktiviert");
return true;
}
/**
* Erstellt eine neue Subdomain für einen Shop
*
* @param string $slug Shop-Slug
* @param bool $debug Debug-Modus (Dry-Run)
* @return bool Erfolg der Operation
*/
private function createSubdomain($slug, $debug = false)
{
$fullDomainName = $slug . '.' . $this->domain;
$this->info("Erstelle neue Subdomain: {$fullDomainName}" . ($debug ? " (simuliert)" : ""));
if ($debug) {
$this->line(" - Pfad: /mein.mivita.care/public/");
$this->line(" - PHP-Version: " . config('app.php_version'));
$this->line(" - SSL wird direkt aktiviert");
return true;
}
try {
$kas = new KasController();
// Parameter für die neue Subdomain
$params = [
'subdomain_name' => $slug,
'domain_name' => $this->domain,
'subdomain_path' => '/mein.mivita.care/public/',
'php_version' => config('app.php_version'),
];
// Subdomain erstellen
$result = $kas->action('add_subdomain', $params);
if ($result === $fullDomainName) {
$this->info("Subdomain {$fullDomainName} erfolgreich erstellt");
// SSL direkt aktivieren
$this->enableSSL($slug);
return true;
} else {
$this->error("Fehler beim Erstellen der Subdomain {$fullDomainName}: " . json_encode($result));
return false;
}
} catch (Exception $e) {
$this->error("Fehler beim Erstellen der Subdomain {$fullDomainName}: " . $e->getMessage());
Log::error("Subdomain Erstellung Fehler", [
'slug' => $slug,
'domain' => $this->domain,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return false;
}
}
/**
* Protokolliert die Ausführungszeit einer Operation
*
* @param string $message Nachricht für die Protokollierung
* @return void
*/
private function logExecutionTime($message)
{
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info($message. ' | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\User;
use App\Services\ShoppingUserService;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
class SyncShoppingUserData extends Command
{
//aufruf: php artisan shopping:sync-user-data --force --start=4
protected $signature = 'shopping:sync-user-data {user_id?} {--force} {--start=1}';
protected $description = 'Synchronisiere Shopping User Daten für einen oder alle User';
public function handle()
{
$userId = $this->argument('user_id');
$force = $this->option('force');
$startId = $this->option('start');
try {
if ($userId) {
$this->syncSingleUser($userId);
} else {
$this->syncAllUsers($force, $startId);
}
} catch (Exception $e) {
$this->error("Ein Fehler ist aufgetreten: " . $e->getMessage());
Log::error("Shopping User Sync Fehler: ", [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return 1;
}
return 0;
}
private function syncAllUsers($force, $startId)
{
$this->info("Starte Synchronisierung für alle User ab ID: {$startId}...");
$count = 0;
$errors = [];
// Aktiviere SQL Query Logging für Debugging
DB::enableQueryLog();
User::where('id', '>=', $startId)
->orderBy('id')
->chunk(10, function($users) use (&$count, &$errors, $force) {
foreach($users as $user) {
try {
$this->info("\nVerarbeite User ID: {$user->id} ({$user->email})");
$this->syncUser($user);
$count++;
$this->info("✓ User ID {$user->id} erfolgreich synchronisiert");
} catch (Exception $e) {
$errorMessage = "Fehler bei User ID {$user->id} ({$user->email}): " . $e->getMessage();
$errors[] = $errorMessage;
$this->error($errorMessage);
// Log die letzten SQL Queries
Log::error("Letzte SQL Queries:", [
'queries' => DB::getQueryLog()
]);
if (!$force) {
throw $e;
}
}
}
});
$this->newLine();
$this->info("Synchronisierung abgeschlossen!");
$this->info("Gesamt synchronisierte User: {$count}");
if (count($errors) > 0) {
$this->warn("Es gab " . count($errors) . " Fehler während der Synchronisierung:");
foreach($errors as $error) {
$this->warn("- " . $error);
}
}
}
private function syncUser(User $user)
{
try {
$this->output->write(" Setze Faker Mail... ");
ShoppingUserService::setFakerMail($user);
$this->info("");
$this->output->write(" Synchronisiere Numbers... ");
ShoppingUserService::syncNumbersByEmail($user);
$this->info("");
$this->output->write(" Synchronisiere Orders... ");
ShoppingUserService::syncOrdersByEmail($user);
$this->info("");
} catch (Exception $e) {
throw new Exception($e->getMessage() . "\n" . $e->getTraceAsString());
}
}
private function syncSingleUser($userId)
{
$user = User::find($userId);
if (!$user) {
throw new Exception("User ID {$userId} nicht gefunden");
}
$this->info("Starte Synchronisierung für User ID {$userId}...");
try {
$this->syncUser($user);
$this->info("✓ Synchronisierung erfolgreich abgeschlossen");
} catch (Exception $e) {
throw new Exception("Fehler bei User ID {$userId}: " . $e->getMessage());
}
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace App\Console\Commands;
use App\User;
use Carbon\Carbon;
use App\Services\UserUtil;
use Illuminate\Console\Command;
class UserCleanUp extends Command
{
/**
* ln -sfv /usr/bin/php73 /usr/bin/php
* php74 artisan user:cleanup
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:cleanup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'User Clean Up inactive for Business Structur and UserDetails';
private $timeStart;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('RUN Command user:cleanup');
\Log::channel('cleanup')->info('COMMAND [user:cleanup] started.');
$this->timeStart = microtime(true);
$this->deleteInavtiveUsers();
//alle inaktive User werden deaktivert, die childs werden dem nächsten aktiven Berater (parent) zugewiesen.
$this->cleanUpInActiveUser();
return 0;
\Log::channel('cleanup')->info('COMMAND [user:cleanup] finished.');
//return 0;
}
//gibt es gelöschte Berater mit Kunden und childs???
private function deleteInavtiveUsers()
{
$this->info('START Command deleteInavtiveUsers');
$count = 0;
$date = Carbon::now()->modify('-2 month');
$delete_users = User::where('admin', 0)->where('payment_account', '<', $date)->get();
foreach ($delete_users as $delete_user) {
/*
dump('delete_users ---------- ');
dump($delete_user->id);
dump($delete_user->email);
*/
//finde nächsten aktiven Sponsor $delete_user->id kann sponsor oder pre sponsor sein
$active_sponsor = UserUtil::findNextActiveSponsor($delete_user->id);
if ($active_sponsor) {
//setze alle Berater vom Sponsor für alle childs
UserUtil::setNewSponsorToChilds($delete_user->id, $active_sponsor->id);
UserUtil::setShoppingUserToNewMember($delete_user->id, $active_sponsor->id);
} else {
\Log::channel('cleanup')->error('deleteInavtiveUsers find no active_sponsor by delete_user_id:' . $delete_user->id);
continue;
}
/*
dump('findNextActiveSponsor');
dump($active_sponsor->email);
*/
//make User to an Client from sponsor and delete User
UserUtil::setUserToClient($delete_user->id, $active_sponsor->id);
$data = [
'user_id' => $delete_user->id,
'email' => $delete_user->email,
'm_account' => $delete_user->account ? $delete_user->account->m_account : '',
'm_first_name' => $delete_user->account ? $delete_user->account->m_first_name : '',
'm_last_name' => $delete_user->account ? $delete_user->account->m_last_name : '',
];
$count++;
\Log::channel('cleanup')->info('deleteUser: ' . json_encode($data));
UserUtil::deleteUser($delete_user);
}
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info('END Command deleteInavtiveUsers: ' . $count . ' | Time: ' . $sec . 'sec :' . round($micro * 1000, 4) . " ms");
}
private function cleanUpInActiveUser()
{
$this->info('START Command cleanUpInActiveUser');
$count = 0;
//clean up user where inactive since 2 weeks
$date = Carbon::now()->modify('-2 weeks');
$inactive_users = User::where('active', true)->where('m_sponsor', '!=', null)->where('payment_account', '<', $date)->get();
foreach ($inactive_users as $inactive_user) {
/*
dump('inactive_user ---------- ');
dump($inactive_user->id);
dump($inactive_user->email);
*/
$active_sponsor = UserUtil::findNextActiveSponsor($inactive_user->m_sponsor);
if ($active_sponsor) {
UserUtil::setNewSponsorToChilds($inactive_user->id, $active_sponsor->id);
} else {
\Log::channel('cleanup')->error('cleanUpInActiveUser find no active_sponsor by inactive_user:' . $inactive_user->id);
}
/*
dump('findNextActiveSponsor');
dump($active_sponsor->email);
*/
$data = [
'user_id' => $inactive_user->id,
'email' => $inactive_user->email,
'm_account' => $inactive_user->account ? $inactive_user->account->m_account : '',
'm_first_name' => $inactive_user->account ? $inactive_user->account->m_first_name : '',
'm_last_name' => $inactive_user->account ? $inactive_user->account->m_last_name : '',
];
$count++;
\Log::channel('cleanup')->info('inactive_user: ' . json_encode($data));
UserUtil::deactiveUser($inactive_user);
}
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info('END Command cleanUpInActiveUser: ' . $count . ' | Time: ' . $sec . 'sec :' . round($micro * 1000, 4) . " ms");
}
}

View file

@ -0,0 +1,292 @@
<?php
namespace App\Console\Commands;
use Carbon\Carbon;
use App\Models\Setting;
use App\Models\UserAbo;
use App\Services\MyLog;
use App\Services\Payment;
use App\Cron\UserMakeOrder;
use App\Services\AboHelper;
use App\Models\UserAboOrder;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class UserMakeAboOrder extends Command
{
/**
* ln -sfv /usr/bin/php73 /usr/bin/php
* php artisan business:store month year
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:make_abo_order';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Make Orders from Abos';
private $timeStart;
private $month;
private $year;
private $sendCreditMail = false;
private $sendUpdateMail = false;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->timeStart = microtime(true);
\Log::channel('cron')->info('UserMakeAboOrder: Befehl gestartet');
$this->info('RUN Command user:make_abo_order');
try {
$this->checkAbosToOrder();
$executionTime = $this->getExecutionTime();
\Log::channel('cron')->info("UserMakeAboOrder: Befehl erfolgreich abgeschlossen in {$executionTime}");
$this->info("Befehl erfolgreich abgeschlossen in {$executionTime}");
return 0;
} catch (\Exception $e) {
\Log::channel('cron')->error('UserMakeAboOrder: Fehler beim Ausführen des Befehls', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error('Fehler beim Ausführen des Befehls: ' . $e->getMessage());
return 1;
}
}
/**
* Prüft alle Abos, die heute fällig sind und erstellt Bestellungen
*
* @return void
*/
private function checkAbosToOrder()
{
$dateNow = Carbon::now()->format('Y-m-d');
\Log::channel('abo_order')->info('UserMakeAboOrder: Suche nach fälligen Abos für Datum', ['date' => $dateNow]);
$userAbos = UserAbo::where('next_date', '=', $dateNow)
->where('active', true)
->get();
$count = $userAbos->count();
\Log::channel('abo_order')->info("UserMakeAboOrder: {$count} fällige Abos gefunden");
$this->info("Gefundene fällige Abos: {$count}");
foreach ($userAbos as $userAbo) {
\Log::channel('abo_order')->info('UserMakeAboOrder: Verarbeite Abo', [
'abo_id' => $userAbo->id,
'payone_userid' => $userAbo->payone_userid
]);
$this->info("Verarbeite Abo: {$userAbo->id} (PayoneUserid: {$userAbo->payone_userid})");
try {
$shoppingOrder = $this->makeOrder($userAbo);
if ($shoppingOrder) {
\Log::channel('abo_order')->info('UserMakeAboOrder: Bestellung erstellt', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id
]);
$this->info("Bestellung erstellt: {$shoppingOrder->id}");
} else {
\Log::channel('abo_order')->warning('UserMakeAboOrder: Keine Bestellung erstellt für Abo', ['abo_id' => $userAbo->id]);
$this->warn("Keine Bestellung erstellt für Abo: {$userAbo->id}");
}
} catch (\Exception $e) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler bei der Verarbeitung des Abos', [
'abo_id' => $userAbo->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error("Fehler bei Abo {$userAbo->id}: " . $e->getMessage());
}
}
}
/**
* Erstellt eine Bestellung für ein Abo
*
* @param UserAbo $userAbo
* @return mixed
*/
private function makeOrder($userAbo)
{
\Log::channel('abo_order')->info('UserMakeAboOrder: Starte Bestellungserstellung', ['abo_id' => $userAbo->id]);
$this->info('Starte Bestellungserstellung für Abo: ' . $userAbo->id);
$shoppingOrder = null;
$userOrder = new UserMakeOrder($userAbo);
try {
if (!$userOrder->createShoppingUser()) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Konnte Shopping-User nicht erstellen', ['abo_id' => $userAbo->id]);
$this->error("Konnte Shopping-User für Abo {$userAbo->id} nicht erstellen");
return null;
}
$shoppingOrder = $userOrder->makeShoppingOrder();
if (!$shoppingOrder) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Konnte Bestellung nicht erstellen', ['abo_id' => $userAbo->id]);
$this->error("Konnte Bestellung für Abo {$userAbo->id} nicht erstellen");
return null;
}
\Log::channel('abo_order')->info('UserMakeAboOrder: Bestellung erstellt, starte Zahlungsvorgang', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id
]);
$response = $userOrder->makePayment();
$this->info('makePayment response: ' . json_encode($response));
if (!isset($response['status'])) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Ungültige Zahlungsantwort', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id,
'response' => $response
]);
$this->error("Ungültige Zahlungsantwort für Abo {$userAbo->id}");
return $shoppingOrder;
}
if ($response['status'] === 'APPROVED') {
\Log::channel('abo_order')->info('UserMakeAboOrder: Zahlung erfolgreich', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id,
'response' => $response
]);
$this->info("Zahlung erfolgreich für Abo {$userAbo->id}");
$this->updateAbo($userAbo, $shoppingOrder, 1);
} elseif ($response['status'] === 'ERROR') {
\Log::channel('abo_order')->error('UserMakeAboOrder: Zahlungsfehler', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id,
'error' => $response
]);
$this->error("Zahlungsfehler für Abo {$userAbo->id}");
MyLog::writeLog(
'userabo',
'error',
'Error:3002 App\Console\Commands\UserMakeAboOrder::makeOrder / makePayment Error response',
$response
);
$this->updateAbo($userAbo, $shoppingOrder, 3);
$shoppingPayment = $userOrder->getShoppingPayment();
$data = [
'mode' => $shoppingPayment->mode,
'txaction' => 'error',
'send_link' => false,
'payment_error' => $response,
];
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, $data);
} else {
\Log::channel('abo_order')->warning('UserMakeAboOrder: Unbekannter Zahlungsstatus', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id,
'status' => $response['status']
]);
$this->warn("Unbekannter Zahlungsstatus für Abo {$userAbo->id}: {$response['status']}");
}
} catch (\Exception $e) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Ausnahme bei der Bestellungserstellung', [
'abo_id' => $userAbo->id,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error("Ausnahme bei Abo {$userAbo->id}: " . $e->getMessage());
}
return $shoppingOrder;
}
/**
* Aktualisiert das Abo nach einer Bestellung
*
* @param UserAbo $userAbo
* @param mixed $shoppingOrder
* @param int $status
* @return void
*/
private function updateAbo($userAbo, $shoppingOrder, $status = 1)
{
\Log::channel('abo_order')->info('UserMakeAboOrder: Aktualisiere Abo', [
'abo_id' => $userAbo->id,
'order_id' => $shoppingOrder->id,
'status' => $status
]);
$this->info("Aktualisiere Abo: {$userAbo->id} mit Status {$status}");
$updateData = [
'next_date' => AboHelper::setNextDate(now(), $userAbo->abo_interval),
'last_date' => now(),
];
if ($status !== 1) {
$updateData['status'] = $status;
}
try {
$userAbo->update($updateData);
UserAboOrder::create([
'user_abo_id' => $userAbo->id,
'shopping_order_id' => $shoppingOrder->id,
'status' => $status,
]);
\Log::channel('abo_order')->info('UserMakeAboOrder: Abo erfolgreich aktualisiert', [
'abo_id' => $userAbo->id,
'next_date' => $updateData['next_date']
]);
} catch (\Exception $e) {
\Log::channel('abo_order')->error('UserMakeAboOrder: Fehler beim Aktualisieren des Abos', [
'abo_id' => $userAbo->id,
'error' => $e->getMessage()
]);
$this->error("Fehler beim Aktualisieren des Abos {$userAbo->id}: " . $e->getMessage());
}
}
/**
* 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';
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace App\Console\Commands;
use App\User;
use Carbon\Carbon;
use App\Services\UserUtil;
use Illuminate\Console\Command;
class UserRestore extends Command
{
/**
* ln -sfv /usr/bin/php73 /usr/bin/php
* php artisan user:restore {user_id}
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:restore {user_id}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'User Restore active User where inactive for Business Structur and UserDetails';
private $timeStart;
private $user_id;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$this->info('RUN Command user:restore');
$this->timeStart = microtime(true);
$this->restoreInavtiveUsers();
return 0;
//\Log::info('Cron is running');
//return 0;
}
//gibt es gelöschte Berater mit Kunden und childs???
private function restoreInavtiveUsers(){
$this->info('START Command restoreInavtiveUsers');
$count = 0;
$this->user_id = $this->argument('user_id');
if(!$this->user_id){
$this->info('NO user_id as argument');
return;
}
$this->info('RUN Command restoreInavtiveUsers on user_id: '.$this->user_id);
$user = User::find($this->user_id);
if(!$user){
$this->info('restoreInavtiveUsers find no user by user_id:'.$this->user_id);
\Log::channel('cleanup')->error('restoreInavtiveUsers find no user by user_id:'.$this->user_id);
return 0;
}
$data = [
'user_id' => $user->id,
'email' => $user->email,
'm_account' => $user->account ? $user->account->m_account : '',
'm_first_name' => $user->account ? $user->account->m_first_name : '',
'm_last_name' => $user->account ? $user->account->m_last_name : '',
];
\Log::channel('cleanup')->info('reactiveUser: '.json_encode($data));
UserUtil::reactiveUser($user);
//childs wieder herstellen
UserUtil::resetChildsToSponsor($user->id);
$diff = microtime(true) - $this->timeStart;
$sec = intval($diff);
$micro = $diff - $sec;
$this->info('END Command deleteInavtiveUsers: '.$count. ' | Time: '.$sec. 'sec :' . round($micro * 1000, 4) . " ms");
}
}
//497
//489 -> de
//478 new