Incentive (E1): Recruiting-Challenge mit Geldpraemie, Auszahlungs-Export und parallelen Incentives

Neue Felder amount_per_partner, bonus_rank_1..3 und include_sales_points an
incentives. Mit ausgeschalteten Umsatzpunkten zaehlt die Wertung nur die
Einmalpunkte je Neupartner, die Rangliste entspricht damit der Anzahl
Neupartner mit Starterpaket. IncentivePayoutService rechnet Neupartner mal
Betrag plus Rang-Bonus fuer qualifizierte Gewinner; Admin-Detailseite bekommt
Praemienspalte, Summen und XLSX-Export fuer die Auszahlung.

Sidenav und Dashboard zeigen alle aktiven Incentives, damit Montenegro und
die Challenge parallel laufen koennen. Teaser, Rangliste und Details zeigen
bei Geldpraemie die Praemien-Kachel und "Deine Praemie bisher" und blenden
Abo-Bloecke aus, wenn Abos keine Rolle spielen. Montenegro bleibt unveraendert.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-09-18 12:10:27 +00:00
parent 03416b0afe
commit e3efa2f5f0
27 changed files with 1418 additions and 129 deletions

View file

@ -121,6 +121,43 @@
</div>
</div>
{{-- ===== PRAEMIE / AUSZAHLUNG (Recruiting-Challenge) ===== --}}
<div class="card mb-4">
<div class="card-header">
<i class="ion ion-md-cash mr-1"></i>
<strong>{{ __('incentive.payout_section') }}</strong>
</div>
<div class="card-body">
<p class="text-muted small">{{ __('incentive.payout_section_help') }}</p>
<div class="form-row">
<div class="form-group col-md-3">
<label for="amount_per_partner">{{ __('incentive.amount_per_partner') }}</label>
<input type="number" step="0.01" min="0" class="form-control" id="amount_per_partner"
name="amount_per_partner"
value="{{ old('amount_per_partner', isset($incentive->amount_per_partner) ? number_format((float) $incentive->amount_per_partner, 2, '.', '') : '') }}">
<small class="form-text text-muted">{{ __('incentive.amount_per_partner_help') }}</small>
</div>
@for ($bonusRank = 1; $bonusRank <= \App\Models\Incentive::RANK_BONUS_SLOTS; $bonusRank++)
<div class="form-group col-md-3">
<label for="bonus_rank_{{ $bonusRank }}">{{ __('incentive.bonus_rank', ['n' => $bonusRank]) }}</label>
<input type="number" step="0.01" min="0" class="form-control" id="bonus_rank_{{ $bonusRank }}"
name="bonus_rank_{{ $bonusRank }}"
value="{{ old('bonus_rank_' . $bonusRank, isset($incentive) && $incentive->{'bonus_rank_' . $bonusRank} !== null ? number_format((float) $incentive->{'bonus_rank_' . $bonusRank}, 2, '.', '') : '') }}">
</div>
@endfor
</div>
<div class="custom-control custom-checkbox">
<input type="hidden" name="include_sales_points" value="0">
<input type="checkbox" class="custom-control-input" id="include_sales_points" name="include_sales_points"
value="1" @if (old('include_sales_points', isset($incentive) ? (int) $incentive->includesSalesPoints() : 1)) checked @endif>
<label class="custom-control-label" for="include_sales_points">{{ __('incentive.include_sales_points') }}</label>
</div>
<small class="form-text text-muted">{{ __('incentive.include_sales_points_help') }}</small>
</div>
</div>
{{-- ===== INHALTE: DEUTSCH (Standard) ===== --}}
<div class="card mb-4">
<div class="card-header bg-primary text-white">

View file

