Statistiken/Abo: Kundenfeedback 2026-07-24 komplett umgesetzt + Steuern: DATEV-Fixes 08+09/2025 (Sammel-Commit Branch-WIP)

Feedback-Umsetzung (dev/2026-07-22/feedback-analyse-2026-07-24.md, alle 10 Punkte):
- VIP-Statistik: eigener Berater ('ich', Linie 0) in Neue-Kundenabos- und beiden Top-10-Listen; Top-10-Umsatz sortiert nach abgerechneten Ist-Punkten + Abo-Anzahl-Spalte; Fußnote nicht-payline-wirksame Punkte (SalesVolumePoints::nonPaylinePoints)
- Führungskräfte-Liste: beide Sichten 'direkt' (an nächster FK abgeschnitten, additiv) und 'gesamt' (komplette Downline), Gesamt-Spalte nach Kundenzählweise
- Team-Abos: je Ebene 'abgerechnet (Monat)' und 'bis zum 25. erwartet'; Payline-Karte mit Teamabo-/Kundenabo-Zählern
- Incentive-Live-Ranking: Qualifizierte zuerst nummeriert (fixt auch isWinner rank<=max_winners)
- Porto: Netto-/Brutto-Kennzeichnung (Berater netto, Endkunden inkl. MwSt., portal_view-Flag; auch CRM-/Shop-Warenkorb + Checkout), Yard::shipping_tax-Fix
- Struktur-Ansicht: Abo-Badges 'A' (eigenes Abo) und '(n)' (Kundenabos) je Teampartner, gebatcht
- Abo-Löschung: Softdelete setzt active=0 (UserAbo deleting-Hook) + Bereinigungs-Migration für gelöschte-aber-aktive Abos
- Doku: technische Analyse + Kundinnen-Feedback-Dokument unter dev/2026-07-22/
- Tests: IncentiveRankingTest, TeamStructureAboBadgesTest, YardShippingTaxTest (neu); BackofficeStatisticsListsTest, TeamAboOverviewTest, PaylineForecastServiceTest, AboAdminDeleteTest, AboOneTimeViewTest erweitert

Außerdem enthalten (Branch-WIP vor dem Feedback): DATEV-Abgleich-Fixes 08+09/2025, USt-ID-Live-Validierung sowie restliche unkommittete Stände (u. a. Abo-Pause-Sprachkeys).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-07-24 13:51:16 +00:00
parent c06740b9be
commit d4e9d108ad
137 changed files with 7610 additions and 2955 deletions

View file

@ -4,9 +4,14 @@ namespace App\Http\Controllers\User;
use App\Exports\UserTeamExport;
use App\Http\Controllers\Controller;
use App\Models\UserAbo;
use App\Models\UserBusiness;
use App\Models\UserLevel;
use App\Models\UserSalesVolume;
use App\Services\Abo\PaylineForecastService;
use App\Services\Abo\TeamAboOverviewService;
use App\Services\AboHelper;
use App\Services\AboOrderCart;
use App\Services\BusinessPlan\ExportBot;
use App\Services\BusinessPlan\TreeCalcBot;
use App\Services\BusinessPlan\TreeCalcBotOptimized;
@ -16,6 +21,7 @@ use App\Services\LevelReportService;
use App\Services\NextLevelBadgeHelper;
use App\Services\TranslationHelper;
use App\User;
use Illuminate\Http\RedirectResponse;
use Maatwebsite\Excel\Facades\Excel;
use Request;
@ -318,6 +324,9 @@ class TeamController extends Controller
})
->addColumn('is_qual_kp', function ($teamUser) {
$user = User::find($teamUser->user_id);
if ($user === null) {
return '-';
}
return TreeHelperOptimized::generateQualKPBadgeForUser($user, $this->month, $this->year);
})
@ -652,7 +661,7 @@ class TeamController extends Controller
$currentLevel = $user->user_level;
// Lade alle aktiven User Level, sortiert nach Position
$userLevels = \App\Models\UserLevel::where('active', true)
$userLevels = UserLevel::where('active', true)
->orderBy('pos', 'asc')
->get();
@ -706,7 +715,7 @@ class TeamController extends Controller
* Konsolidierte Team-Abo-Übersicht: Abos im Team nach Ebenen/Linien gruppiert,
* inkl. Eigen-Abos, Kundenabos und neuer Abos im gewählten Monat.
*/
public function showAbos(\App\Services\Abo\TeamAboOverviewService $overviewService)
public function showAbos(TeamAboOverviewService $overviewService, PaylineForecastService $paylineService)
{
$user = User::find(\Auth::user()->id);
$now = now();
@ -725,6 +734,7 @@ class TeamController extends Controller
'growthChart' => $this->buildGrowthChart($growth),
'growthYear' => $growthYear,
'filterYears' => HTMLHelper::getYearRange(2022),
'paylineAboTurnover' => $paylineService->aboTurnover($user, (int) $now->month, (int) $now->year),
];
return view('user.team.abos', $data);
@ -767,7 +777,7 @@ class TeamController extends Controller
* (`user_abos_team_show`) eingeflossen. Diese Route bleibt für Bookmarks/Altlinks
* bestehen und leitet dauerhaft dorthin weiter.
*/
public function showTeamCustomerAbos(): \Illuminate\Http\RedirectResponse
public function showTeamCustomerAbos(): RedirectResponse
{
return redirect()->route('user_abos_team_show');
}
@ -778,7 +788,7 @@ class TeamController extends Controller
public function detailAbo($id)
{
$user = User::find(\Auth::user()->id);
$user_abo = \App\Models\UserAbo::findOrFail($id);
$user_abo = UserAbo::findOrFail($id);
// Prüfe ob das Abo zu einem Team-Mitglied gehört (effizient via Sponsor-Kette)
if (! AboHelper::isUserInTeam($user->id, $user_abo->user_id)) {
@ -786,9 +796,9 @@ class TeamController extends Controller
}
// Lade Abo-Details (ähnlich wie AboController)
\App\Services\AboOrderCart::initYard($user_abo);
$customer_detail = \App\Services\AboOrderCart::getCustomerDetail();
\App\Services\AboOrderCart::makeOrderYard($user_abo);
AboOrderCart::initYard($user_abo);
$customer_detail = AboOrderCart::getCustomerDetail();
AboOrderCart::makeOrderYard($user_abo);
$data = [
'user_abo' => $user_abo,
@ -1071,7 +1081,7 @@ class TeamController extends Controller
{
$levels = [0 => 'Alle Level'];
$userLevels = \App\Models\UserLevel::orderBy('pos')->get(['id', 'name']);
$userLevels = UserLevel::orderBy('pos')->get(['id', 'name']);
foreach ($userLevels as $level) {
$levels[$level->id] = $level->name;
}