'', 2 => '', 3 => '']; // Wird in getFilterActive() übersetzt private $filter_next_level = [0 => '', 1 => '', 2 => '', 3 => '']; // Wird in getFilterNextLevel() übersetzt private $month; private $year; private $forceLiveCalculation; public function __construct() { $this->middleware('active.account'); } /** * Zeigt die Team-Übersicht mit optimierter TreeCalcBotOptimized-Datenverarbeitung * Lädt Team-Daten für DataTable-Anzeige */ public function show() { $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); $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); $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, 'performance' => [ 'execution_time' => $executionTime, 'memory_used' => $memoryUsed, 'user_id' => $user->id, '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), 'filter_active' => $this->getFilterActive(), 'filter_levels' => $this->getFilterLevels(), 'filter_next_level' => $this->getFilterNextLevel(), 'error' => __('team.error_loading_optimized_overview') . $e->getMessage(), 'performance' => [ 'execution_time' => $executionTime, 'memory_used' => 'N/A', 'version' => 'Fallback', 'calculation_type' => 'Error' ], 'optimized' => false, ]; return view('user.team.show', $data); } } public function structure() { $startTime = microtime(true); $startMemory = memory_get_usage(); $user = User::find(\Auth::user()->id); 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'), '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'), '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"); \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), 'TreeCalcBot' => $TreeCalcBot, 'performance' => [ 'execution_time' => $executionTime, 'memory_used' => $memoryUsed, '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), 'TreeCalcBot' => $TreeCalcBot, 'error' => 'Fehler aufgetreten, Standard-Version wird verwendet: ' . $e->getMessage(), 'performance' => [ 'execution_time' => $executionTime, 'memory_used' => 'N/A', 'user_count' => '-', 'version' => 'Fallback', 'calculation_type' => $forceLiveCalculation ? __('team.live_not_supported_fallback') : __('team.cache') ], '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')) { $user = User::find(454); } $this->setFilterVars(); $TreeCalcBot = new TreeCalcBot(session('team_user_filter_month'), session('team_user_filter_year'), 'member'); $TreeCalcBot->initStructureUser($user->id); //for testing //$TreeCalcBot->initUser(56); $data = [ 'filter_months' => HTMLHelper::getTransMonths(), 'filter_years' => HTMLHelper::getYearRange(2022), 'TreeCalcBot' => $TreeCalcBot, ]; return view('user.team.structure', $data); } /** * Optimierte DataTable für Team-Übersicht mit TreeCalcBotOptimized-Daten * Nutzt bereits berechnete Business-Daten für bessere Performance */ public function datatableOptimized() { try { $startTime = microtime(true); $this->setFilterVars(); $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); $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 $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 ''; }) ->addColumn('m_account', function ($teamUser) { return $teamUser->m_account; }) ->addColumn('email', function ($teamUser) { return e($teamUser->email); }) ->addColumn('first_name', function ($teamUser) { return e($teamUser->first_name); }) ->addColumn('last_name', function ($teamUser) { return e($teamUser->last_name); }) ->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); return TreeHelperOptimized::generateQualKPBadgeForUser($user, $this->month, $this->year); }) ->addColumn('sales_volume_KP_points', function ($teamUser) { return formatNumber($teamUser->sales_volume_points_KP_sum, 0); }) ->addColumn('sales_volume_total', function ($teamUser) { 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) ->first(); if ($userBusiness) { return NextLevelBadgeHelper::generateBadgeFromUserBusiness($userBusiness); } return NextLevelBadgeHelper::renderNoDataBadge(); }) ->addColumn('active_account', function ($teamUser) { return get_active_badge($teamUser->active_account); }) ->addColumn('payment_account_date', function ($teamUser) { return $teamUser->active_date ? formatDate($teamUser->active_date) : "-"; }) ->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(); } } /** * Standard DataTable für Team-Übersicht (Fallback-Version) */ public function datatable() { try { $user = User::find(\Auth::user()->id); $query = $this->initTeamSearch($user); return \DataTables::eloquent($query) ->addColumn('id', function (User $teamUser) { return ''; }) ->addColumn('m_account', function (User $teamUser) { return $teamUser->account ? e($teamUser->account->m_account) : ''; }) ->addColumn('user_level', function (User $teamUser) { return $teamUser->user_level ? e($teamUser->user_level->getLang('name')) : ''; }) ->addColumn('is_qual_kp', function (User $teamUser) { if (!$teamUser->user_level) { return '-'; } $qualKP = (int) $teamUser->user_level->qual_kp; $month = Request::get('team_user_filter_month') ?: session('team_user_filter_month'); $year = Request::get('team_user_filter_year') ?: session('team_user_filter_year'); $pointsSum = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum'); $isQual = $pointsSum >= $qualKP; $badgeClass = $isQual ? 'badge-outline-success' : 'badge-outline-warning-dark'; return ' KU ' . $qualKP . ''; }) ->addColumn('sales_volume_KP_points', function (User $teamUser) { $month = Request::get('team_user_filter_month') ?: session('team_user_filter_month'); $year = Request::get('team_user_filter_year') ?: session('team_user_filter_year'); $total = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_KP_sum'); $individual = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_KP_points'); $shop = (int) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_points_shop'); return '
' . $total . '
' . 'E: ' . $individual . ' | S: ' . $shop . ''; }) ->addColumn('sales_volume_total', function (User $teamUser) { $month = Request::get('team_user_filter_month') ?: session('team_user_filter_month'); $year = Request::get('team_user_filter_year') ?: session('team_user_filter_year'); $total = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total_sum'); $individual = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total'); $shop = (float) $teamUser->getUserSalesVolumeBy($month, $year, 'sales_volume_total_shop'); return '
' . formatNumber($total) . ' €
' . 'E: ' . formatNumber($individual) . ' | S: ' . formatNumber($shop) . ' €'; }) ->addColumn('email', function (User $teamUser) { return e($teamUser->email); }) ->addColumn('first_name', function (User $teamUser) { return $teamUser->account ? e($teamUser->account->first_name) : ''; }) ->addColumn('last_name', function (User $teamUser) { return $teamUser->account ? e($teamUser->account->last_name) : ''; }) ->addColumn('sponsor', function (User $teamUser) { if (!$teamUser->user_sponsor) { return '-'; } $sponsor = $teamUser->user_sponsor; $html = ''; if ($sponsor->account) { $html .= e($sponsor->account->first_name . ' ' . $sponsor->account->last_name); $html .= '
' . e($sponsor->email); $html .= ' | ' . e($sponsor->account->m_account); $html .= ''; } return $html; }) ->addColumn('active_account', function (User $teamUser) { return get_active_badge($teamUser->isActiveAccount()); }) ->addColumn('payment_account_date', function (User $teamUser) { return $teamUser->payment_account ? $teamUser->getPaymentAccountDateFormat(false) : "-"; }) ->addColumn('next_level_qualified', function (User $teamUser) { // 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) { $q->where('m_account', 'LIKE', '%' . $keyword . '%'); }); } }) ->filterColumn('first_name', function ($query, $keyword) { if ($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) { $q->where('last_name', 'LIKE', '%' . $keyword . '%'); }); } }) ->filterColumn('email', function ($query, $keyword) { if ($keyword != "") { $query->where('email', 'LIKE', '%' . $keyword . '%'); } }) ->orderColumn('id', 'users.id $1') ->orderColumn('m_account', 'user_accounts.m_account $1') ->orderColumn('first_name', 'user_accounts.first_name $1') ->orderColumn('last_name', 'user_accounts.last_name $1') ->orderColumn('email', 'users.email $1') ->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); } } /** * Zeigt den Marketingplan für User an * Übersichtliche Darstellung aller Karriere-Level mit wichtigen Informationen */ 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, 'currentLevel' => $currentLevel, 'performance' => [ '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(), 'currentUser' => null, 'currentLevel' => null ]); } } /** * Link für neue Mitglieder */ public function addMember() { $user = User::find(\Auth::user()->id); if ($user->isActiveShop() && $user->shop) { $shop_register_link = $user->shop->getSubdomain(false) . "/reg"; } else { $member_id = 'm' . ($user->id + config('mivita.add_number_id')); $shop_register_link = config('app.protocol') . config('app.domain') . config('app.tld_care') . '/reg/' . $member_id; } $data = [ 'shop_register_link' => $shop_register_link ]; return view('user.team.members', $data); } /** * Initialisiert die Team-Suche für den eingeloggten User */ private function initTeamSearch($currentUser) { $this->setFilterVars(); // Finde alle Team-Mitglieder des aktuellen Users (direkte und indirekte) $query = User::with(['account', 'user_level', 'user_sponsor.account']) ->select('users.*', 'user_accounts.m_account', 'user_accounts.first_name', 'user_accounts.last_name') ->leftJoin('user_accounts', 'users.id', '=', 'user_accounts.id') ->where('users.deleted_at', '=', null) ->where('users.id', '!=', 1) ->where('users.admin', '<', 4) ->where('users.m_level', '!=', null) ->where('users.payment_account', '!=', null); // 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()); } elseif ($activeFilter == 2) { $query->where('users.payment_account', '<', now()); } // activeFilter == 3 bedeutet alle (keine weitere Einschränkung) return $query; } public function points() { $this->setFilterVars(); $user = User::find(\Auth::user()->id); $userSalesVolume = $user->getUserSalesVolume(intval(session('team_user_points_filter_month')), intval(session('team_user_points_filter_year')), 'first'); $data = [ 'userSalesVolume' => $userSalesVolume, 'filter_months' => HTMLHelper::getTransMonths(), 'filter_years' => HTMLHelper::getYearRange(2022), ]; return view('user.team.points', $data); } public function export() { $user = User::find(\Auth::user()->id); if (!$user->isVIP()) { abort(404); } $ExportBot = new ExportBot('member'); $ExportBot->initStructureUser($user, 'list'); //tree or list $data = [ 'ExportBot' => $ExportBot, ]; return view('user.team.export', $data); } public function userTeamExport() { if (Request::get('action') === "export") { $user = User::find(\Auth::user()->id); $ExportBot = new ExportBot('member'); $ExportBot->initStructureUser($user, 'list'); //tree or list $columns = []; $filename = __('team.filename_export') . date('Y-m-d-H-i-s'); $headers = array( __('tables.line'), __('tables.level'), __('tables.email'), __('tables.firstname'), __('tables.lastname'), __('tables.address'), __('tables.addition'), __('tables.postcode'), __('tables.city'), __('tables.country'), __('tables.phone'), __('tables.mobil'), __('tables.birthday'), __('tables.partner_since'), __('tables.account'), __('tables.account_to'), __('tables.sponsor'), ); if (isset($ExportBot->user_list->childs)) { foreach ($ExportBot->user_list->childs as $child) { $columns[] = array( __('tables.line') => $child->line, __('tables.level') => $child->level_name, __('tables.email') => $child->email, __('tables.firstname') => $child->first_name, __('tables.lastname') => $child->last_name, __('tables.address') => $child->address, __('tables.addition') => $child->address_2, __('tables.postcode') => $child->zipcode, __('tables.city') => $child->city, __('tables.country') => $child->country_id, __('tables.phone') => $child->phone, __('tables.mobil') => $child->mobil, __('tables.birthday') => $child->birthday, __('tables.partner_since') => $child->partner_since, __('tables.account') => ($child->active_account == 1 ? __('yes') : __('no')), __('tables.account_to') => $child->payment_account_date, __('tables.sponsor') => $child->sponsor_name, ); } } return Excel::download(new UserTeamExport($columns, $headers), $filename . '.xls'); } } private function setFilterVars() { if (!session('team_user_filter_month')) { session(['team_user_filter_month' => intval(date('m'))]); } if (!session('team_user_filter_year')) { session(['team_user_filter_year' => intval(date('Y'))]); } if (!session('team_user_points_filter_month')) { session(['team_user_points_filter_month' => intval(date('m'))]); } if (!session('team_user_points_filter_year')) { session(['team_user_points_filter_year' => intval(date('Y'))]); } if (!session('team_user_filter_active')) { session(['team_user_filter_active' => 1]); } if (!session('team_user_filter_level')) { session(['team_user_filter_level' => 0]); } if (!session('team_user_filter_next_level')) { session(['team_user_filter_next_level' => 0]); } if (Request::get('team_user_filter_month')) { session(['team_user_filter_month' => Request::get('team_user_filter_month')]); } if (Request::get('team_user_filter_year')) { session(['team_user_filter_year' => Request::get('team_user_filter_year')]); } if (Request::get('team_user_points_filter_month')) { session(['team_user_points_filter_month' => Request::get('team_user_points_filter_month')]); } if (Request::get('team_user_points_filter_year')) { session(['team_user_points_filter_year' => Request::get('team_user_points_filter_year')]); } if (Request::get('team_user_filter_active')) { session(['team_user_filter_active' => Request::get('team_user_filter_active')]); } if (Request::get('team_user_filter_level')) { session(['team_user_filter_level' => Request::get('team_user_filter_level')]); } 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 { session(['team_user_filter_next_level' => 0]); } } private function initSearchPoints() { $this->setFilterVars(); $user_id = \Auth::user()->id; $query = UserSalesVolume::with('user', 'user.account')->with('shopping_order')->select('user_sales_volumes.*') ->where('user_sales_volumes.user_id', '=', $user_id) ->where('user_sales_volumes.month', '=', Request::get('team_user_points_filter_month')) ->where('user_sales_volumes.year', '=', Request::get('team_user_points_filter_year')); return $query; } public function datatablePoints() { $query = $this->initSearchPoints(); return \DataTables::eloquent($query) ->addColumn('order', function (UserSalesVolume $UserSalesVolume) { if ($UserSalesVolume->shopping_order) { if ($UserSalesVolume->status === 1 && $UserSalesVolume->shopping_order->auth_user_id === $UserSalesVolume->user_id) { return '' . $UserSalesVolume->shopping_order->id . ''; } if (($UserSalesVolume->status === 2 || $UserSalesVolume->status === 3) && $UserSalesVolume->shopping_order->member_id === $UserSalesVolume->user_id) { return '' . $UserSalesVolume->shopping_order->id . ''; } } return ''; }) ->addColumn('total_net', function (UserSalesVolume $UserSalesVolume) { return formatNumber($UserSalesVolume->total_net) . ' €'; }) ->addColumn('status_turnover', function (UserSalesVolume $UserSalesVolume) { return '' . $UserSalesVolume->getStatusTurnoverType() . ''; }) ->addColumn('status', function (UserSalesVolume $UserSalesVolume) { return '' . $UserSalesVolume->getStatusType() . ''; }) ->addColumn('message', function (UserSalesVolume $UserSalesVolume) { return '' . $UserSalesVolume->message . ''; }) ->addColumn('info', function (UserSalesVolume $UserSalesVolume) { return '' . $UserSalesVolume->info . ''; }) ->orderColumn('id', 'id $1') ->orderColumn('order', 'order $1') ->orderColumn('status', 'status $1') ->orderColumn('message', 'message $1') ->orderColumn('info', 'info $1') ->rawColumns(['id', 'order', 'status_turnover', 'status', 'message', 'info', 'total_net']) ->make(true); } public function load() { $user = User::find(\Auth::user()->id); $userSalesVolume = $user->getUserSalesVolume(intval(session('team_user_points_filter_month')), intval(session('team_user_points_filter_year')), 'first'); $data = [ 'userSalesVolume' => $userSalesVolume, ]; $html = view('user.team._points_sum', $data)->render(); return response()->json(['response' => true, 'data' => $data, 'html' => $html]); } /** * Formatiert Bytes in lesbare Einheiten (aus BusinessControllerOptimized übernommen) */ 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]; } /** * 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 ü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 */ public function getTeamUsersFromStructure(TreeCalcBotOptimized $treeCalcBot): array { $allUsers = []; $processedIds = []; // Debug: Prüfe TreeCalcBot-Inhalt $businessUsers = $treeCalcBot->getItems(); \Log::info("TeamController: TreeCalcBot root items count: " . count($businessUsers)); // Sammle alle Root-User UND deren verschachtelte businessUserItems foreach ($businessUsers as $businessUser) { // 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 ?? []), ]); // 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, kann bei usern eigenlich nicht vorkommen, da sie immer teil des baums sind if ($treeCalcBot->isParentless()) { $parentless = $treeCalcBot->__get('parentless'); if (is_array($parentless)) { foreach ($parentless as $businessUser) { if ($businessUser) { // 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 */ private function applyTeamFiltersToBusinessUsers($businessUsers): array { $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]; \Log::debug("TeamController: First BusinessUser properties", [ 'user_id' => $firstUser->user_id ?? 'not set', 'active_account' => $firstUser->active_account ?? 'not set', 'm_level_id' => $firstUser->m_level_id ?? 'not set', 'next_qual_user_level' => isset($firstUser->next_qual_user_level) ? 'set' : 'not set', 'next_can_user_level' => isset($firstUser->next_can_user_level) ? 'set' : 'not set', ]); } $filtered = array_filter($businessUsers, function ($businessUser) use ($activeFilter, $levelFilter, $nextLevelFilter) { // Active Filter anwenden if ($activeFilter == 1) { // Nur aktive if (!$businessUser->active_account) { return false; } } elseif ($activeFilter == 2) { // Nur inaktive if ($businessUser->active_account) { return false; } } // 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) { return false; } break; case 2: // In Arbeit (gelb) - hat next_can_user_level aber kein next_qual_user_level if ($hasNextQual || !$hasNextCan) { return false; } break; case 3: // Kein Level (rot) - hat weder next_qual noch next_can if ($hasNextQual || $hasNextCan) { return false; } break; } } return true; // Alle Filter bestanden }); // Array-Indizes neu setzen für korrekte DataTable-Verarbeitung return array_values($filtered); } /** * NEUE OPTIMIERTE Methode: Sammelt ALLE BusinessUserItems in einer flachen Liste * Perfekt für DataTable-Verarbeitung - keine verschachtelte Struktur */ private function collectAllBusinessUserItemsFlat(array $businessUserItems, &$allUsers, &$processedIds, $depth = 1): void { // 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; } // 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; } }