@ -8,6 +8,12 @@
<a href="{{ route('admin_incentive_edit', [$incentive->id]) }}" class="btn btn-sm btn-warning">
<span class="fa fa-edit"></span> {{ __('incentive.edit') }}
</a>
@if ($incentive->hasCashPayout())
<a href="{{ route('admin_incentive_payout_export', [$incentive->id]) }}" class="btn btn-sm btn-success"
id="incentive-payout-export">
<span class="fa fa-file-excel"></span> {{ __('incentive.payout_export') }}
</a>
@endif
{{-- <form action="{{ route('admin_incentive_recalculate', [$incentive->id]) }}" method="POST" class="d-inline">
@csrf
<button type="submit" class="btn btn-sm btn-info" onclick="return confirm('{{ __('incentive.recalculate_confirm') }}')">
@ -45,9 +51,37 @@
</div>
<div class="col-md-4">
<strong>{{ __('incentive.min_direct_partners') }}:</strong> {{ $incentive->min_direct_partners }}<br>
<strong>{{ __('incentive.min_customer_abos') }}:</strong> {{ $incentive->min_customer_abos }}
<strong>{{ __('incentive.min_customer_abos') }}:</strong> {{ $incentive->min_customer_abos }}<br>
<strong>{{ __('incentive.include_sales_points') }}:</strong>
{{ $incentive->includesSalesPoints() ? __('incentive.yes') : __('incentive.no') }}
</div>
</div>
@if ($incentive->hasCashPayout())
<hr>
<div class="row" id="incentive-payout-config">
<div class="col-md-4">
<strong>{{ __('incentive.amount_per_partner') }}:</strong>
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount((float) $incentive->amount_per_partner) }}
</div>
<div class="col-md-4">
@foreach ($incentive->rankBonuses() as $bonusRank => $bonusAmount)
<strong>{{ __('incentive.bonus_rank', ['n' => $bonusRank]) }}:</strong>
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($bonusAmount) }}<br>
@endforeach
</div>
@if ($payoutTotals)
<div class="col-md-4">
<strong>{{ __('incentive.payout_recipients') }}:</strong> {{ $payoutTotals['recipients'] }}<br>
<strong>{{ __('incentive.payout_partner_amount') }}:</strong>
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($payoutTotals['partner_amount']) }}<br>
<strong>{{ __('incentive.payout_rank_bonus') }}:</strong>
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($payoutTotals['rank_bonus']) }}<br>
<strong>{{ __('incentive.payout_total') }}:</strong>
<span class="font-weight-bold">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($payoutTotals['total']) }}</span>
</div>
@endif
</div>
@endif
</div>
</div>
@ -68,6 +102,9 @@
<th>{{ __('incentive.partners') }}</th>
<th>{{ __('incentive.abos') }}</th>
<th>{{ __('incentive.qualified') }}</th>
@if ($incentive->hasCashPayout())
<th class="text-right">{{ __('incentive.payout_total') }}</th>
@endif
<th>{{ __('incentive.status') }}</th>
<th></th>
</tr>
@ -117,6 +154,19 @@
<span class="badge badge-secondary">{{ __('incentive.no') }}</span>
@endif
</td>
@if ($incentive->hasCashPayout())
@php $payout = $payoutByParticipantId[$p->id] ?? null; @endphp
<td class="text-right">
@if ($payout && $payout['total'] > 0)
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($payout['total']) }}
@if ($payout['rank_bonus'] > 0)
<small class="text-muted d-block">{{ __('incentive.payout_incl_bonus', ['amount' => \App\Services\Incentive\IncentivePayoutService::formatAmount($payout['rank_bonus'])]) }}</small>
@endif
@else
—
@endif
</td>
@endif
<td>
@if ($isWinner)
<span class="badge badge-warning">{{ __('incentive.winner') }}</span>
@ -136,7 +186,7 @@
</tr>
@empty
<tr>
<td colspan="10" class="text-center text-muted">{{ __('incentive.no_participants') }}</td>
<td colspan="{{ $incentive->hasCashPayout() ? 11 : 10 }}" class="text-center text-muted">{{ __('incentive.no_participants') }}</td>
</tr>
@endforelse
</tbody>

View file

