10.April 2026

This commit is contained in:
Kevin Adametz 2026-04-10 17:15:27 +02:00
parent a00c42e770
commit f58c709945
208 changed files with 19280 additions and 2914 deletions

View file

@ -45,9 +45,13 @@ class AboController extends Controller
}
if ($view === 'ot') {
$user_abos = UserAbo::where('member_id', \Auth::user()->id)
$selectedYear = (int) \Request::get('year', now()->year);
$baseQuery = UserAbo::where('member_id', \Auth::user()->id)
->where('status', '>', 1)
->where('is_for', 'ot')
->where('is_for', 'ot');
$user_abos = (clone $baseQuery)
->with(['user_abo_items', 'user_abo_items.product', 'shopping_user'])
->orderBy('id', 'desc')
->get();
@ -55,6 +59,10 @@ class AboController extends Controller
'user_abos' => $user_abos,
'view' => 'ot',
'isAdmin' => false,
'chartData' => AboHelper::getMonthlyAboCounts($baseQuery, $selectedYear, 'ot', \Auth::user()->id),
'chartYear' => $selectedYear,
'chartYears' => \App\Services\HTMLHelper::getYearRange(2026),
'chartMonths' => \App\Services\HTMLHelper::getTransMonths(),
]);
}

View file

@ -0,0 +1,200 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Admin\IncentiveController as AdminIncentiveController;
use App\Http\Controllers\Controller;
use App\Models\Incentive;
use App\Models\IncentiveParticipant;
use App\Models\UserAbo;
use App\Services\Incentive\IncentivePointsLogRepairService;
use App\Services\Incentive\IncentiveTracker;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Request;
class IncentiveController extends Controller
{
/**
* Anzahl Plaetze in der User-Live-Rangliste (Gewinner-Highlight bleibt ueber max_winners, typ. Top 20).
*/
public const USER_RANKING_DISPLAY_LIMIT = 30;
public function __construct()
{
$this->middleware('active.account');
}
public function teaser($slug)
{
$incentive = Incentive::where('slug', $slug)
->where('status', '!=', 0)
->firstOrFail();
$user = Auth::user();
$participant = IncentiveParticipant::where('incentive_id', $incentive->id)
->where('user_id', $user->id)
->first();
$galleryImages = $this->collectGalleryImages($incentive);
return view('user.incentive.teaser', [
'incentive' => $incentive,
'participant' => $participant,
'galleryImages' => $galleryImages,
]);
}
public function show($slug)
{
$incentive = Incentive::where('slug', $slug)
->where('status', '!=', 0) // not draft
->firstOrFail();
$user = Auth::user();
$participant = IncentiveParticipant::where('incentive_id', $incentive->id)
->where('user_id', $user->id)
->first();
$ranking = IncentiveParticipant::where('incentive_id', $incentive->id)
->withRankingActivity()
->with('user', 'user.account')
->orderByIncentiveLeaderboard()
->limit(self::USER_RANKING_DISPLAY_LIMIT)
->get();
$participateHasTrackableAbos = false;
if (! $participant?->accepted_terms_at) {
$participateHasTrackableAbos = $this->userHasTrackableAbosForIncentive($user, $incentive);
}
return view('user.incentive.show', [
'incentive' => $incentive,
'participant' => $participant,
'hasConfirmedParticipation' => $participant && $participant->accepted_terms_at !== null,
'ranking' => $ranking,
'rankingDisplayLimit' => self::USER_RANKING_DISPLAY_LIMIT,
'participateHasTrackableAbos' => $participateHasTrackableAbos,
]);
}
public function participate($slug)
{
$incentive = Incentive::where('slug', $slug)
->active()
->firstOrFail();
if (! Request::has('accept_terms')) {
\Session()->flash('alert-error', __('incentive.terms_required'));
return redirect(route('user_incentive_show', [$slug]));
}
$user = Auth::user();
$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_show', [$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_show', [$slug]));
}
public function details($slug)
{
$incentive = Incentive::where('slug', $slug)
->where('status', '!=', 0)
->firstOrFail();
$user = Auth::user();
$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_show', [$slug]));
}
$data = AdminIncentiveController::buildParticipantDetailData($participant);
return view('user.incentive.details', $data);
}
/**
* Sammelt alle verfuegbaren Galerie-Bilder aus public/img/incentive/
* (ohne das Hauptbild, das bereits als Hero verwendet wird).
*
* @return list<string>
*/
private function collectGalleryImages(Incentive $incentive): array
{
$dir = public_path('img/incentive');
if (! is_dir($dir)) {
return [];
}
$files = glob($dir . '/*.{jpg,jpeg,png,webp}', GLOB_BRACE) ?: [];
$images = [];
foreach ($files as $file) {
$basename = basename($file);
$images[] = 'img/incentive/' . $basename;
}
sort($images);
return $images;
}
/**
* 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;
}
}

View file

@ -63,7 +63,6 @@ class MembershipController extends Controller
if ($user->isActiveAccount() && ! $user->isActiveShop()) {
$payment_greaterThan = Carbon::parse($user->payment_account)->modify('-'.(config('mivita.renewal_days') + 1).' days');
$userHistoryUpgradeOrder = UserHistory::whereUserId($user->id)->whereAction('upgrade_order')->where('created_at', '>=', $payment_greaterThan)->get()->last();
}
$userHistoryDeleteMembership = UserHistory::whereUserId($user->id)->whereAction('delete_membership')->whereStatus(50)->get()->last();
@ -87,7 +86,6 @@ class MembershipController extends Controller
];
return view('user.membership.index', $data);
}
private function checkShoppingCountry($user)
@ -158,8 +156,11 @@ class MembershipController extends Controller
if ($product->images->count()) {
$image = $product->images->first()->slug;
}
$qty = Request::get('qty') ? Request::get('qty') : 1;
$qty = $product->is_membership_only ? 1 : (Request::get('qty') ? Request::get('qty') : 1);
$cartItem = Yard::instance('shopping')->add($product->id, $product->getLang('name'), $qty, $product->getPriceWith(\App\Services\UserService::getTaxFree(), false, \App\Services\UserService::$user_country), false, false, ['image' => $image, 'slug' => $product->slug, 'weight' => $product->weight, 'points' => $product->points, 'no_commission' => $product->no_commission, 'no_free_shipping' => $product->no_free_shipping, 'show_on' => $product->show_on]);
if ($cartItem->qty > 1) {
Yard::instance('shopping')->update($cartItem->rowId, 1);
}
if (\App\Services\UserService::getTaxFree()) {
Yard::setTax($cartItem->rowId, 0);
} else {
@ -214,7 +215,6 @@ class MembershipController extends Controller
\Session()->flash('alert-success', __('msg.booked_package_has_been_changed'));
return back();
}
}
}
@ -236,11 +236,9 @@ class MembershipController extends Controller
\Session()->flash('alert-error', __('msg.error_checkbox_not_confirm'));
return back();
}
\Session()->flash('alert-error', __('msg.error_checkbox_not_confirm'));
return back();
}
}

View file

@ -14,6 +14,7 @@ use App\Services\AboHelper;
use App\Services\MyLog;
use App\Services\OrderPaymentService;
use App\Services\Payment;
use App\Services\ProductOrderContext;
use App\Services\Shop;
use App\Services\UserService;
use App\Services\Util;
@ -182,6 +183,16 @@ class OrderController extends Controller
$delivery_id = $shopping_user->id;
}
$isAbo = str_contains($for, 'abo');
$previousFor = session('user_order_flow_for');
if ($previousFor !== null && $previousFor !== $for) {
$previousAbo = str_contains($previousFor, 'abo');
if (ProductOrderContext::allowedShowOnIds($previousAbo, $previousFor) !== ProductOrderContext::allowedShowOnIds($isAbo, $for)) {
Yard::instance('shopping')->destroy();
}
}
session(['user_order_flow_for' => $for]);
if ($for === 'ot-customer' || $for === 'abo-ot-customer') {
UserService::initCustomerYard($shopping_user, $for);
} else {
@ -262,7 +273,7 @@ class OrderController extends Controller
// Prepare common data
$data['is_from'] = 'user_order';
$data['is_for'] = $for;
$data['is_abo'] = $data['is_abo'] ?? 0;
$data['is_abo'] = str_contains($for, 'abo');
$data['abo_interval'] = $data['abo_interval'] ?? 0;
$data['shopping_user_id'] = $id;
$data['user_price_infos'] = Yard::instance('shopping')->getUserPriceInfos();
@ -406,6 +417,17 @@ class OrderController extends Controller
throw new \Exception(__('msg.shipping_country_was_not_correctly'));
}
$isAbo = str_contains($data['shipping_is_for'], 'abo');
foreach (Yard::instance('shopping')->content() as $row) {
$product = Product::find($row->id);
if (! $product) {
continue;
}
if (! ProductOrderContext::isProductAllowedInContext($product, $isAbo, $data['shipping_is_for'])) {
throw new \Exception(__('msg.cart_product_not_allowed_for_order_type'));
}
}
if ($data['shipping_is_for'] !== 'ot-customer') {
if (Yard::instance('shopping')->shipping_free) {
$identifier = 'error-'.time().mt_rand(1000000, 9999999);
@ -748,6 +770,15 @@ class OrderController extends Controller
return response()->json(['response' => false, 'message' => 'Product not found']);
}
$isAbo = str_contains($is_for, 'abo');
$qty = isset($data['qty']) ? (int) $data['qty'] : 0;
if ($qty > 0 && ! ProductOrderContext::isProductAllowedInContext($product, $isAbo, $is_for)) {
return response()->json([
'response' => false,
'message' => __('msg.cart_product_not_allowed_for_order_type'),
]);
}
$image = '';
if ($product->images->count()) {
$image = $product->images->first()->slug;

View file

@ -713,9 +713,13 @@ class TeamController extends Controller
// Hole Team-Mitglieder-IDs effizient via Sponsor-Hierarchie
$teamUserIds = AboHelper::getTeamUserIds($user->id);
// Hole Abos der Team-Mitglieder
$abos = \App\Models\UserAbo::whereIn('user_id', $teamUserIds)
$selectedYear = (int) Request::get('year', now()->year);
$baseQuery = \App\Models\UserAbo::whereIn('user_id', $teamUserIds)
->where('is_for', 'me')
->where('status', '>', 1);
// Hole Abos der Team-Mitglieder
$abos = (clone $baseQuery)
->with(['user', 'user.account', 'user_abo_items', 'user_abo_items.product'])
->orderBy('next_date', 'asc')
->get();
@ -724,11 +728,45 @@ class TeamController extends Controller
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
'abos' => $abos,
'chartData' => AboHelper::getMonthlyAboCounts($baseQuery, $selectedYear, 'team_abos', $user->id),
'chartYear' => $selectedYear,
'chartYears' => HTMLHelper::getYearRange(2026),
'chartMonths' => HTMLHelper::getTransMonths(),
];
return view('user.team.abos', $data);
}
/**
* Zeigt eine Übersicht der Kunden-Abos aller Team-Mitglieder (anonymisiert)
*/
public function showTeamCustomerAbos(): \Illuminate\View\View
{
$user = User::find(\Auth::user()->id);
$teamUserIds = AboHelper::getTeamUserIds($user->id);
$selectedYear = (int) Request::get('year', now()->year);
$baseQuery = \App\Models\UserAbo::whereIn('member_id', $teamUserIds)
->where('is_for', 'ot')
->where('status', '>', 1);
$abos = (clone $baseQuery)
->with(['member', 'member.account', 'user_abo_items', 'user_abo_items.product'])
->orderBy('member_id')
->orderBy('next_date', 'asc')
->get();
$groupedByMember = $abos->groupBy('member_id');
return view('user.team.customer_abos', [
'groupedByMember' => $groupedByMember,
'chartData' => AboHelper::getMonthlyAboCounts($baseQuery, $selectedYear, 'team_cust_abos', $user->id),
'chartYear' => $selectedYear,
'chartYears' => HTMLHelper::getYearRange(2026),
'chartMonths' => HTMLHelper::getTransMonths(),
]);
}
/**
* Zeigt die Detail-Ansicht eines Team-Abos an
*/