middleware('active.account'); } /** * Die eine Incentive-Seite: Teilnahme oder Kopf mit Stand, Titelbild, Text, * Kacheln, "So funktioniert es", Galerie und Live-Rangliste. */ public function teaser($slug) { $incentive = Incentive::where('slug', $slug) ->where('status', '!=', 0) ->firstOrFail(); $user = Auth::user(); abort_unless($incentive->isVisibleFor($user), 404); $participant = IncentiveParticipant::where('incentive_id', $incentive->id) ->where('user_id', $user->id) ->first(); // Datenschutz (Kundenentscheid 25.09.2026): Unbestaetigte Teilnehmer sehen nur VIP-Konten; // der eigene Eintrag bleibt fuer den Berater selbst sichtbar. $ranking = IncentiveParticipant::where('incentive_id', $incentive->id) ->withRankingActivity() ->when(! $user->isVIP(), fn ($query) => $query->where( fn ($visible) => $visible->whereNotNull('accepted_terms_at')->orWhere('user_id', $user->id) )) ->with('user', 'user.account') ->orderByIncentiveLeaderboard() ->paginate(100); $participateHasTrackableAbos = false; if (! $participant?->accepted_terms_at && $incentive->includesAbos()) { $participateHasTrackableAbos = $this->userHasTrackableAbosForIncentive($user, $incentive); } $payoutService = new IncentivePayoutService; $participantPayout = $incentive->hasCashPayout() && $participant ? $payoutService->forParticipant($incentive, $participant) : null; $rankingPayouts = $incentive->hasCashPayout() ? $ranking->getCollection()->mapWithKeys(fn (IncentiveParticipant $row) => [ $row->id => $payoutService->forParticipant($incentive, $row), ]) : collect(); return view('user.incentive.teaser', [ 'incentive' => $incentive, 'participant' => $participant, 'hasConfirmedParticipation' => $participant && $participant->accepted_terms_at !== null, 'galleryImages' => $this->collectGalleryImages($incentive), 'ranking' => $ranking, 'participateHasTrackableAbos' => $participateHasTrackableAbos, 'isVipView' => $user->isVIP(), 'participantPayout' => $participantPayout, 'rankingPayouts' => $rankingPayouts, ]); } /** * Alte Ranglisten-URL: leitet auf die zusammengefasste Seite mit Sprungmarke zur Rangliste. */ public function show($slug) { return redirect(route('user_incentive_teaser', [$slug]).'#ranking'); } public function participate($slug) { $incentive = Incentive::where('slug', $slug) ->active() ->firstOrFail(); $user = Auth::user(); abort_unless($incentive->isVisibleFor($user), 404); if (! Request::has('accept_terms')) { \Session()->flash('alert-error', __('incentive.terms_required')); return redirect(route('user_incentive_teaser', [$slug])); } $participant = IncentiveParticipant::firstOrCreate( [ 'incentive_id' => $incentive->id, 'user_id' => $user->id, ], [ 'accepted_terms_at' => null, ] ); if ($participant->accepted_terms_at !== null) { \Session()->flash('alert-info', __('incentive.already_participating')); return redirect(route('user_incentive_teaser', [$slug])); } $participant->accepted_terms_at = Carbon::now(); $participant->save(); $repair = app(IncentivePointsLogRepairService::class); $repair->syncMissingTrackingAbos($participant); $repair->syncMissingSalesVolumeLogs($participant); $participant->refresh()->recalculateFromTrackingTables()->save(); IncentiveTracker::updateRanking($incentive); \Session()->flash('alert-success', __('incentive.participation_confirmed')); return redirect(route('user_incentive_teaser', [$slug])); } public function details($slug) { $incentive = Incentive::where('slug', $slug) ->where('status', '!=', 0) ->firstOrFail(); $user = Auth::user(); abort_unless($incentive->isVisibleFor($user), 404); $participant = IncentiveParticipant::with('incentive', 'user', 'user.account') ->where('incentive_id', $incentive->id) ->where('user_id', $user->id) ->firstOrFail(); if ($participant->accepted_terms_at === null) { \Session()->flash('alert-info', __('incentive.details_requires_confirmation')); return redirect(route('user_incentive_teaser', [$slug])); } $data = AdminIncentiveController::buildParticipantDetailData($participant); return view('user.incentive.details', $data); } /** * Galerie-Bilder des Incentives aus seinem eigenen Ordner ({@see Incentive::galleryImages()}). * * @return list */ private function collectGalleryImages(Incentive $incentive): array { return $incentive->galleryImages(); } /** * Hinweis auf der Teilnehmen-Karte: Es gibt bereits ein Eigenabo oder ein Kundenabo im Qualifikationszeitraum. */ private function userHasTrackableAbosForIncentive(User $user, Incentive $incentive): bool { $qualEnd = $incentive->qualification_end->copy()->endOfDay(); $hasOwnActiveAbo = UserAbo::query() ->where('user_id', $user->id) ->where('is_for', 'me') ->where('status', 2) ->exists(); $hasCustomerAboInQualification = UserAbo::query() ->where('member_id', $user->id) ->where('is_for', 'ot') ->where('status', 2) ->whereBetween('created_at', [ $incentive->qualification_start, $qualEnd, ]) ->exists(); return $hasOwnActiveAbo || $hasCustomerAboInQualification; } }