23-01-2026

This commit is contained in:
Kevin Adametz 2026-01-23 17:35:23 +01:00
parent a939cd51ef
commit a8b395e20d
248 changed files with 29342 additions and 4805 deletions

View file

@ -0,0 +1,88 @@
@if(isset($dashboardNews) && $dashboardNews)
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4 border-primary">
<h5 class="card-header with-elements bg-primary text-white">
<div class="card-header-title">
<i class="ion ion-md-megaphone mr-2"></i>
{{__('home.news_updates') }}
</div>
<div class="card-header-elements ml-auto">
<span class="badge badge-light">{{ $dashboardNews->getDisplayDateFormatted() }}</span>
</div>
</h5>
<div class="card-body">
{{-- Kurzer Teaser Text --}}
<div class="news-preview">
<h6 class="font-weight-bold mb-2">
<i class="ion ion-md-information-circle text-primary mr-1"></i>
{{ $dashboardNews->getLang('title') }}
</h6>
<p class="mb-2">
{{ $dashboardNews->getLang('teaser') }}
</p>
</div>
{{-- Collapse für mehr Inhalt --}}
@if($dashboardNews->getLang('content'))
<div class="collapse" id="newsCollapse">
<hr class="my-3">
<div class="news-full-content">
{!! $dashboardNews->getLang('content') !!}
</div>
{{-- Datei-Links --}}
@if($dashboardNews->hasFileLinks())
<div class="mt-3">
<div class="d-flex flex-wrap gap-2">
@foreach($dashboardNews->getFileLinks() as $linkData)
<a href="{{ route('storage_file', [$linkData['file']->id, 'dc_file', 'download']) }}"
class="btn btn-sm btn-outline-success"
target="_blank"
title="{{ $linkData['file']->original_name }}">
<i class="ion ion-md-download mr-1"></i>
{{ $linkData['label'] }}
</a>
@endforeach
<a href="{{ route('user_downloadcenter') }}"
class="btn btn-sm btn-outline-success"
title="{{ __('navigation.downloadcenter') }}">
<i class="ion ion-ios-download mr-1"></i>
{{ __('navigation.downloadcenter') }}
</a>
</div>
</div>
@endif
</div>
{{-- Mehr lesen Button --}}
<div class="mt-3">
<a class="btn btn-sm btn-outline-primary" data-toggle="collapse" href="#newsCollapse" role="button" aria-expanded="false" aria-controls="newsCollapse">
<span class="when-closed">
<i class="ion ion-md-arrow-dropdown"></i> {{__('home.read_more')}}
</span>
<span class="when-open">
<i class="ion ion-md-arrow-dropup"></i> {{__('home.read_less')}}
</span>
</a>
</div>
@endif
</div>
</div>
</div>
<style>
.when-open { display: none; }
.when-closed { display: inline; }
[aria-expanded="true"] .when-open { display: inline; }
[aria-expanded="true"] .when-closed { display: none; }
.gap-2 > * { margin-right: 0.5rem; margin-bottom: 0.5rem; }
.gap-2 > *:last-child { margin-right: 0; }
</style>
@endif

View file