@ -1,6 +1,19 @@
@if (isset($activeIncentive) && $activeIncentive)
@php
// Mehrere aktive Incentives parallel (E1): Fallback auf die alte Einzelvariable.
$dashIncentives = isset($activeIncentives) && $activeIncentives
? $activeIncentives
: collect(isset($activeIncentive) && $activeIncentive ? [$activeIncentive] : []);
@endphp
@foreach ($dashIncentives as $activeIncentive)
@php
$incentiveParticipant = isset($incentiveParticipants) && $incentiveParticipants
? $incentiveParticipants->get($activeIncentive->id)
: ($incentiveParticipant ?? null);
$hasConfirmedDash = $incentiveParticipant && $incentiveParticipant->accepted_terms_at !== null;
$dashSuffix = '-' . $activeIncentive->id;
$dashPayout = $activeIncentive->hasCashPayout() && $incentiveParticipant
? (new \App\Services\Incentive\IncentivePayoutService)->forParticipant($activeIncentive, $incentiveParticipant)
: null;
@endphp
<div class="d-flex col-xl-12 align-items-stretch">
<div class="w-100 mb-4 inc-dash-widget">
@ -12,7 +25,7 @@
<div class="inc-dash-hero-overlay"></div>
<div class="inc-dash-hero-content">
<span class="inc-dash-badge">
<i class="ion ion-md-trophy mr-1"></i> Incentive VIP
<i class="ion ion-md-trophy mr-1"></i> {{ $activeIncentive->hasCashPayout() ? __('incentive.badge_challenge') : 'Incentive VIP' }}
</span>
<h4 class="inc-dash-title">
{{ $activeIncentive->getLang('name') ?: $activeIncentive->name }}
@ -25,7 +38,7 @@
@else
<div class="inc-dash-header-plain">
<span class="inc-dash-badge">
<i class="ion ion-md-trophy mr-1"></i> Incentive VIP
<i class="ion ion-md-trophy mr-1"></i> {{ $activeIncentive->hasCashPayout() ? __('incentive.badge_challenge') : 'Incentive VIP' }}
</span>
<h4 class="font-weight-bold mb-1">
{{ $activeIncentive->getLang('name') ?: $activeIncentive->name }}
@ -51,8 +64,14 @@
<div class="inc-dash-stat">
<span
class="inc-dash-stat-value">{{ number_format($incentiveParticipant->total_points, 0, ',', '.') }}</span>
<span class="inc-dash-stat-label">{{ __('incentive.points_short') }}</span>
<span class="inc-dash-stat-label">{{ $activeIncentive->ranksByPartnerCount() ? __('incentive.new_partners_short') : __('incentive.points_short') }}</span>
</div>
@if ($dashPayout)
<div class="inc-dash-stat">
<span class="inc-dash-stat-value">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($dashPayout['total']) }}</span>
<span class="inc-dash-stat-label">{{ __('incentive.payout_so_far') }}</span>
</div>
@endif
<div class="inc-dash-stat">
@if ($incentiveParticipant->is_qualified)
<span class="inc-dash-qual-yes">{{ __('incentive.qualified') }}</span>
@ -80,7 +99,7 @@
@endif
@if ($activeIncentive->isActive())
<button type="button" class="btn inc-dash-btn-notice" data-toggle="modal"
data-target="#incParticipateModal">
data-target="#incParticipateModal{{ $dashSuffix }}">
<i class="ion ion-md-checkmark-circle mr-1"></i>
{{ __('incentive.dash_notice_btn') }}
</button>
@ -145,12 +164,12 @@
{{-- Teilnahme-Modal --}}
@if (!$hasConfirmedDash && $activeIncentive->isActive())
<div class="modal fade" id="incParticipateModal" tabindex="-1" role="dialog"
aria-labelledby="incParticipateModalLabel" aria-hidden="true">
<div class="modal fade" id="incParticipateModal{{ $dashSuffix }}" tabindex="-1" role="dialog"
aria-labelledby="incParticipateModalLabel{{ $dashSuffix }}" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header" style="background: linear-gradient(135deg, #6b7758, #4a5340);">
<h5 class="modal-title text-white" id="incParticipateModalLabel">
<h5 class="modal-title text-white" id="incParticipateModalLabel{{ $dashSuffix }}">
<i class="ion ion-md-trophy mr-2" style="color: #d7d700;"></i>
{{ __('incentive.dash_modal_title') }}
</h5>
@ -158,7 +177,7 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="incDashParticipateForm"
<form id="incDashParticipateForm{{ $dashSuffix }}"
action="{{ route('user_incentive_participate', [$activeIncentive->slug]) }}" method="POST">
@csrf
<div class="modal-body">
@ -174,14 +193,14 @@
@if ($activeIncentive->getLang('terms'))
<div class="card mb-4" style="border: 1px solid #e0e0d8;">
<div class="card-header py-2 px-3" style="cursor: pointer; background: #f4f5f0;"
data-toggle="collapse" data-target="#dashTermsCollapse">
data-toggle="collapse" data-target="#dashTermsCollapse{{ $dashSuffix }}">
<div class="d-flex align-items-center">
<i class="ion ion-md-document mr-2" style="color: #6b7758;"></i>
<strong class="small">{{ __('incentive.terms') }}</strong>
<i class="ion ion-md-chevron-down ml-auto text-muted"></i>
</div>
</div>
<div id="dashTermsCollapse" class="collapse">
<div id="dashTermsCollapse{{ $dashSuffix }}" class="collapse">
<div class="card-body small"
style="max-height: 280px; overflow-y: auto; line-height: 1.6;">
{!! $activeIncentive->getLang('terms') !!}
@ -191,12 +210,12 @@
@endif
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="dashAcceptTerms"
<input type="checkbox" class="custom-control-input" id="dashAcceptTerms{{ $dashSuffix }}"
name="accept_terms" value="1" required>
<label class="custom-control-label" for="dashAcceptTerms">
<label class="custom-control-label" for="dashAcceptTerms{{ $dashSuffix }}">
{{ __('incentive.accept_terms') }}
@if ($activeIncentive->getLang('terms'))
(<a href="#dashTermsCollapse" data-toggle="collapse"
(<a href="#dashTermsCollapse{{ $dashSuffix }}" data-toggle="collapse"
class="text-muted">{{ __('incentive.show_terms') }}</a>)
@endif
</label>
@ -217,6 +236,9 @@
</div>
@endif
@endforeach
@if ($dashIncentives->isNotEmpty())
<style>
.inc-dash-notice {
background: rgba(215, 185, 0, 0.10);

View file

@ -290,15 +290,15 @@
<div>{{ __('navigation.downloadcenter') }}</div>
</a>
</li>
@php $activeIncentive = \App\Models\Incentive::active()->first(); @endphp
@if ($activeIncentive)
<li class="sidenav-item{{ Request::is('incentive/*') ? ' active' : '' }}">
<a href="{{ route('user_incentive_teaser', [$activeIncentive->slug]) }}"
@php $sidenavIncentives = \App\Models\Incentive::activeForNavigation(); @endphp
@foreach ($sidenavIncentives as $sidenavIncentive)
<li class="sidenav-item{{ Request::is('incentive/' . $sidenavIncentive->slug . '*') ? ' active' : '' }}">
<a href="{{ route('user_incentive_teaser', [$sidenavIncentive->slug]) }}"
class="sidenav-link"><i class="text-secondary sidenav-icon ion ion-md-trophy"></i>
<div>{{ __('navigation.incentive') }}</div>
<div>{{ $sidenavIncentives->count() > 1 ? ($sidenavIncentive->getLang('name') ?: $sidenavIncentive->name) : __('navigation.incentive') }}</div>
</a>
</li>
@endif
@endforeach
@endif
@endif

View file

@ -0,0 +1,7 @@
{{-- Praemien-Hinweise in der Punkte-Erklaerung (nur bei Geldpraemie). Erwartet $incentive. --}}
@if ((float) $incentive->amount_per_partner > 0)
<li>{{ __('incentive.payout_per_partner_text', ['amount' => \App\Services\Incentive\IncentivePayoutService::formatAmount((float) $incentive->amount_per_partner)]) }}</li>
@endif
@if ($incentive->rankBonuses() !== [])
<li>{{ __('incentive.payout_top_bonus_text', ['n' => count($incentive->rankBonuses()), 'list' => collect($incentive->rankBonuses())->map(fn ($amount, $rank) => __('incentive.payout_rank_label', ['n' => $rank]).' '.\App\Services\Incentive\IncentivePayoutService::formatAmount($amount))->implode(', ')]) }}</li>
@endif

View file

@ -0,0 +1,31 @@
{{-- Praemien-Kachel einer Recruiting-Challenge (ersetzt die Mindestqualifikation, wenn keine gefordert ist). Erwartet $incentive. --}}
<div class="card inc-info-tile shadow-sm mb-4" id="incentive-payout-tile">
<div class="card-body">
<div class="d-flex align-items-start">
<div class="inc-tile-icon inc-tile-icon-plane mr-3">
<i class="ion ion-md-cash"></i>
</div>
<div>
<h6 class="font-weight-bold mb-2">{{ __('incentive.payout_tile_title') }}</h6>
<ul class="list-unstyled mb-2">
@if ((float) $incentive->amount_per_partner > 0)
<li class="mb-1">
<strong style="color: #6b7758;">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount((float) $incentive->amount_per_partner) }}</strong>
{{ __('incentive.payout_per_partner_label') }}
</li>
@endif
@foreach ($incentive->rankBonuses() as $bonusRank => $bonusAmount)
<li>
<strong style="color: #6b7758;">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($bonusAmount) }}</strong>
{{ __('incentive.payout_rank_bonus_label', ['n' => $bonusRank]) }}
</li>
@endforeach
</ul>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.payout_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>

