mivita/app/Http/Controllers/HomeController.php
Kevin 386851aee8 Backoffice-Statistik vereinfacht & Dashboard-Monatsstatistik konsolidiert
- Statistik-Uebersicht: Tabelle auf Linie/Berater/Umsatz(Punkte)/Teamabos/
  Teamkundenabos reduziert; Neupartner als grünes "+N"-Badge in der
  Berater-Spalte; 1000-Punkte-Shop & Neupartner als klickbare KPI-Cards
  (Augen-Icon/Liste anzeigen)
- Teamkundenabos-Drilldown pro Teampartner gruppiert (Anzahl + Volumenpunkte
  gesamt, aufklappbar je Abo), CSV-Export angepasst
- Home: fehlerhafte Monatsstatistik-Card (inkonsistente aktiv-Definition,
  kein Monatsbezug, schwere Live-Logik im Blade) mit der Abo-KPI-Card
  zusammengefuehrt; Logik nach HomeController::buildMonthlyStats() ausgelagert,
  konsistent ueber TeamAboOverviewService/BackofficeDashboardService
- Team-Payline fuer laufenden Monat live berechnet und 6h gecacht (Snapshot)
- Kacheln in Cluster (Umsatz/Neupartner/Abos) gruppiert
- Services: teamKpis() um *_new ergaenzt, personalAboKpis() & newPartnersCount() neu
- i18n (de/en/es/fr), Tests ergaenzt/aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 13:00:38 +00:00