@ -1,15 +1,31 @@
@php
$selectedMonth = request()->get('points_month', date('n'));
$selectedYear = request()->get('points_year', date('Y'));
@endphp
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4">
<h5 class="card-header with-elements">
<div class="card-header-title">{{__('home.current_points_for') }} {{ HTMLHelper::getMonth(date('n')) }} {{ date('Y') }} </div>
<h5 class="card-header with-elements d-flex justify-content-between align-items-center flex-wrap">
<div class="card-header-title">{{__('home.current_points_for') }} {{ HTMLHelper::getMonth($selectedMonth) }} {{ $selectedYear }} </div>
<div class="d-flex align-items-center mt-2 mt-md-0">
<select id="points-month-filter" class="form-control custom-select form-control-sm mr-2" style="width: auto;">
@foreach(HTMLHelper::getTransMonths() as $monthNum => $monthName)
<option value="{{ $monthNum }}" {{ $selectedMonth == $monthNum ? 'selected' : '' }}>{{ $monthName }}</option>
@endforeach
</select>
<select id="points-year-filter" class="form-control custom-select form-control-sm" style="width: auto;">
@foreach(HTMLHelper::getYearRange() as $year)
<option value="{{ $year }}" {{ $selectedYear == $year ? 'selected' : '' }}>{{ $year }}</option>
@endforeach
</select>
</div>
</h5>
<div class="card-body">
@if($user->isActiveAccount())
<h6>
@php($userSalesVolume = $user->getUserSalesVolume(date('n'), date('Y'), 'first'))
@php($userSalesVolume = $user->getUserSalesVolume($selectedMonth, $selectedYear, 'first'))
@if($userSalesVolume)
<div class="mb-2">
<strong>{{ __('team.total_points') }}: {{ $userSalesVolume->getPointsKPSum() }}</strong> | {{ __('team.own') }}: {{ $userSalesVolume->month_points }} | {{ __('team.shop') }}: {{ $userSalesVolume->month_shop_points }}<br>
<strong>{{ __('team.total_points') }}: {{ formatNumber($userSalesVolume->getPointsKPSum()) }}</strong> | {{ __('team.own') }}: {{ $userSalesVolume->getFormattedMonthKPPoints() }} | {{ __('team.shop') }}: {{ $userSalesVolume->getFormattedMonthShopPoints() }}<br>
</div>
<div>
<strong>{{ __('team.total_turnover') }}: {{ formatNumber($userSalesVolume->getTotalNetSum()) }} &euro;</strong> | {{ __('team.own') }}: {{ formatNumber($userSalesVolume->month_total_net) }} &euro; | {{ __('team.shop') }}: {{ formatNumber($userSalesVolume->month_shop_total_net) }} &euro;
@ -25,12 +41,13 @@
<th>{{__('tables.net_sales')}}</th>
<th>{{__('tables.type')}}</th>
<th>{{__('tables.order')}}</th>
<th>{{__('tables.member')}}</th>
<th>{{__('tables.info') }}</th>
<th>{{__('tables.note') }}</th>
</tr>
</thead>
<tbody>
@php($userSalesVolumes = $user->getUserSalesVolume(date('n'), date('Y')))
@php($userSalesVolumes = $user->getUserSalesVolume($selectedMonth, $selectedYear, 'get', ['shopping_order.shopping_user']))
@if($userSalesVolumes)
@foreach ($userSalesVolumes as $userSalesVolume)
<tr>
@ -38,7 +55,7 @@
{{ $userSalesVolume->date }}
</td>
<td class="text-left font-weight-semibold">
{{ $userSalesVolume->points }}
{{ $userSalesVolume->getFormattedPoints() }}
</td>
<td class="text-left font-weight-semibold">
{{ formatNumber($userSalesVolume->total_net) }} &euro;
@ -56,6 +73,13 @@
@endif
@endif
</td>
<td class="text-left font-weight-semibold">
@if($userSalesVolume->shopping_order && $userSalesVolume->shopping_order->shopping_user)
<span class="no-line-break"> {{ $userSalesVolume->shopping_order->shopping_user->billing_firstname }} {{ $userSalesVolume->shopping_order->shopping_user->billing_lastname }}<br>
<small>{{ $userSalesVolume->shopping_order->shopping_user->billing_email }}</small></span>
@endif
</td>
<td class="text-left font-weight-semibold">
<span class="no-line-break"> {{ $userSalesVolume->message }}</span>
</td>
@ -93,5 +117,19 @@
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
// Filter für Monat/Jahr
function updatePointsFilter() {
var month = $('#points-month-filter').val();
var year = $('#points-year-filter').val();
var url = new URL(window.location.href);
url.searchParams.set('points_month', month);
url.searchParams.set('points_year', year);
window.location.href = url.toString();
}
$('#points-month-filter, #points-year-filter').on('change', function() {
updatePointsFilter();
});
});
</script>

View file