View file

@ -31,21 +31,36 @@
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.partners') }}</h5>
<h2 class="font-weight-bold {{ $participant->qualified_partners >= $incentive->min_direct_partners ? 'text-success' : '' }}">
{{ $participant->qualified_partners }}/{{ $incentive->min_direct_partners }}
{{ $participant->qualified_partners }}@if ($incentive->min_direct_partners > 0)/{{ $incentive->min_direct_partners }}@endif
</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.abos') }}</h5>
<h2 class="font-weight-bold {{ $participant->qualified_abos >= $incentive->min_customer_abos ? 'text-success' : '' }}">
{{ $participant->qualified_abos }}/{{ $incentive->min_customer_abos }}
</h2>
@if ($incentive->hasCashPayout() && !$incentive->usesAbos())
@php $detailPayout = (new \App\Services\Incentive\IncentivePayoutService)->forParticipant($incentive, $participant); @endphp
<div class="col-md-3">
<div class="card text-center" id="incentive-payout-card">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.payout_so_far') }}</h5>
<h2 class="font-weight-bold">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($detailPayout['total']) }}</h2>
@if ($detailPayout['rank_bonus'] > 0)
<small class="text-muted">{{ __('incentive.payout_incl_bonus', ['amount' => \App\Services\Incentive\IncentivePayoutService::formatAmount($detailPayout['rank_bonus'])]) }}</small>
@endif
</div>
</div>
</div>
</div>
@else
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title text-muted">{{ __('incentive.abos') }}</h5>
<h2 class="font-weight-bold {{ $participant->qualified_abos >= $incentive->min_customer_abos ? 'text-success' : '' }}">
{{ $participant->qualified_abos }}/{{ $incentive->min_customer_abos }}
</h2>
</div>
</div>
</div>
@endif
</div>
{{-- Sektion A: Neupartner-Punkte --}}
@ -64,21 +79,23 @@
</div>
</div>
{{-- Sektion B: Kundenabo-Punkte --}}
<div class="card mb-4">
<div class="card-header">
<strong>{{ __('incentive.section_abos') }}</strong>
{{-- Sektion B: Kundenabo-Punkte (entfaellt bei reiner Recruiting-Challenge) --}}
@if ($incentive->usesAbos() || $abo_sources->isNotEmpty())
<div class="card mb-4">
<div class="card-header">
<strong>{{ __('incentive.section_abos') }}</strong>
</div>
<div class="card-body p-0">
@include('partials.incentive._source_table', [
'sources' => $abo_sources,
'type' => 'abo',
'label_header' => __('incentive.customer_abo'),
'date_header' => __('incentive.abo_date'),
'empty_message' => __('incentive.no_abos_yet'),
])
</div>
</div>
<div class="card-body p-0">
@include('partials.incentive._source_table', [
'sources' => $abo_sources,
'type' => 'abo',
'label_header' => __('incentive.customer_abo'),
'date_header' => __('incentive.abo_date'),
'empty_message' => __('incentive.no_abos_yet'),
])
</div>
</div>
@endif
{{-- Gesamtpunkte --}}
<div class="card border-primary">

