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

475
app/Http/Controllers/User/TeamController.php Executable file → Normal file
View file

@ -45,7 +45,7 @@ class TeamController extends Controller
$this->middleware('active.account');
}
/**
* Zeigt die Team-Übersicht mit optimierter TreeCalcBotOptimized-Datenverarbeitung
@ -55,60 +55,58 @@ class TeamController extends Controller
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
try {
$this->setFilterVars();
$user = User::find(\Auth::user()->id);
$this->month = session('team_user_filter_month');
$this->year = session('team_user_filter_year');
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
\Log::info("TeamController: Building optimized team overview for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation ? " with forced live calculation" : ""));
$forceLiveCalculation = false;
\Log::info("TeamController: Building optimized team overview for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation === true ? " with forced live calculation" : "not live calculation"));
// Verwende TreeCalcBotOptimized für bessere Performance
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'member', $forceLiveCalculation);
$TreeCalcBot->initStructureUser($user->id);
//$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'member', $forceLiveCalculation);
//$TreeCalcBot->initStructureUser($user->id);
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
$calculationType = $forceLiveCalculation ? " (LIVE)" : " (CACHE)";
\Log::info("TeamController: Optimized team overview built in {$executionTime}ms, Memory: {$memoryUsed}{$calculationType}");
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'filter_active' => $this->getFilterActive(),
'filter_levels' => $this->getFilterLevels(),
'filter_next_level' => $this->getFilterNextLevel(),
'TreeCalcBot' => $TreeCalcBot,
//'TreeCalcBot' => $TreeCalcBot,
'performance' => [
'execution_time' => $executionTime,
'memory_used' => $memoryUsed,
'user_id' => $user->id,
'user_count' => $TreeCalcBot->getTotalUserCount(),
'user_count' => 0, //$TreeCalcBot->getTotalUserCount(),
'version' => 'Optimized',
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
],
'optimized' => true,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.show', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error in optimized show for user {$user->id}: " . $e->getMessage());
// Fallback mit minimalen Daten
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
@ -124,7 +122,7 @@ class TeamController extends Controller
],
'optimized' => false,
];
return view('user.team.show', $data);
}
}
@ -133,55 +131,54 @@ class TeamController extends Controller
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
$user = User::find(\Auth::user()->id);
if(config('app.debug')){
if (config('app.debug')) {
$user = User::find(454);
}
$this->setFilterVars();
// Prüfe ob optimierte Version explizit angefordert wird
$useOptimized = Request::get('use_optimized', true);
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
try {
if ($useOptimized) {
// Verwende User-spezifische optimierte Version
$TreeCalcBot = new TreeCalcBotOptimized(
session('team_user_filter_month'),
session('team_user_filter_year'),
session('team_user_filter_month'),
session('team_user_filter_year'),
'member',
$forceLiveCalculation
);
$TreeCalcBot->initStructureUser($user->id, $forceLiveCalculation);
$optimizedUsed = true;
} else {
// Standard TreeCalcBot mit Performance-Monitoring
$TreeCalcBot = new TreeCalcBot(
session('team_user_filter_month'),
session('team_user_filter_year'),
session('team_user_filter_month'),
session('team_user_filter_year'),
'member'
);
// Standard TreeCalcBot unterstützt forceLiveCalculation nicht
$TreeCalcBot->initStructureUser($user->id);
$optimizedUsed = false;
}
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsed = $this->formatBytes($endMemory - $startMemory);
$versionInfo = ($optimizedUsed ? "OPTIMIZED" : "STANDARD") .
($forceLiveCalculation ? " + LIVE" : " + CACHE");
$versionInfo = ($optimizedUsed ? "OPTIMIZED" : "STANDARD") .
($forceLiveCalculation ? " + LIVE" : " + CACHE");
\Log::info("TeamController: Structure built for user {$user->id} in {$executionTime}ms, Memory: {$memoryUsed} ({$versionInfo})");
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
@ -189,28 +186,27 @@ class TeamController extends Controller
'performance' => [
'execution_time' => $executionTime,
'memory_used' => $memoryUsed,
'user_count' => $optimizedUsed && method_exists($TreeCalcBot, 'getTotalUserCount')
? $TreeCalcBot->getTotalUserCount()
: '-',
'user_count' => $optimizedUsed && method_exists($TreeCalcBot, 'getTotalUserCount')
? $TreeCalcBot->getTotalUserCount()
: '-',
'version' => $optimizedUsed ? 'Optimized' : 'Standard',
'calculation_type' => $forceLiveCalculation ? 'Live' : 'Cache'
],
'optimized' => $optimizedUsed,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.structure', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error in structure for user {$user->id}: " . $e->getMessage());
// Fallback zur Standard-Implementierung
$TreeCalcBot = new TreeCalcBot(session('team_user_filter_month'), session('team_user_filter_year'), 'member');
$TreeCalcBot->initStructureUser($user->id);
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
@ -226,14 +222,15 @@ class TeamController extends Controller
'optimized' => false,
'forceLiveCalculation' => $forceLiveCalculation,
];
return view('user.team.structure', $data);
}
}
public function structureOld()
{
abort(403, 'This page is removed');
$user = User::find(\Auth::user()->id);
if(config('app.debug')){
if (config('app.debug')) {
$user = User::find(454);
}
$this->setFilterVars();
@ -263,27 +260,29 @@ class TeamController extends Controller
$user = User::find(\Auth::user()->id);
$this->month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$this->year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
// Prüfe ob Live-Berechnung erzwungen werden soll
$forceLiveCalculation = Request::get('force_live_calculation', false) || Request::get('live', false);
\Log::info("TeamController: Building optimized datatable for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation == true ? " with forced live calculation" : ""));
$forceLiveCalculation = false;
\Log::info("TeamController: Building optimized datatable for user {$user->id} ({$this->month}/{$this->year})" .
($forceLiveCalculation == true ? " with forced live calculation" : ""));
// Lade TreeCalcBotOptimized-Daten
$TreeCalcBot = new TreeCalcBotOptimized($this->month, $this->year, 'member', $forceLiveCalculation);
$TreeCalcBot->initStructureUser($user->id, $forceLiveCalculation);
// Extrahiere alle User aus der Struktur
$teamUsers = collect($this->getTeamUsersFromStructure($TreeCalcBot));
// \Log::info("TeamController: TeamUsers: " . $teamUsers->count());
$teamUsersRaw = $this->getTeamUsersFromStructure($TreeCalcBot);
// KRITISCH: Bereinige die Objekte für DataTables (entferne zirkuläre Referenzen)
$teamUsers = collect($this->cleanBusinessUserItemsForDataTable($teamUsersRaw));
\Log::info("TeamController: TeamUsers cleaned for DataTable: " . $teamUsers->count());
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
$this->forceLiveCalculation = $forceLiveCalculation;
\Log::info("TeamController: Optimized datatable data prepared in {$executionTime}ms for " . $teamUsers->count() . " users");
return \DataTables::of($teamUsers)
->addColumn('id', function ($teamUser) {
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
@ -310,7 +309,6 @@ class TeamController extends Controller
})
->addColumn('user_level', function ($teamUser) {
return $teamUser->user_level_name ? TranslationHelper::transUserLevelName($teamUser->user_level_name) : '';
})
->addColumn('is_qual_kp', function ($teamUser) {
$user = User::find($teamUser->user_id);
@ -320,12 +318,11 @@ class TeamController extends Controller
return formatNumber($teamUser->sales_volume_points_KP_sum, 0);
})
->addColumn('sales_volume_total', function ($teamUser) {
return formatNumber($teamUser->payline_points_qual_kp, 0);
return formatNumber($teamUser->payline_points_qual_kp, 0);
})
->addColumn('next_level_qualified', function ($teamUser) {
$userBusiness = UserBusiness::where('user_id', $teamUser->user_id)
->where('month', $this->month)
->where('year', $this->year)
@ -334,7 +331,6 @@ class TeamController extends Controller
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
}
return NextLevelBadgeHelper::renderNoDataBadge();
})
->addColumn('active_account', function ($teamUser) {
return get_active_badge($teamUser->active_account);
@ -344,10 +340,9 @@ class TeamController extends Controller
})
->rawColumns(['id', 'next_level_qualified', 'active_account', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total'])
->make(true);
} catch (\Exception $e) {
\Log::error("TeamController: Error in optimized datatable: " . $e->getMessage());
// Fallback zur Standard-DataTable
return $this->datatable();
}
@ -361,7 +356,7 @@ class TeamController extends Controller
try {
$user = User::find(\Auth::user()->id);
$query = $this->initTeamSearch($user);
return \DataTables::eloquent($query)
->addColumn('id', function (User $teamUser) {
return '<button type="button" class="btn icon-btn btn-xs btn-secondary" data-toggle="modal" data-target="#modals-load-content"
@ -397,7 +392,7 @@ class TeamController extends Controller
$individual = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_KP_points');
$shop = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_shop');
return '<div class="no-line-break">' . $total . '</div>' .
'<span class="small no-line-break">E: ' . $individual . ' | S: ' . $shop . '</span>';
'<span class="small no-line-break">E: ' . $individual . ' | S: ' . $shop . '</span>';
})
->addColumn('sales_volume_total', function (User $teamUser) {
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
@ -406,7 +401,7 @@ class TeamController extends Controller
$individual = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total');
$shop = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total_shop');
return '<div class="no-line-break">' . formatNumber($total) . ' &euro;</div>' .
'<span class="small no-line-break">E: ' . formatNumber($individual) . ' | S: ' . formatNumber($shop) . ' &euro;</span>';
'<span class="small no-line-break">E: ' . formatNumber($individual) . ' | S: ' . formatNumber($shop) . ' &euro;</span>';
})
->addColumn('email', function (User $teamUser) {
return e($teamUser->email);
@ -441,35 +436,35 @@ class TeamController extends Controller
// Verwende bereits berechnete UserBusiness-Daten für bessere Performance
$month = Request::get('team_user_filter_month') ?: session('team_user_filter_month');
$year = Request::get('team_user_filter_year') ?: session('team_user_filter_year');
$userBusiness = UserBusiness::where('user_id', $teamUser->id)
->where('month', $month)
->where('year', $year)
->first();
if ($userBusiness) {
return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness);
}
return NextLevelBadgeHelper::renderNoDataBadge();
})
->filterColumn('m_account', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$query->whereHas('account', function ($q) use ($keyword) {
$q->where('m_account', 'LIKE', '%' . $keyword . '%');
});
}
})
->filterColumn('first_name', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$query->whereHas('account', function ($q) use ($keyword) {
$q->where('first_name', 'LIKE', '%' . $keyword . '%');
});
}
})
->filterColumn('last_name', function ($query, $keyword) {
if ($keyword != "") {
$query->whereHas('account', function($q) use ($keyword) {
$query->whereHas('account', function ($q) use ($keyword) {
$q->where('last_name', 'LIKE', '%' . $keyword . '%');
});
}
@ -487,10 +482,9 @@ class TeamController extends Controller
->orderColumn('active_account', 'users.payment_account $1')
->rawColumns(['id', 'is_qual_kp', 'sales_volume_KP_points', 'sales_volume_total', 'sponsor', 'active_account', 'next_level_qualified'])
->make(true);
} catch (\Exception $e) {
\Log::error("TeamController: Error in userDatatable: " . $e->getMessage());
return response()->json([
'error' => 'Team-Datatable konnte nicht geladen werden: ' . $e->getMessage()
], 500);
@ -504,21 +498,21 @@ class TeamController extends Controller
public function marketingplan()
{
$startTime = microtime(true);
try {
$user = User::find(\Auth::user()->id);
$currentLevel = $user->user_level;
// Lade alle aktiven User Level, sortiert nach Position
$userLevels = \App\Models\UserLevel::where('active', true)
->orderBy('pos', 'asc')
->get();
$endTime = microtime(true);
$executionTime = round(($endTime - $startTime) * 1000, 2);
\Log::info("TeamController: Marketingplan loaded for user {$user->id} in {$executionTime}ms");
$data = [
'userLevels' => $userLevels,
'currentUser' => $user,
@ -527,12 +521,11 @@ class TeamController extends Controller
'execution_time' => $executionTime
]
];
return view('user.team.marketingplan', $data);
} catch (\Exception $e) {
\Log::error("TeamController: Error loading marketingplan: " . $e->getMessage());
return view('user.team.marketingplan', [
'error' => __('marketingplan.loading_error') . ' ' . $e->getMessage(),
'userLevels' => collect(),
@ -580,7 +573,7 @@ class TeamController extends Controller
// Filtere Team-Mitglieder basierend auf Sponsor-Hierarchie
// TODO: Hier müsste die Logik implementiert werden, um nur Team-Mitglieder des aktuellen Users zu finden
// Für jetzt zeigen wir alle aktiven User (kann später spezifiziert werden)
$activeFilter = Request::get('team_user_filter_active') ?: session('team_user_filter_active', 1);
if ($activeFilter == 1) {
$query->where('users.payment_account', '>=', now());
@ -591,7 +584,7 @@ class TeamController extends Controller
return $query;
}
public function points()
{
$this->setFilterVars();
@ -718,12 +711,12 @@ class TeamController extends Controller
}
if (Request::get('team_user_filter_level')) {
session(['team_user_filter_level' => Request::get('team_user_filter_level')]);
}else{
} else {
session(['team_user_filter_level' => 0]);
}
if (Request::get('team_user_filter_next_level')) {
session(['team_user_filter_next_level' => Request::get('team_user_filter_next_level')]);
}else{
} else {
session(['team_user_filter_next_level' => 0]);
}
}
@ -812,96 +805,120 @@ class TeamController extends Controller
return round($bytes, $precision) . ' ' . $units[$i];
}
/**
* Holt verfügbare User Level für Filter
*/
private function getFilterLevels(): array
{
$levels = [0 => 'Alle Level'];
$userLevels = \App\Models\UserLevel::orderBy('pos')->get(['id', 'name']);
foreach ($userLevels as $level) {
$levels[$level->id] = $level->name;
}
return $levels;
/**
* Holt verfügbare User Level für Filter
*/
private function getFilterLevels(): array
{
$levels = [0 => 'Alle Level'];
$userLevels = \App\Models\UserLevel::orderBy('pos')->get(['id', 'name']);
foreach ($userLevels as $level) {
$levels[$level->id] = $level->name;
}
/**
* Holt übersetzte Filter für Aktiv-Status
*/
private function getFilterActive(): array
{
return [
1 => __('team.filter_active'),
2 => __('team.filter_not_active'),
3 => __('team.filter_all')
];
}
return $levels;
}
/**
* Holt übersetzte Filter für Next Level Status
*/
private function getFilterNextLevel(): array
{
return [
0 => __('team.all_status'),
1 => __('team.qualified_green'),
2 => __('team.in_progress_yellow'),
3 => __('team.no_level_red')
];
}
/**
* Holt übersetzte Filter für Aktiv-Status
*/
private function getFilterActive(): array
{
return [
1 => __('team.filter_active'),
2 => __('team.filter_not_active'),
3 => __('team.filter_all')
];
}
/**
* Holt übersetzte Filter für Next Level Status
*/
private function getFilterNextLevel(): array
{
return [
0 => __('team.all_status'),
1 => __('team.qualified_green'),
2 => __('team.in_progress_yellow'),
3 => __('team.no_level_red')
];
}
// Performance-optimierte Badge-Generierung wurde in NextLevelBadgeHelper ausgelagert
// Alte performance-lastige Methoden wurden entfernt um die Datatable-Performance zu verbessern
/**
* Extrahiert alle User aus TreeCalcBotOptimized-Struktur für DataTable-Anzeige
* Sammelt rekursiv alle User aus der Struktur und macht sie als flache Liste verfügbar
* Sammelt rekursiv alle User aus der Struktur und macht sie als FLACHE Liste verfügbar
*/
public function getTeamUsersFromStructure(TreeCalcBotOptimized $treeCalcBot): array
{
$allUsers = [];
$deep = 0;
$processedIds = [];
// Debug: Prüfe TreeCalcBot-Inhalt
$businessUsers = $treeCalcBot->getItems();
\Log::info("TeamController: TreeCalcBot items count: " . count($businessUsers));
// Sammle alle Root-User
\Log::info("TeamController: TreeCalcBot root items count: " . count($businessUsers));
// Sammle alle Root-User UND deren verschachtelte businessUserItems
foreach ($businessUsers as $businessUser) {
\Log::debug("TeamController: Processing businessUser", [
'user_id' => ($businessUser->user_id),
// WICHTIG: user_id korrekt über b_user abrufen (Magic Method Problem mit isset())
$userId = $businessUser->user_id; // Über __get() Method
\Log::debug("TeamController: Processing root businessUser", [
'user_id' => $userId,
'businessUserItems_count' => count($businessUser->businessUserItems ?? []),
]);
$businessUser->deep = $deep;
$allUsers[] = $businessUser;
$this->collectUserIdsFromBusinessUser($businessUser, $allUsers, $deep+1, false);
// WICHTIG: Root-User selbst hinzufügen (korrigierte user_id Prüfung)
//nur User können auch children haben - businessUserItems
if ($userId && !isset($processedIds[$userId])) {
$processedIds[$userId] = true;
$businessUser->deep = 0;
$allUsers[] = $businessUser;
$this->collectAllBusinessUserItemsFlat($businessUser->businessUserItems ?? [], $allUsers, $processedIds, 1);
\Log::debug("TeamController: Root-User hinzugefügt: {$userId}");
}
}
// Sammle parentless User
// Sammle parentless User, kann bei usern eigenlich nicht vorkommen, da sie immer teil des baums sind
if ($treeCalcBot->isParentless()) {
$parentless = $treeCalcBot->__get('parentless');
//\Log::info("TeamController: Found " . count($parentless) . " parentless users");
if (is_array($parentless)) {
foreach ($parentless as $businessUser) {
if ($businessUser) {
$businessUser->deep = 0;
$allUsers[] = $businessUser;
// Sammle rekursiv alle Unter-User
$this->collectUserIdsFromBusinessUser($businessUser, $allUsers, 0, true);
// WICHTIG: user_id korrekt über b_user abrufen (Magic Method Problem mit isset())
$userId = $businessUser->user_id; // Über __get() Method
if ($userId) {
// Prüfe ob dieser parentless User bereits gesammelt wurde
if (!isset($processedIds[$userId])) {
$processedIds[$userId] = true;
$businessUser->deep = 0;
$allUsers[] = $businessUser;
\Log::debug("TeamController: Parentless-User hinzugefügt: {$userId}");
// Sammle ALLE verschachtelten businessUserItems rekursiv
$this->collectAllBusinessUserItemsFlat($businessUser->businessUserItems ?? [], $allUsers, $processedIds, 1);
} else {
\Log::debug("TeamController: Parentless-User übersprungen: {$userId} (bereits verarbeitet)");
}
} else {
\Log::warning("TeamController: Parentless BusinessUser ohne user_id übersprungen");
}
}
}
}
}
\Log::info("TeamController: AllUsers before filtering: " . count($allUsers));
// Filter anwenden
$filteredUsers = $this->applyTeamFiltersToBusinessUsers($allUsers);
\Log::info("TeamController: AllUsers after filtering: " . count($filteredUsers));
return $filteredUsers;
}
/**
* Wendet Team-Filter auf BusinessUser-Objekte an
*/
@ -910,9 +927,9 @@ class TeamController extends Controller
$activeFilter = Request::get('team_user_filter_active') ?: session('team_user_filter_active', 1);
$levelFilter = Request::get('team_user_filter_level') ?: session('team_user_filter_level', 0);
$nextLevelFilter = Request::get('team_user_filter_next_level') ?: session('team_user_filter_next_level', 0);
\Log::info("TeamController: Applying filters - Active: {$activeFilter}, Level: {$levelFilter}, NextLevel: {$nextLevelFilter}");
// Debug: Zeige verfügbare Eigenschaften des ersten BusinessUsers
if (!empty($businessUsers)) {
$firstUser = $businessUsers[0];
@ -924,8 +941,8 @@ class TeamController extends Controller
'next_can_user_level' => isset($firstUser->next_can_user_level) ? 'set' : 'not set',
]);
}
$filtered = array_filter($businessUsers, function($businessUser) use ($activeFilter, $levelFilter, $nextLevelFilter) {
$filtered = array_filter($businessUsers, function ($businessUser) use ($activeFilter, $levelFilter, $nextLevelFilter) {
// Active Filter anwenden
if ($activeFilter == 1) { // Nur aktive
if (!$businessUser->active_account) {
@ -937,19 +954,19 @@ class TeamController extends Controller
}
}
// activeFilter == 3 bedeutet alle (keine Einschränkung)
// Level Filter anwenden
if ($levelFilter && $levelFilter != 0) {
if ($businessUser->m_level_id != $levelFilter) {
return false;
}
}
// Next Level Filter anwenden
if ($nextLevelFilter && $nextLevelFilter != 0) {
$hasNextQual = ($businessUser->next_qual_user_level) && $businessUser->next_qual_user_level !== '[]';
$hasNextCan = ($businessUser->next_can_user_level) && $businessUser->next_can_user_level !== '[]';
switch ($nextLevelFilter) {
case 1: // Qualifiziert (grün) - hat next_qual_user_level
if (!$hasNextQual) {
@ -968,35 +985,153 @@ class TeamController extends Controller
break;
}
}
return true; // Alle Filter bestanden
});
// Array-Indizes neu setzen für korrekte DataTable-Verarbeitung
return array_values($filtered);
}
/**
* Hilfsmethode zum rekursiven Sammeln von User-IDs aus BusinessUser-Struktur
* NEUE OPTIMIERTE Methode: Sammelt ALLE BusinessUserItems in einer flachen Liste
* Perfekt für DataTable-Verarbeitung - keine verschachtelte Struktur
*/
private function collectUserIdsFromBusinessUser($businessUser, &$allUsers, $deep, $parentless): void
private function collectAllBusinessUserItemsFlat(array $businessUserItems, &$allUsers, &$processedIds, $depth = 1): void
{
if (isset($businessUser->businessUserItems) && is_array($businessUser->businessUserItems)) {
\Log::debug("TeamController: Collecting from businessUser with " . count($businessUser->businessUserItems) . " sub-items");
foreach ($businessUser->businessUserItems as $subBusinessUser) {
if ($subBusinessUser) {
$subBusinessUser->deep = $deep;
$allUsers[] = $subBusinessUser;
if($subBusinessUser->user_id){
\Log::debug("TeamController: Collected user ID: " . $subBusinessUser->user_id);
// Schutz vor zu tiefer Rekursion (maximale Tiefe: 20 Levels)
$maxDepth = 20;
if ($depth > $maxDepth) {
\Log::warning("TeamController: Maximale Sammlungstiefe ({$maxDepth}) erreicht bei Tiefe {$depth}");
return;
}
foreach ($businessUserItems as $businessUserItem) {
if ($businessUserItem) {
// WICHTIG: user_id korrekt über b_user abrufen (Magic Method Problem mit isset())
$userId = $businessUserItem->user_id; // Über __get() Method
if ($userId) {
// KRITISCHER SCHUTZ: Prüfe ob User bereits gesammelt wurde
if (isset($processedIds[$userId])) {
\Log::debug("TeamController: Überspringe bereits gesammelten User {$userId} (Duplikat verhindert)");
continue;
}
// Rekursiver Aufruf für weitere Unter-User
$newDeep = $parentless ? 0 : $deep+1;
$this->collectUserIdsFromBusinessUser($subBusinessUser, $allUsers, $newDeep, $parentless);
// User zu flacher Liste hinzufügen
$processedIds[$userId] = true;
$businessUserItem->deep = $depth;
$allUsers[] = $businessUserItem;
\Log::debug("TeamController: Flach gesammelt - User ID: {$userId} at depth {$depth}");
// Rekursiv ALLE verschachtelten businessUserItems sammeln
if (isset($businessUserItem->businessUserItems) && is_array($businessUserItem->businessUserItems) && !empty($businessUserItem->businessUserItems)) {
\Log::debug("TeamController: Sammle " . count($businessUserItem->businessUserItems) . " verschachtelte Items von User {$userId}");
$this->collectAllBusinessUserItemsFlat($businessUserItem->businessUserItems, $allUsers, $processedIds, $depth + 1);
}
} else {
\Log::warning("TeamController: BusinessUserItem ohne user_id bei Tiefe {$depth} übersprungen");
}
}
}
}
/**
* DEPRECATED: Alte Methode zum rekursiven Sammeln von User-IDs aus BusinessUser-Struktur
* Wird durch collectAllBusinessUserItemsFlat() ersetzt
*/
private function collectUserIdsFromBusinessUser($businessUser, &$allUsers, $deep, $parentless, &$processedIds = []): void
{
// Schutz vor zu tiefer Rekursion (maximale Tiefe: 20 Levels)
$maxDepth = 20;
if ($deep > $maxDepth) {
\Log::warning("TeamController: Maximale Sammlungstiefe ({$maxDepth}) erreicht");
return;
}
if (isset($businessUser->businessUserItems) && is_array($businessUser->businessUserItems)) {
\Log::debug("TeamController: Collecting from businessUser with " . count($businessUser->businessUserItems) . " sub-items at depth {$deep}");
foreach ($businessUser->businessUserItems as $subBusinessUser) {
if ($subBusinessUser) {
// WICHTIG: user_id korrekt über b_user abrufen (Magic Method Problem mit isset())
$userId = $subBusinessUser->user_id; // Über __get() Method
if ($userId) {
// KRITISCHER BUGFIX: Prüfe ob User bereits gesammelt wurde
if (isset($processedIds[$userId])) {
\Log::debug("TeamController: Überspringe bereits gesammelten User {$userId} (zirkuläre Referenz verhindert)");
continue;
}
$processedIds[$userId] = true;
$subBusinessUser->deep = $deep;
$allUsers[] = $subBusinessUser;
\Log::debug("TeamController: Collected user ID: {$userId} at depth {$deep}");
// Rekursiver Aufruf für weitere Unter-User mit Schutz vor Zyklen
$newDeep = $parentless ? 0 : $deep + 1;
$this->collectUserIdsFromBusinessUser($subBusinessUser, $allUsers, $newDeep, $parentless, $processedIds);
} else {
\Log::warning("TeamController: SubBusinessUser ohne user_id bei Tiefe {$deep} übersprungen");
}
}
}
}
}
/**
* KRITISCHE METHODE: Bereinigt BusinessUserItemOptimized Objekte für DataTables
* Entfernt zirkuläre Referenzen und extrahiert nur nötige Properties
*/
private function cleanBusinessUserItemsForDataTable(array $businessUserItems): array
{
$cleanedUsers = [];
foreach ($businessUserItems as $businessUserItem) {
if (!$businessUserItem) {
continue;
}
try {
// Extrahiere nur die Properties, die für DataTable benötigt werden
$cleanedUser = new \stdClass();
// Basis Properties (direkt über Magic Method __get)
$cleanedUser->user_id = $businessUserItem->user_id;
$cleanedUser->m_account = $businessUserItem->m_account;
$cleanedUser->email = $businessUserItem->email;
$cleanedUser->first_name = $businessUserItem->first_name;
$cleanedUser->last_name = $businessUserItem->last_name;
$cleanedUser->user_level_name = $businessUserItem->user_level_name;
$cleanedUser->active_account = $businessUserItem->active_account;
$cleanedUser->active_date = $businessUserItem->active_date;
// Sales Volume Properties
$cleanedUser->sales_volume_points_KP_sum = $businessUserItem->sales_volume_points_KP_sum ?? 0;
$cleanedUser->payline_points_qual_kp = $businessUserItem->payline_points_qual_kp ?? 0;
// Depth für Debug/Sortierung (falls gesetzt)
$cleanedUser->deep = $businessUserItem->deep ?? 0;
// Level-Informationen für Filter
$cleanedUser->m_level_id = $businessUserItem->m_level_id;
$cleanedUser->next_qual_user_level = $businessUserItem->next_qual_user_level;
$cleanedUser->next_can_user_level = $businessUserItem->next_can_user_level;
$cleanedUsers[] = $cleanedUser;
\Log::debug("TeamController: Cleaned user {$cleanedUser->user_id} for DataTable");
} catch (\Exception $e) {
\Log::error("TeamController: Error cleaning BusinessUserItem for DataTable: " . $e->getMessage());
// Skip diesen User, statt alles abzubrechen
continue;
}
}
\Log::info("TeamController: Cleaned " . count($cleanedUsers) . " users for DataTable (from " . count($businessUserItems) . " raw items)");
return $cleanedUsers;
}
}