@ -0,0 +1,246 @@
@php
$selectedMonth = request()->get('stats_month', date('n'));
$selectedYear = request()->get('stats_year', date('Y'));
// Start- und Enddatum für den Monat
$startDate = \Carbon\Carbon::createFromDate($selectedYear, $selectedMonth, 1)->startOfMonth();
$endDate = \Carbon\Carbon::createFromDate($selectedYear, $selectedMonth, 1)->endOfMonth();
// UserBusiness für den Monat laden (enthält Payline-Punkte)
$userBusiness = \App\Models\UserBusiness::where('user_id', $user->id)
->where('month', $selectedMonth)
->where('year', $selectedYear)
->first();
// UserSalesVolume für KP-Punkte
$userSalesVolume = $user->getUserSalesVolume($selectedMonth, $selectedYear, 'first');
// Kunden-Umsatz Punkte (KP - Eigene Punkte + Shop)
$customerPoints = $userSalesVolume ? $userSalesVolume->getPointsKPSum() : 0;
// Team-Umsatz Punkte (Payline) - Live-Berechnung wenn UserBusiness nicht verfügbar
$teamPaylinePoints = 0;
$isLiveCalculation = false;
if ($userBusiness) {
// Gespeicherte Daten aus UserBusiness verwenden
$teamPaylinePoints = $userBusiness->payline_points ?? 0;
} else {
// Live-Berechnung über TreeCalcBot (für aktuellen Monat)
$isLiveCalculation = true;
try {
$treeCalcBot = new \App\Services\BusinessPlan\TreeCalcBot((int)$selectedMonth, (int)$selectedYear, 'member');
$treeCalcBot->initBusinesslUserDetail($user);
if ($treeCalcBot->business_user) {
$teamPaylinePoints = $treeCalcBot->business_user->payline_points ?? 0;
}
} catch (\Exception $e) {
\Log::warning("Live-Berechnung Payline-Punkte fehlgeschlagen für User {$user->id}: " . $e->getMessage());
$teamPaylinePoints = 0;
}
}
// Direkte Neupartner (Firstlines) in diesem Monat
$directNewPartners = \App\User::where('m_sponsor', $user->id)
->where('active_date', '>=', $startDate)
->where('active_date', '<=', $endDate)
->where('m_level', '!=', null)
->where('payment_account', '!=', null)
->count();
// ===== REKURSIVE TEAM-STRUKTUR =====
// Hilfsfunktion um alle Team-Mitglieder rekursiv zu sammeln (inkl. Sponsor-Kette)
$getAllTeamMemberIds = function($sponsorId, $maxDepth = 20) use (&$getAllTeamMemberIds) {
static $cache = [];
if (isset($cache[$sponsorId])) {
return $cache[$sponsorId];
}
$teamIds = [];
$currentLevel = [$sponsorId];
$depth = 0;
while (!empty($currentLevel) && $depth < $maxDepth) {
$children = \App\User::whereIn('m_sponsor', $currentLevel)
->where('m_level', '!=', null)
->where('payment_account', '!=', null)
->whereNull('deleted_at')
->pluck('id')
->toArray();
$teamIds = array_merge($teamIds, $children);
$currentLevel = $children;
$depth++;
}
$cache[$sponsorId] = $teamIds;
return $teamIds;
};
// Alle Team-Mitglieder rekursiv laden
$allTeamMemberIds = $getAllTeamMemberIds($user->id);
// Neupartner im gesamten Team (rekursiv im Marketingplan)
$teamNewPartners = 0;
if (!empty($allTeamMemberIds)) {
$teamNewPartners = \App\User::whereIn('id', $allTeamMemberIds)
->where('active_date', '>=', $startDate)
->where('active_date', '<=', $endDate)
->count();
}
// Kundenabos (is_for = 'customer' oder shop-bezogen)
$customerAbos = \App\Models\UserAbo::where('member_id', $user->id)
->where('is_for', 'customer')
->whereIn('status', [1, 2]) // aktive Abos
->where('active', true)
->count();
// Eigene Abos
$ownAbos = \App\Models\UserAbo::where('user_id', $user->id)
->where('is_for', 'me')
->whereIn('status', [1, 2])
->where('active', true)
->count();
// Team-Abos (rekursiv über gesamtes Team im Marketingplan)
$teamAbos = 0;
if (!empty($allTeamMemberIds)) {
$teamAbos = \App\Models\UserAbo::whereIn('user_id', $allTeamMemberIds)
->where('is_for', 'me')
->whereIn('status', [1, 2])
->where('active', true)
->count();
}
@endphp
@if($user->isActiveAccount())
<div class="d-flex col-xl-12 align-items-stretch">
<div class="card w-100 mb-4">
<h5 class="card-header with-elements d-flex justify-content-between align-items-center flex-wrap">
<div class="card-header-title">
<i class="ion ion-md-stats mr-2"></i>{{__('home.monthly_statistics') }} - {{ HTMLHelper::getMonth($selectedMonth) }} {{ $selectedYear }}
</div>
<div class="d-flex align-items-center mt-2 mt-md-0">
<select id="stats-month-filter" class="form-control custom-select form-control-sm mr-2" style="width: auto;">
@foreach(HTMLHelper::getTransMonths() as $monthNum => $monthName)
<option value="{{ $monthNum }}" {{ $selectedMonth == $monthNum ? 'selected' : '' }}>{{ $monthName }}</option>
@endforeach
</select>
<select id="stats-year-filter" class="form-control custom-select form-control-sm" style="width: auto;">
@foreach(HTMLHelper::getYearRange() as $year)
<option value="{{ $year }}" {{ $selectedYear == $year ? 'selected' : '' }}>{{ $year }}</option>
@endforeach
</select>
</div>
</h5>
<div class="card-body">
<div class="row">
{{-- Kunden-Umsatz Punkte --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-cart" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_turnover_points') }}</div>
<div class="font-weight-bold text-large">{{ number_format($customerPoints, 0, ',', '.') }}</div>
</div>
</div>
</div>
{{-- Team-Umsatz Punkte (Payline) --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-success text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-people" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">
{{ __('home.team_turnover_points') }} (Payline)
@if($isLiveCalculation)
<span class="badge badge-pill badge-info ml-1" title="{{ __('home.live_calculation_hint') }}">Live</span>
@endif
</div>
<div class="font-weight-bold text-large">{{ number_format($teamPaylinePoints, 0, ',', '.') }}</div>
</div>
</div>
</div>
{{-- Direkte Neupartner --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-info text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-person-add" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.direct_new_partners') }}</div>
<div class="font-weight-bold text-large">{{ $directNewPartners }}</div>
</div>
</div>
</div>
{{-- Neupartner im Team --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-warning text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-contacts" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_new_partners') }}</div>
<div class="font-weight-bold text-large">{{ $teamNewPartners }}</div>
</div>
</div>
</div>
{{-- Kundenabos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-secondary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-repeat" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.customer_subscriptions') }}</div>
<div class="font-weight-bold text-large">{{ $customerAbos }} + {{ $ownAbos }} {{ __('home.own') }}</div>
</div>
</div>
</div>
{{-- Team-Abos --}}
<div class="col-md-6 col-lg-4 mb-4">
<div class="d-flex align-items-center">
<div class="bg-dark text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="ion ion-md-sync" style="font-size: 1.5rem;"></i>
</div>
<div class="ml-3">
<div class="text-muted small">{{ __('home.team_subscriptions') }}</div>
<div class="font-weight-bold text-large">{{ $teamAbos }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// Filter für Monat/Jahr - Statistiken
function updateStatsFilter() {
var month = $('#stats-month-filter').val();
var year = $('#stats-year-filter').val();
var url = new URL(window.location.href);
url.searchParams.set('stats_month', month);
url.searchParams.set('stats_year', year);
window.location.href = url.toString();
}
$('#stats-month-filter, #stats-year-filter').on('change', function() {
updateStatsFilter();
});
});
</script>
@endif