View file

@ -490,9 +490,15 @@
</div>
@endif
<div class="stat-box">
<div class="stat-label">{{ __('incentive.total_points') }}</div>
<div class="stat-label">{{ $incentive->ranksByPartnerCount() ? __('incentive.new_partners_short') : __('incentive.total_points') }}</div>
<div class="stat-value">{{ number_format($participant->total_points, 0, ',', '.') }}</div>
</div>
@if ($participantPayout)
<div class="stat-box">
<div class="stat-label">{{ __('incentive.payout_so_far') }}</div>
<div class="stat-value">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($participantPayout['total']) }}</div>
</div>
@endif
<div class="mr-3">
@if ($participant->is_qualified)
<span class="badge-qual">{{ __('incentive.qualified') }}</span>
@ -560,6 +566,9 @@
</div>
</div>
@if ($incentive->hasCashPayout() && !$incentive->hasMinimumRequirements())
@include('user.incentive._payout_tile', ['incentive' => $incentive])
@else
{{-- Mindestqualifikation --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
@ -580,14 +589,15 @@
{{ __('incentive.min_abos_label') }}
</li>
</ul>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
@endif
{{-- Punkte-System --}}
<div class="inc-points-section mb-4">
@ -596,7 +606,7 @@
{{ __('incentive.section_points') }}
</h6>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="{{ $incentive->usesAbos() ? 'col-md-6 mb-3 mb-md-0' : 'col-md-12' }}">
<div class="inc-point-card inc-point-card-partner">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-person-add mr-1"></i>
@ -609,10 +619,14 @@
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_starter_package_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_partner_boost') }}</li>
@endif
@include('user.incentive._payout_bullets', ['incentive' => $incentive])
</ul>
</div>
</div>
@if ($incentive->usesAbos())
<div class="col-md-6">
<div class="inc-point-card inc-point-card-abo">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
@ -626,10 +640,13 @@
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_abo_direct') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
</ul>
</div>
</div>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_abo_boost') }}</li>
@endif
</ul>
</div>
</div>
@endif
</div>
</div>
@ -694,9 +711,15 @@
</div>
@endif
<div class="stat-box">
<div class="stat-label">{{ __('incentive.total_points') }}</div>
<div class="stat-label">{{ $incentive->ranksByPartnerCount() ? __('incentive.new_partners_short') : __('incentive.total_points') }}</div>
<div class="stat-value">{{ number_format($participant->total_points, 0, ',', '.') }}</div>
</div>
@if ($participantPayout)
<div class="stat-box">
<div class="stat-label">{{ __('incentive.payout_so_far') }}</div>
<div class="stat-value">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($participantPayout['total']) }}</div>
</div>
@endif
<div>
@if ($participant->is_qualified)
<span class="badge-qual">{{ __('incentive.qualified') }}</span>
@ -759,6 +782,9 @@
</div>
</div>
@if ($incentive->hasCashPayout() && !$incentive->hasMinimumRequirements())
@include('user.incentive._payout_tile', ['incentive' => $incentive])
@else
{{-- Mindestqualifikation --}}
<div class="card inc-info-tile shadow-sm mb-4">
<div class="card-body">
@ -779,14 +805,15 @@
{{ __('incentive.min_abos_label') }}
</li>
</ul>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
<p class="text-muted small mb-0">
<i class="ion ion-md-information-circle mr-1"></i>
{{ __('incentive.min_qual_ranking_hint') }}
</p>
</div>
</div>
</div>
</div>
@endif
{{-- Punkte-System --}}
<div class="inc-points-section mb-4">
@ -795,7 +822,7 @@
{{ __('incentive.section_points') }}
</h6>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="{{ $incentive->usesAbos() ? 'col-md-6 mb-3 mb-md-0' : 'col-md-12' }}">
<div class="inc-point-card inc-point-card-partner">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
<i class="ion ion-md-person-add mr-1"></i>
@ -807,10 +834,15 @@
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
<li>{{ __('incentive.points_starter_package_label') }}</li>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_partner_boost') }}</li>
@endif
@include('user.incentive._payout_bullets', ['incentive' => $incentive])
</ul>
</div>
</div>
@if ($incentive->usesAbos())
<div class="col-md-6">
<div class="inc-point-card inc-point-card-abo">
<h6 class="font-weight-bold mb-1" style="font-size: .9rem;">
@ -823,10 +855,13 @@
</div>
<ul class="pl-3 mb-0">
<li>{{ __('incentive.points_onetime_label') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
</ul>
</div>
</div>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_abo_boost') }}</li>
@endif
</ul>
</div>
</div>
@endif
</div>
</div>
@ -929,9 +964,16 @@
<tr>
<th style="width: 60px;">{{ __('incentive.rank') }}</th>
<th>{{ __('incentive.consultant') }}</th>
<th class="text-right">{{ __('incentive.total_points') }}</th>
<th class="text-center">{{ __('incentive.partners') }}</th>
<th class="text-center">{{ __('incentive.abos') }}</th>
<th class="text-right">{{ $incentive->ranksByPartnerCount() ? __('incentive.new_partners_short') : __('incentive.total_points') }}</th>
@if (!$incentive->ranksByPartnerCount())
<th class="text-center">{{ __('incentive.partners') }}</th>
@endif
@if ($incentive->usesAbos())
<th class="text-center">{{ __('incentive.abos') }}</th>
@endif
@if ($incentive->hasCashPayout())
<th class="text-right">{{ __('incentive.payout_column') }}</th>
@endif
<th class="text-center">{{ __('incentive.status') }}</th>
</tr>
</thead>
@ -978,18 +1020,32 @@
<td class="text-right">
<strong>{{ number_format($p->total_points, 0, ',', '.') }}</strong>
</td>
<td class="text-center">
{{ $p->qualified_partners }}/{{ $incentive->min_direct_partners }}
@if ($p->qualified_partners >= $incentive->min_direct_partners)
<span class="check-ok">&#10003;</span>
@endif
</td>
<td class="text-center">
{{ $p->qualified_abos }}/{{ $incentive->min_customer_abos }}
@if ($p->qualified_abos >= $incentive->min_customer_abos)
<span class="check-ok">&#10003;</span>
@endif
</td>
@if (!$incentive->ranksByPartnerCount())
<td class="text-center">
{{ $p->qualified_partners }}@if ($incentive->min_direct_partners > 0)/{{ $incentive->min_direct_partners }}@endif
@if ($incentive->min_direct_partners > 0 && $p->qualified_partners >= $incentive->min_direct_partners)
<span class="check-ok">&#10003;</span>
@endif
</td>
@endif
@if ($incentive->usesAbos())
<td class="text-center">
{{ $p->qualified_abos }}@if ($incentive->min_customer_abos > 0)/{{ $incentive->min_customer_abos }}@endif
@if ($incentive->min_customer_abos > 0 && $p->qualified_abos >= $incentive->min_customer_abos)
<span class="check-ok">&#10003;</span>
@endif
</td>
@endif
@if ($incentive->hasCashPayout())
@php $rowPayout = $rankingPayouts[$p->id] ?? null; @endphp
<td class="text-right">
@if ($rowPayout && $rowPayout['total'] > 0)
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($rowPayout['total']) }}
@else
&mdash;
@endif
</td>
@endif
<td class="text-center">
@if ($isWinner)
<span class="badge-winner">{{ __('incentive.winner') }}</span>

