mivita/app/Console/Commands/TestGrowthBonusCalculation.php
Kevin 316a9b76d6 Pint: Formatierung des bisherigen Stands, ohne inhaltliche Aenderung
Die Dateien lagen im Arbeitsbaum formatiert und inhaltlich geaendert
zusammen. Dieser Commit enthaelt nur die Pint-Formatierung des alten
Stands, damit die folgenden Commits nur noch Inhalt zeigen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 15:39:17 +00:00

322 lines
12 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\UserBusiness;
use App\Services\BusinessPlan\BusinessUserItemOptimized;
use App\Services\BusinessPlan\GrowthBonusCalculator;
use App\Services\BusinessPlan\TreeCalcBotOptimized;
use App\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
class TestGrowthBonusCalculation extends Command
{
protected $signature = 'test:growth-bonus
{user_id : Die User-ID für den Test}
{--month=12 : Der Monat}
{--year=2025 : Das Jahr}
{--debug : Zeigt detaillierte Debug-Informationen}
{--from-db : Verwendet gespeicherte Daten und simuliert Berechnung}';
protected $description = 'Testet die Growth Bonus Berechnung für einen User';
public function handle()
{
$userId = $this->argument('user_id');
$month = (int) $this->option('month');
$year = (int) $this->option('year');
$debug = $this->option('debug');
$fromDb = $this->option('from-db');
$this->info('=== Growth Bonus Test ===');
$this->info("User: {$userId}, Monat: {$month}/{$year}");
$this->newLine();
// User laden
$user = User::find($userId);
if (! $user) {
$this->error("User {$userId} nicht gefunden!");
return 1;
}
$date = Carbon::createFromDate($year, $month, 1);
// Gespeicherte UserBusiness-Daten laden
$userBusiness = UserBusiness::where('user_id', $userId)
->where('month', $month)
->where('year', $year)
->first();
if ($userBusiness) {
$this->info('=== GESPEICHERTE DATEN (UserBusiness) ===');
$qualLevel = $userBusiness->qual_user_level;
$this->table(
['Feld', 'Wert'],
[
['user_id', $userBusiness->user_id],
['qual_user_level (Name)', $qualLevel['name'] ?? 'NULL'],
['Growth Bonus (aus qual_user_level)', $qualLevel['growth_bonus'] ?? 'NULL'],
['active_growth_bonus (gespeichert)', $userBusiness->active_growth_bonus ?? 'NULL'],
['commission_growth_total', $userBusiness->commission_growth_total ?? 0],
]
);
} else {
$this->warn("Keine gespeicherten UserBusiness-Daten für {$month}/{$year}");
if (! $fromDb) {
$this->info('Versuche Live-Berechnung...');
} else {
$this->error('--from-db erfordert gespeicherte Daten!');
return 1;
}
}
if ($fromDb && $userBusiness) {
$this->testFromDatabase($userId, $month, $year, $debug);
} else {
$this->testLiveCalculation($user, $date, $month, $year, $debug);
}
$this->newLine();
$this->info('=== TEST ABGESCHLOSSEN ===');
return 0;
}
private function testFromDatabase(int $userId, int $month, int $year, bool $debug): void
{
$this->newLine();
$this->info('=== ANALYSE AUS DATENBANK ===');
// Lade User und seine Firstlines
$userBusiness = UserBusiness::where('user_id', $userId)
->where('month', $month)
->where('year', $year)
->first();
$qualLevel = $userBusiness->qual_user_level;
$myGrowthBonus = (float) ($qualLevel['growth_bonus'] ?? 0);
$this->info("Mein Growth Bonus Anspruch: {$myGrowthBonus}%");
if ($myGrowthBonus <= 0) {
$this->warn('Kein Growth Bonus Anspruch!');
return;
}
// Lade Firstlines
$firstlineIds = User::where('m_sponsor', $userId)->pluck('id')->toArray();
$this->info('Anzahl Firstlines: '.count($firstlineIds));
$this->newLine();
$this->info('=== FIRSTLINE ANALYSE ===');
$totalExpectedGrowth = 0;
$problemsFound = false;
foreach ($firstlineIds as $flId) {
$flBusiness = UserBusiness::where('user_id', $flId)
->where('month', $month)
->where('year', $year)
->first();
if (! $flBusiness) {
continue;
}
$flQualLevel = $flBusiness->qual_user_level;
$flGrowthBonus = (float) ($flQualLevel['growth_bonus'] ?? 0);
$flLevelName = $flQualLevel['name'] ?? 'Kein Level';
$flTPSum = (float) ($flBusiness->sales_volume_points_TP_sum ?? 0);
if ($flTPSum <= 0) {
continue;
}
// Berechne erwartete Differenz
$expectedDiff = max(0, $myGrowthBonus - $flGrowthBonus);
$expectedCommission = round($flTPSum / 100 * $expectedDiff, 2);
// Problem-Erkennung: Wenn FL einen Growth Bonus hat aber wir
// trotzdem den vollen Betrag bekommen
$isPotentialProblem = $flGrowthBonus > 0 && $expectedDiff < $myGrowthBonus;
$this->info("--- Firstline User {$flId} ---");
$this->table(
['Feld', 'Wert'],
[
['Level erreicht', $flLevelName],
['Growth Bonus (FL)', $flGrowthBonus.'%'],
['Team-Punkte (TP_sum)', number_format($flTPSum, 0, ',', '.')],
['Mein Anspruch', $myGrowthBonus.'%'],
['Differenz (erwartet)', $expectedDiff.'%'],
['Erwartete Provision', number_format($expectedCommission, 2, ',', '.').' €'],
['ACHTUNG: Blockade?', $isPotentialProblem ? 'JA - FL sollte blockieren!' : 'Nein'],
]
);
if ($isPotentialProblem) {
$problemsFound = true;
$this->error(" ⚠️ User {$flId} hat {$flLevelName} ({$flGrowthBonus}%) erreicht!");
$this->error(" Differenz sollte nur {$expectedDiff}% sein, nicht {$myGrowthBonus}%!");
}
$totalExpectedGrowth += $expectedCommission;
}
$this->newLine();
$this->info('=== ZUSAMMENFASSUNG ===');
$this->info('Erwartete Growth Bonus Summe (mit korrekter Differenz): '.number_format($totalExpectedGrowth, 2, ',', '.').' €');
$this->info('Gespeicherte Growth Bonus Summe: '.number_format($userBusiness->commission_growth_total ?? 0, 2, ',', '.').' €');
if ($problemsFound) {
$this->newLine();
$this->error('⚠️ PROBLEME GEFUNDEN! Die Blockade durch qualifizierte Firstlines funktioniert möglicherweise nicht korrekt.');
}
// Detailanalyse: Wie wurde die Berechnung durchgeführt?
if ($debug) {
$this->newLine();
$this->info('=== DEBUG: Rekursive Volumen-Analyse ===');
$this->analyzeVolumeDistribution($userId, $month, $year, $myGrowthBonus, 1);
}
}
private function analyzeVolumeDistribution(int $userId, int $month, int $year, float $myPercent, int $depth = 1): array
{
if ($depth > 10) {
return ['0.0' => 0];
}
$userBusiness = UserBusiness::where('user_id', $userId)
->where('month', $month)
->where('year', $year)
->first();
if (! $userBusiness) {
return ['0.0' => 0];
}
$qualLevel = $userBusiness->qual_user_level;
$myProtection = (float) ($qualLevel['growth_bonus'] ?? 0);
$indent = str_repeat(' ', $depth);
// Eigenes Volumen (TP_sum - aber nur direkte Punkte, nicht Team)
// Bei gespeicherten Daten ist das schwer zu unterscheiden
// Vereinfachung: Für den Test nehmen wir TP_sum als Gesamt-Volumen
$this->line("{$indent}User {$userId}: Protection={$myProtection}%");
// Lade Kinder
$childIds = User::where('m_sponsor', $userId)->pluck('id')->toArray();
$volumes = [];
foreach ($childIds as $childId) {
$childBusiness = UserBusiness::where('user_id', $childId)
->where('month', $month)
->where('year', $year)
->first();
if (! $childBusiness) {
continue;
}
$childTP = (float) ($childBusiness->sales_volume_points_TP_sum ?? 0);
if ($childTP <= 0) {
continue;
}
$childQual = $childBusiness->qual_user_level;
$childProtection = (float) ($childQual['growth_bonus'] ?? 0);
$childLevelName = $childQual['name'] ?? 'Kein Level';
// Berechne Differenz
$diff = max(0, $myPercent - $childProtection);
$commission = round($childTP / 100 * $diff, 2);
$this->line("{$indent} └─ Child {$childId}: {$childLevelName}, Protection={$childProtection}%, TP={$childTP}");
$this->line("{$indent} Differenz: {$myPercent}% - {$childProtection}% = {$diff}%");
$this->line("{$indent} Provision: {$childTP} * {$diff}% = {$commission}€");
if ($childProtection > 0) {
$this->warn("{$indent} ⚠️ BLOCKADE durch {$childLevelName}!");
}
}
return $volumes;
}
private function testLiveCalculation(User $user, Carbon $date, int $month, int $year, bool $debug): void
{
$this->newLine();
$this->info('=== LIVE-BERECHNUNG ===');
// TreeCalcBot erstellen
$treeCalcBot = new TreeCalcBotOptimized($month, $year, 'member', true);
// BusinessUserItem erstellen
$businessUserItem = new BusinessUserItemOptimized($date, $treeCalcBot);
$businessUserItem->makeUserFromModel($user, true);
$businessUserItem->addUserID();
// Kinder laden
$businessUserItem->readParentsBusinessUsers(true, 0);
// Qualifikation berechnen
$businessUserItem->calcQualPP(true);
$qualUserLevel = $businessUserItem->getQualUserLevel();
$this->table(
['Feld', 'Wert'],
[
['user_id', $businessUserItem->user_id],
['isQualLevel()', $businessUserItem->isQualLevel() ? 'JA' : 'NEIN'],
['isQualificationCalculated()', $businessUserItem->isQualificationCalculated() ? 'JA' : 'NEIN'],
['getQualUserLevel() (Name)', $qualUserLevel['name'] ?? 'NULL'],
['Growth Bonus (qual_user_level)', $qualUserLevel['growth_bonus'] ?? 'NULL'],
['getActiveGrowthBonus()', $businessUserItem->getActiveGrowthBonus()],
['getQualifiedGrowthBonus()', $businessUserItem->getQualifiedGrowthBonus()],
]
);
$this->newLine();
$this->info('=== FIRSTLINES (Kinder) ===');
if (empty($businessUserItem->businessUserItems)) {
$this->warn('Keine Firstlines geladen!');
} else {
foreach ($businessUserItem->businessUserItems as $index => $childItem) {
$childQual = $childItem->getQualUserLevel();
$this->info('--- Firstline '.($index + 1).' ---');
$this->table(
['Feld', 'Wert'],
[
['user_id', $childItem->user_id],
['isQualLevel()', $childItem->isQualLevel() ? 'JA' : 'NEIN'],
['getQualUserLevel() (Name)', $childQual['name'] ?? 'NULL'],
['Growth Bonus (qual_user_level)', $childQual['growth_bonus'] ?? 'NULL'],
['getActiveGrowthBonus()', $childItem->getActiveGrowthBonus()],
['getQualifiedGrowthBonus()', $childItem->getQualifiedGrowthBonus()],
['sales_volume_points_TP_sum', $childItem->sales_volume_points_TP_sum ?? 0],
]
);
}
}
if ($qualUserLevel && ($qualUserLevel['growth_bonus'] ?? 0) > 0) {
$calculator = new GrowthBonusCalculator;
$qualData = (object) $qualUserLevel;
$totalGrowthBonus = $calculator->calculate($businessUserItem, $qualData);
$this->newLine();
$this->info("Berechneter Growth Bonus: {$totalGrowthBonus}");
}
}
}