421 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers;
use App\Models\DashboardNews;
use App\Models\Incentive;
use App\Models\IncentiveParticipant;
use App\Models\ShoppingPayment;
use App\Models\UserAbo;
use App\Models\UserBusiness;
use App\Services\Abo\TeamAboOverviewService;
use App\Services\AboHelper;
use App\Services\Backoffice\BackofficeDashboardService;
use App\Services\BusinessPlan\TreeCalcBot;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Request;
use Util;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct() {}
public function index()
{
if (! Auth::check()) {
return redirect('login');
}
return redirect('home');
}
public function newsArchive(): \Illuminate\View\View
{
return view('dashboard.news_archive', [
'currentNews' => \App\Models\DashboardNews::getActiveNews(),
'archiveNews' => \App\Models\DashboardNews::getArchiveNews(),
]);
}
// login / Dashboard
public function show()
{
if (! Auth::check()) {
return redirect('login');
}
$user = Auth::user();
$activeIncentive = null;
$incentiveParticipant = null;
if ($user->isActiveAccount()) {
$activeIncentive = Incentive::active()->first();
if ($activeIncentive) {
$incentiveParticipant = IncentiveParticipant::where('incentive_id', $activeIncentive->id)
->where('user_id', $user->id)
->first();
}
}
// Eigenes Abo des Beraters für die Einmalprodukte-Dashboard-Card (nur wenn
// das Zeitfenster offen ist und das Feature für den User sichtbar ist).
$oneTimeAbo = null;
$aboOneTimeId = UserAbo::where('user_id', $user->id)
->where('is_for', 'me')
->where('active', true)
->where('status', 2)
->value('id');
if ($aboOneTimeId) {
$abo = UserAbo::find($aboOneTimeId);
if ($abo && AboHelper::isOneTimeWindowOpen($abo) && AboHelper::isOneTimeFeatureVisible($abo)) {
$oneTimeAbo = $abo;
}
}
$monthlyStats = null;
if ($user->isActiveAccount()) {
$monthlyStats = $this->buildMonthlyStats($user);
}
$data = [
'user' => $user,
'now' => Carbon::now(),
'dashboardNews' => DashboardNews::getActiveNews(),
'activeIncentive' => $activeIncentive,
'incentiveParticipant' => $incentiveParticipant,
'oneTimeAbo' => $oneTimeAbo,
'monthlyStats' => $monthlyStats,
];
return view('home', $data);
}
/**
* Kennzahlen der Monatsstatistik-Card. Bündelt persönliche Punkte/Abos, Team-Abo-KPIs
* (konsistent über {@see TeamAboOverviewService}) und die Team-Payline-Punkte.
*
* @return array<string, mixed>
*/
private function buildMonthlyStats(User $user): array
{
$month = max(1, min(12, (int) Request::get('stats_month', now()->month)));
$year = (int) Request::get('stats_year', now()->year);
$salesVolume = $user->getUserSalesVolume($month, $year, 'first');
[$paylinePoints, $paylineIsLive] = $this->teamPaylinePoints($user, $month, $year);
$firstlineIds = User::where('m_sponsor', $user->id)
->whereColumn('id', '!=', 'm_sponsor')
->whereNull('deleted_at')
->pluck('id')
->map(fn ($id) => (int) $id)
->all();
$dashboardService = app(BackofficeDashboardService::class);
$teamOverview = app(TeamAboOverviewService::class);
return array_merge(
[
'month' => $month,
'year' => $year,
'customer_points' => $salesVolume ? (float) $salesVolume->getPointsKPSum() : 0.0,
'team_payline_points' => $paylinePoints,
'payline_is_live' => $paylineIsLive,
'direct_new_partners' => $dashboardService->newPartnersCount($firstlineIds, $month, $year),
],
$teamOverview->teamKpis($user, $month, $year),
$teamOverview->personalAboKpis($user, $month, $year)
);
}
/**
* Team-Payline-Punkte: aus dem UserBusiness-Snapshot (abgeschlossene Monate) oder
* – für den laufenden Monat – einmalig live berechnet und für Folge-Aufrufe gecacht.
*
* @return array{0: float, 1: bool} [Punkte, istLive]
*/
private function teamPaylinePoints(User $user, int $month, int $year): array
{
$userBusiness = UserBusiness::where('user_id', $user->id)
->where('month', $month)
->where('year', $year)
->first();
if ($userBusiness) {
return [(float) ($userBusiness->payline_points ?? 0), false];
}
$points = Cache::remember(
"home_payline_{$user->id}_{$month}_{$year}",
now()->addHours(6),
fn (): float => $this->calculatePaylineLive($user, $month, $year)
);
return [(float) $points, true];
}
private function calculatePaylineLive(User $user, int $month, int $year): float
{
try {
$treeCalcBot = new TreeCalcBot($month, $year, 'member');
$treeCalcBot->initBusinesslUserDetail($user);
if ($treeCalcBot->business_user) {
return (float) ($treeCalcBot->business_user->payline_points ?? 0);
}
} catch (\Throwable $e) {
\Log::warning("Live-Berechnung Payline-Punkte fehlgeschlagen für User {$user->id}: ".$e->getMessage());
}
return 0.0;
}
public function loadingModal()
{
$data = Request::get('data');
$target = Request::get('target');
$response = '';
if ($data === 'data_protection') {
$data = [
'modal' => true,
'user_shop' => true,
'isMivitaShop' => false,
];
$response = view('legal.data_protect_de', $data)->render();
}
if ($data === 'imprint') {
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.imprint_de', $data)->render();
}
if ($data === 'shop_term_of_use') {
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.shop_term_of_use_de', $data)->render();
}
if ($data === 'agb') {
$data = [
'modal' => true,
'user_shop' => Util::getUserShop(),
];
$response = view('legal.agb_de', $data)->render();
}
if (Request::ajax()) {
return response()->json(['response' => $response, 'target' => $target]);
}
abort(404);
}
/* public function checkLogin($identify, $token)
{
if($identify){
//user find by $identify
$user = User::where('identify', '=', $identify)->first();
if(!$user){
return abort(404);
}
//user - check für from $sf_guard_user - old system
$sf_guard_user = SfGuardUser::where('identify', '=', $identify)->first();
if(!$sf_guard_user){
return abort(404);
}
if($user->id != $sf_guard_user->user_id){
return abort(404);
}
if($sf_guard_user->token != $token){
return abort(404);
}
$time = Carbon::parse($sf_guard_user->token_at);
$now = Carbon::now();
$duration = $time->diffInSeconds($now);
if($duration > 3){
return abort(404);
}
$sf_guard_user->token = null;
$sf_guard_user->token_at = null;
$sf_guard_user->save();
if(!Auth::check()){
$user->last_login = now();
$user->save();
Auth::login($user);
}
if(Auth::check()){
return redirect('/templates');
}
}
return abort(404);
}
*/
public function zahlungsarten()
{
return view('web.templates.zahlungsarten', [
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
]);
}
public function versandkosten()
{
return view('web.templates.versandkosten', [
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
]);
}
public function legalDataProtected()
{
$data = [
'modal' => false,
'user_shop' => Util::getUserShop(),
'isMivitaShop' => Util::isMivitaShop(),
'yard_instance' => 'webshop',
];
return view('legal.data_protected', $data);
}
public function legalAGB()
{
$data = [
'modal' => false,
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
'yard_instance' => 'webshop',
];
return view('legal.agb', $data);
}
public function legalImprint()
{
$data = [
'modal' => false,
'user_shop' => Util::getUserShop(),
'yard_instance' => 'webshop',
];
return view('legal.imprint', $data);
}
public function verify($confirmation_code)
{
if (! $confirmation_code) {
return redirect('/status/error');
}
$user = User::whereConfirmationCode($confirmation_code)->first();
if (! $user) {
return redirect('/status/not/found');
}
$user_auto_login = false;
if ($user->confirmed === 0) {
$user->confirmed = 1;
$user->confirmation_date = now();
$user_auto_login = true;
// nur bei der ersten Verifizierung den user auto login
}
// wird nun in WizardController::releaseAccount() auf null gesetzt
// $user->confirmation_code = null;
// $user->confirmation_code_to = null;
// $user->confirmation_code_remider = 0;
$user->save();
// Login!
if ($user_auto_login) {
Auth::login($user);
}
$url = Util::getMyMivitaUrl();
return redirect($url);
}
public function statusRegister()
{
return view('status.status_register');
}
public function statusVerify()
{
return view('status.status_verify');
}
public function statusError()
{
return view('status.status_error');
}
public function notFound()
{
return view('status.not_found');
}
/**
* @return string
*/
public function checkMail()
{
$data = Request::all();
if ($data['user_id'] === 'new') {
if (User::where('email', $data['email'])->count()) {
return json_encode(false);
}
} else {
if (User::where('email', $data['email'])->where('id', '!=', $data['user_id'])->count()) {
return json_encode(false);
}
}
return json_encode(true);
}
public function blocked()
{
return view('status.user_blocked');
}
public function backToShop($reference = '')
{
if ($reference) {
$ShoppingPayment = ShoppingPayment::where('reference', $reference)->first();
if ($ShoppingPayment && $ShoppingPayment->status === 'success') {
$user = Auth::user();
// is form wizard create payment
if ($user && ($user->wizard == 13 || $user->wizard == 20)) {
$user->wizard = 15; // realese Payments
$user->save();
return redirect(route('wizard_create', [15]));
}
} else {
\Session()->flash('alert-error', __('msg.error_occurred_with_order'));
return redirect(url('/'));
}
}
}
}