View file

@ -515,25 +515,54 @@
</div>
</div>
{{-- Mindestqualifikation --}}
<div class="col-md-4 mb-3 mb-md-0">
<div class="card info-tile h-100 shadow-sm">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-plane">
<i class="ion ion-md-airplane"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.section_min_qual') }}</h5>
<div class="mb-3">
<div class="tile-value">{{ $incentive->min_direct_partners }}</div>
<span class="tile-label">{{ __('incentive.min_partners_label') }}</span>
</div>
<div>
<div class="tile-value">{{ $incentive->min_customer_abos }}</div>
<span class="tile-label">{{ __('incentive.min_abos_label') }}</span>
@if ($incentive->hasCashPayout() && !$incentive->hasMinimumRequirements())
{{-- Praemie (Recruiting-Challenge) --}}
<div class="col-md-4 mb-3 mb-md-0">
<div class="card info-tile h-100 shadow-sm" id="incentive-payout-tile">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-plane">
<i class="ion ion-md-cash"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.payout_tile_title') }}</h5>
@if ((float) $incentive->amount_per_partner > 0)
<div class="mb-3">
<div class="tile-value">{{ \App\Services\Incentive\IncentivePayoutService::formatAmount((float) $incentive->amount_per_partner) }}</div>
<span class="tile-label">{{ __('incentive.payout_per_partner_label') }}</span>
</div>
@endif
@if ($incentive->rankBonuses() !== [])
<div>
<span class="tile-label d-block mb-1">{{ __('incentive.payout_top_bonus_title', ['n' => count($incentive->rankBonuses())]) }}</span>
@foreach ($incentive->rankBonuses() as $bonusRank => $bonusAmount)
<div><strong>{{ __('incentive.payout_rank_label', ['n' => $bonusRank]) }}:</strong>
{{ \App\Services\Incentive\IncentivePayoutService::formatAmount($bonusAmount) }}</div>
@endforeach
</div>
@endif
</div>
</div>
</div>
</div>
@else
{{-- Mindestqualifikation --}}
<div class="col-md-4 mb-3 mb-md-0">
<div class="card info-tile h-100 shadow-sm">
<div class="card-body text-center py-4">
<div class="tile-icon tile-icon-plane">
<i class="ion ion-md-airplane"></i>
</div>
<h5 class="font-weight-bold mb-3">{{ __('incentive.section_min_qual') }}</h5>
<div class="mb-3">
<div class="tile-value">{{ $incentive->min_direct_partners }}</div>
<span class="tile-label">{{ __('incentive.min_partners_label') }}</span>
</div>
<div>
<div class="tile-value">{{ $incentive->min_customer_abos }}</div>
<span class="tile-label">{{ __('incentive.min_abos_label') }}</span>
</div>
</div>
</div>
</div>
@endif
{{-- Punkte-System --}}
<div class="col-md-4">
@ -550,13 +579,15 @@
</span>
<span class="tile-label d-block">{{ __('incentive.points_partners_title') }}</span>
</div>
<div>
<span class="tile-value" style="color: #9a9a00;">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</span>
<span class="tile-label d-block">{{ __('incentive.points_abos_title') }}</span>
</div>
@if ($incentive->usesAbos())
<div>
<span class="tile-value" style="color: #9a9a00;">
{{ number_format($incentive->points_abo_onetime, 0, ',', '.') }}
<span class="small font-weight-normal">{{ __('incentive.points_short') }}</span>
</span>
<span class="tile-label d-block">{{ __('incentive.points_abos_title') }}</span>
</div>
@endif
</div>
</div>
</div>
@ -570,7 +601,7 @@
{{ __('incentive.section_points') }}
</h4>
<div class="row">
<div class="col-md-6 mb-3 mb-md-0">
<div class="{{ $incentive->usesAbos() ? 'col-md-6 mb-3 mb-md-0' : 'col-md-12' }}">
<div class="point-card point-card-partner">
<h6 class="font-weight-bold mb-2">
<i class="ion ion-md-person-add mr-1"></i>
@ -583,10 +614,14 @@
<ul>
<li>{{ __('incentive.teaser_partner_onetime_text') }}</li>
<li>{{ __('incentive.points_starter_package_label') }}</li>
<li>{{ __('incentive.points_partner_boost') }}</li>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_partner_boost') }}</li>
@endif
@include('user.incentive._payout_bullets', ['incentive' => $incentive])
</ul>
</div>
</div>
@if ($incentive->usesAbos())
<div class="col-md-6">
<div class="point-card point-card-abo">
<h6 class="font-weight-bold mb-2">
@ -600,10 +635,13 @@
<ul>
<li>{{ __('incentive.teaser_abo_onetime_text') }}</li>
<li>{{ __('incentive.points_abo_direct') }}</li>
<li>{{ __('incentive.points_abo_boost') }}</li>
@if ($incentive->includesSalesPoints())
<li>{{ __('incentive.points_abo_boost') }}</li>
@endif
</ul>
</div>
</div>
@endif
</div>
</div>