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:
parent
4899e3a890
commit
b28f51bf62
27 changed files with 1418 additions and 129 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Exports\xExport;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Incentive;
|
||||
use App\Models\IncentiveNewAbo;
|
||||
|
|
@ -12,8 +13,11 @@ use App\Models\UserAbo;
|
|||
use App\Models\UserAboOrder;
|
||||
use App\Models\UserSalesVolume;
|
||||
use App\Services\Incentive\IncentiveCalculationService;
|
||||
use App\Services\Incentive\IncentivePayoutService;
|
||||
use App\User;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Request;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
class IncentiveController extends Controller
|
||||
{
|
||||
|
|
@ -50,8 +54,14 @@ class IncentiveController extends Controller
|
|||
'min_direct_partners' => 'required|integer|min:0',
|
||||
'min_customer_abos' => 'required|integer|min:0',
|
||||
'max_winners' => 'required|integer|min:1',
|
||||
'amount_per_partner' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_1' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_2' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_3' => 'nullable|numeric|min:0|max:999999',
|
||||
'include_sales_points' => 'nullable|boolean',
|
||||
'status' => 'required|integer|in:0,1,2',
|
||||
]);
|
||||
$data = $this->normalizePayoutFields($data);
|
||||
|
||||
$data = array_merge($data, $this->extractTranslations());
|
||||
|
||||
|
|
@ -70,12 +80,52 @@ class IncentiveController extends Controller
|
|||
->orderByIncentiveLeaderboard()
|
||||
->get();
|
||||
|
||||
$payoutService = new IncentivePayoutService;
|
||||
$payoutByParticipantId = $incentive->hasCashPayout()
|
||||
? $participants->mapWithKeys(fn (IncentiveParticipant $participant) => [
|
||||
$participant->id => $payoutService->forParticipant($incentive, $participant),
|
||||
])
|
||||
: collect();
|
||||
|
||||
return view('admin.incentive.show', [
|
||||
'incentive' => $incentive,
|
||||
'participants' => $participants,
|
||||
'payoutByParticipantId' => $payoutByParticipantId,
|
||||
'payoutTotals' => $incentive->hasCashPayout()
|
||||
? $payoutService->totals($payoutService->rows($incentive))
|
||||
: null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auszahlungsliste der Geldpraemie als XLSX (Februar-Auszahlung der Recruiting-Challenge).
|
||||
*/
|
||||
public function payoutExport($id): BinaryFileResponse
|
||||
{
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
$service = new IncentivePayoutService;
|
||||
|
||||
$filename = 'incentive-'.$incentive->slug.'-auszahlung-'.now()->format('Y-m-d').'.xlsx';
|
||||
|
||||
return Excel::download(new xExport($service->exportRows($incentive), $service->exportHeaders()), $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leere Betragsfelder als NULL speichern, Checkbox als bool.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function normalizePayoutFields(array $data): array
|
||||
{
|
||||
foreach (['amount_per_partner', 'bonus_rank_1', 'bonus_rank_2', 'bonus_rank_3'] as $field) {
|
||||
$data[$field] = isset($data[$field]) && $data[$field] !== '' ? round((float) $data[$field], 2) : null;
|
||||
}
|
||||
$data['include_sales_points'] = filter_var($data['include_sales_points'] ?? false, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$incentive = Incentive::findOrFail($id);
|
||||
|
|
@ -102,8 +152,14 @@ class IncentiveController extends Controller
|
|||
'min_direct_partners' => 'required|integer|min:0',
|
||||
'min_customer_abos' => 'required|integer|min:0',
|
||||
'max_winners' => 'required|integer|min:1',
|
||||
'amount_per_partner' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_1' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_2' => 'nullable|numeric|min:0|max:999999',
|
||||
'bonus_rank_3' => 'nullable|numeric|min:0|max:999999',
|
||||
'include_sales_points' => 'nullable|boolean',
|
||||
'status' => 'required|integer|in:0,1,2',
|
||||
]);
|
||||
$data = $this->normalizePayoutFields($data);
|
||||
|
||||
$data = array_merge($data, $this->extractTranslations());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue