Anerkennungen: firmenweite Monats-Ranglisten im Salescenter

- Neue Seite Anerkennungen (/user/recognitions, Menue unter News Archiv) fuer
  alle Berater mit aktivem Konto: oben die Anerkennungen des Vormonats in den
  fuenf Kategorien der Monatsmail, Filter zurueck bis Januar 2026; darunter
  Live-Ranglisten (Top 10 je Kategorie) des laufenden Monats, 15 Min. Cache.
- Kategorien: Top-Recruiter (ab 2 Neupartnern), neue Kunden-Abos,
  Kundenabo-Punkte, Kunden-Abos gesamt, Gesamtpunkte (1.000-Punkte-Shop,
  mit Platz 4). Regeln wie TopResultsService/VIP-Statistik, nur firmenweit.
- Firmenkonto (6) und Grosskundin (3) nie aufgefuehrt:
  config mivita.recognition_excluded_user_ids.
- Abgeschlossene Monate sind bis zum 3. des Folgemonats vorlaeufig und werden
  dann eingefroren: neue Tabelle recognition_snapshots, Command
  recognitions:freeze (taeglich 06:15, idempotent, holt offene Monate nach).
- September-Anerkennungen firmenweit geprueft und korrigiert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-10-09 13:13:22 +00:00
parent 9ad69a3218
commit 7bc551ca1f
22 changed files with 1595 additions and 0 deletions

View file

@ -0,0 +1,53 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Recognition\RecognitionFilterRequest;
use App\Services\Team\RecognitionService;
use Illuminate\View\View;
/**
* Anerkennungen im Salescenter: oben die Ranglisten des gewählten
* abgeschlossenen Monats (Standard: Vormonat), darunter der Live-Stand des
* laufenden Monats. Sichtbar für alle Berater mit aktivem Konto.
*/
class RecognitionController extends Controller
{
public function __construct(private RecognitionService $recognitions)
{
$this->middleware('active.account');
}
public function index(RecognitionFilterRequest $request): View
{
$closedMonths = RecognitionService::closedMonths();
$selected = $this->selectedMonth((string) $request->validated('period'), $closedMonths);
return view('user.recognitions.index', [
'periods' => $closedMonths,
'selected' => $selected,
'recognitions' => $selected
? $this->recognitions->recognitions($selected['month'], $selected['year'])
: null,
'live' => $this->recognitions->liveRankings((int) now()->month, (int) now()->year),
]);
}
/**
* Nur abgeschlossene Monate sind wählbar; alles andere fällt auf den Vormonat zurück.
*
* @param array<int, array{month: int, year: int}> $closedMonths
* @return array{month: int, year: int}|null
*/
private function selectedMonth(string $period, array $closedMonths): ?array
{
foreach ($closedMonths as $closedMonth) {
if ($period === sprintf('%d-%02d', $closedMonth['year'], $closedMonth['month'])) {
return $closedMonth;
}
}
return $closedMonths[0] ?? null;
}
}