# Tiefenbonus (Infinity Differential Bonus) - Implementierungskonzept ## Übersicht Der Tiefenbonus (auch "Infinity Differential Bonus" genannt) ist ein unbegrenzter Tiefenbonus für qualifizierte Member, der durch gleichrangige oder höherrangige Downlines geblockt wird (Breakaway-Prinzip). --- ## Geschäftslogik ### Start-Ebenen nach Qualifikation Die Ebene, ab der der Tiefenbonus greift, variiert nach Qualifikationsstufe: | Qualifikation | Start-Ebene | Beschreibung | | ------------- | ----------- | ------------------------------ | | Bronze | 7 | Bonus ab Ebene 7 bis unendlich | | Silber | 7 | Bonus ab Ebene 7 bis unendlich | | Gold | 7 | Bonus ab Ebene 7 bis unendlich | | Diamant | 7 | Bonus ab Ebene 7 bis unendlich | | Platin\* | 8 | Bonus ab Ebene 8 bis unendlich | | Platin\*\* | 8 | Bonus ab Ebene 8 bis unendlich | | Platin\*\*\* | 9 | Bonus ab Ebene 9 bis unendlich | ### Bonus-Prozentsätze nach Qualifikation | Qualifikation | Bonus-Prozentsatz | Bemerkung | | ------------- | ----------------- | --------------------- | | Bronze | 1.0% | Einstiegslevel | | Silber | 1.5% | +0.5% zu Bronze | | Gold | 2.0% | +0.5% zu Silber | | Diamant | 2.5% | +0.5% zu Gold | | Platin\* | 3.0% | +0.5% zu Diamant | | Platin\*\* | 3.5% | +0.5% zu Platin\* | | Platin\*\*\* | 4.0% | Höchste Qualifikation | ### Differenzbonus-Logik (Breakaway-Prinzip) **Grundprinzip:** - Du erhältst den Bonus auf alle Umsätze in deinen qualifizierenden Ebenen - **ABER:** Wenn ein Downline-Member die gleiche oder höhere Qualifikation hat, wird er zum "Breakaway" - Ab diesem Breakaway-Punkt erhältst du nur noch die **Differenz** zwischen deinem und seinem Bonus-Prozentsatz **Formel:** ``` Dein Bonus = MAX(0, Dein Prozentsatz - Downline Prozentsatz) ``` **Beispiele:** #### Beispiel 1: Gold über Bronze ``` Du: Gold (2%) └─> Ebene 7: Bronze Member (1%) └─> Ebene 8-∞: Deren Team Dein Bonus auf Ebene 7+: - Auf Umsätze des Bronze Members: 2% - 1% = 1% (Differenz) - Bronze erhält seinen 1% (ab seiner Ebene 7) - Du erhältst die Differenz von 1% ``` #### Beispiel 2: Gold über Gold (Breakaway) ``` Du: Gold (2%) └─> Ebene 7: Gold Member (2%) └─> Ebene 8-∞: Deren Team Dein Bonus auf Ebene 7+: - Auf Umsätze des Gold Members: 2% - 2% = 0% (BREAKAWAY) - Der Gold Member erhält seine vollen 2% - Du erhältst NICHTS auf diesen Zweig ``` #### Beispiel 3: Diamant über Silber und Bronze ``` Du: Diamant (2.5%) ├─> Zweig A, Ebene 7: Silber (1.5%) │ └─> Deren Team → Du erhältst: 2.5% - 1.5% = 1% │ └─> Zweig B, Ebene 7: Bronze (1%) └─> Deren Team → Du erhältst: 2.5% - 1% = 1.5% ``` #### Beispiel 4: Mehrstufiger Breakaway ``` Du: Platin* (3%, Start Ebene 8) └─> Ebene 8: Gold (2%, Start Ebene 7) ├─> Ebene 9: Bronze (1%, Start Ebene 7) │ └─> Ebene 10-∞: Team │ - Gold erhält: 2% - 1% = 1% │ - Du erhältst: 3% - 2% = 1% (Differenz zu Gold) │ └─> Ebene 9: Platin* (3%, Start Ebene 8) └─> Ebene 10-∞: Team - Platin* erhält: 3% - Du erhältst: 3% - 3% = 0% (BREAKAWAY) ``` --- ## Algorithmus-Design ### 1. Datenstruktur #### Qualifikations-Konfiguration ```php // Neue Klasse: GrowthBonusConfig class GrowthBonusConfig { private const QUALIFICATIONS = [ 'bronze' => [ 'level' => 1, 'start_line' => 7, 'bonus_percent' => 1.0, ], 'silber' => [ 'level' => 2, 'start_line' => 7, 'bonus_percent' => 1.5, ], 'gold' => [ 'level' => 3, 'start_line' => 7, 'bonus_percent' => 2.0, ], 'diamant' => [ 'level' => 4, 'start_line' => 7, 'bonus_percent' => 2.5, ], 'platin1' => [ 'level' => 5, 'start_line' => 8, 'bonus_percent' => 3.0, ], 'platin2' => [ 'level' => 6, 'start_line' => 8, 'bonus_percent' => 3.5, ], 'platin3' => [ 'level' => 7, 'start_line' => 9, 'bonus_percent' => 4.0, ], ]; } ``` #### Erweiterung BusinessUserItemOptimized ```php // Neue Properties für BusinessUserItemOptimized private ?string $qualification_level; // 'bronze', 'silber', 'gold', etc. private float $growth_bonus_percent = 0.0; // Bonus-Prozentsatz private int $growth_bonus_start_line = 0; // Ab welcher Ebene private array $growth_bonus_details = []; // Detaillierte Berechnung ``` --- ### 2. Berechnungsalgorithmus #### Phase 1: Qualifikation ermitteln **Methode:** `determineQualificationLevel(BusinessUserItemOptimized $user): string` **Input:** - `$user->qual_kp` (Kundenpunkte) - `$user->qual_pp` (Payline-Punkte) - `$user->sales_volume_points_sum` (Gesamtumsatz) **Output:** - Qualifikationslevel als String ('bronze', 'silber', etc.) **Logik:** ``` Prüfe von oben nach unten (Platin*** → Bronze): 1. Erfüllt User die Kriterien für Platin***? JA → return 'platin3' NEIN → weiter 2. Erfüllt User die Kriterien für Platin**? JA → return 'platin2' NEIN → weiter 3. ... (alle Stufen durchgehen) 4. Erfüllt User mindestens Bronze? JA → return 'bronze' NEIN → return null (kein Growth Bonus) ``` **Qualifikationskriterien** (Beispielwerte, müssen angepasst werden): ```php private const QUALIFICATION_CRITERIA = [ 'bronze' => [ 'min_kp' => 1000, 'min_pp' => 2000, ], 'silber' => [ 'min_kp' => 2500, 'min_pp' => 5000, ], 'gold' => [ 'min_kp' => 5000, 'min_pp' => 15000, ], 'diamant' => [ 'min_kp' => 10000, 'min_pp' => 50000, ], 'platin1' => [ 'min_kp' => 20000, 'min_pp' => 100000, ], 'platin2' => [ 'min_kp' => 40000, 'min_pp' => 250000, ], 'platin3' => [ 'min_kp' => 80000, 'min_pp' => 500000, ], ]; ``` --- #### Phase 2: Tiefenbonus berechnen (Hauptalgorithmus) **Methode:** `calculateGrowthBonus(BusinessUserItemOptimized $user): array` **Voraussetzung:** - User muss mindestens Bronze-Qualifikation haben - `business_lines` müssen bereits berechnet sein (aus `calculateUserPointsOptimized`) **Algorithmus:** ``` 1. Qualifikation des aktuellen Users ermitteln └─> Wenn keine Qualifikation: RETURN (kein Bonus) 2. Start-Ebene und Bonus-Prozentsatz aus Config laden Beispiel: Gold → Start Ebene 7, Bonus 2% 3. Für jede Ebene >= Start-Ebene: 3.1 Durchlaufe alle Sub-User auf dieser Ebene 3.2 Für jeden Sub-User: a) Ermittle dessen Qualifikation b) Berechne Differenz-Prozentsatz: differential = Dein Prozentsatz - Sub-User Prozentsatz Wenn differential <= 0: → BREAKAWAY (Blocker) → Markiere diesen Zweig → Keine weiteren Berechnungen in diesem Zweig Sonst: → Berechne Bonus auf Umsatz: bonus_amount = Sub-User Umsatz * differential → Speichere Details: - Ebene - Sub-User ID - Umsatz - Differential % - Bonus-Betrag c) Wenn KEIN Breakaway: → Rekursiv in tiefere Ebenen dieses Zweigs → ABER: Übergebe als "Blocker-Prozentsatz" den höheren Wert zwischen: - Deinem Prozentsatz - Sub-User Prozentsatz 4. Summiere alle Bonus-Beträge 5. Return Detaillierte Ergebnisse: - total_growth_bonus (Gesamtbonus) - details_by_line (Array pro Ebene) - breakaway_users (Liste der Blocker) ``` --- #### Pseudo-Code (Detailliert) ```php function calculateGrowthBonus( BusinessUserItemOptimized $user, ?float $blockerPercent = null ): array { // 1. Qualifikation prüfen $userQual = $this->determineQualificationLevel($user); if (!$userQual) { return ['total' => 0, 'details' => []]; } // 2. Config laden $config = GrowthBonusConfig::get($userQual); $userPercent = $blockerPercent ?? $config['bonus_percent']; $startLine = $config['start_line']; $totalBonus = 0; $details = []; $breakawayUsers = []; // 3. Durchlaufe alle Ebenen ab Start-Ebene foreach ($user->business_lines as $lineNumber => $lineData) { if ($lineNumber < $startLine) { continue; // Noch nicht qualifizierend } // 4. Hole alle Sub-User auf dieser Ebene $subUsers = $this->getSubUsersAtLine($user, $lineNumber); foreach ($subUsers as $subUser) { // 5. Ermittle Sub-User Qualifikation $subUserQual = $this->determineQualificationLevel($subUser); $subUserPercent = $subUserQual ? GrowthBonusConfig::get($subUserQual)['bonus_percent'] : 0.0; // 6. Berechne Differenz $differential = $userPercent - $subUserPercent; if ($differential <= 0) { // BREAKAWAY - Blocker gefunden $breakawayUsers[] = [ 'user_id' => $subUser->user_id, 'line' => $lineNumber, 'qualification' => $subUserQual, 'percent' => $subUserPercent, ]; // STOP: Keine weiteren Berechnungen in diesem Zweig continue; } // 7. Berechne Bonus auf Sub-User Umsatz $subUserVolume = $subUser->sales_volume_points_TP_sum ?? 0; $bonusAmount = $subUserVolume * ($differential / 100); $totalBonus += $bonusAmount; $details[$lineNumber][] = [ 'user_id' => $subUser->user_id, 'user_name' => $subUser->b_user->full_name ?? 'Unknown', 'qualification' => $subUserQual, 'volume' => $subUserVolume, 'your_percent' => $userPercent, 'their_percent' => $subUserPercent, 'differential' => $differential, 'bonus_amount' => $bonusAmount, ]; // 8. Rekursiv in tiefere Ebenen (wenn kein Breakaway) $deeperBonus = $this->calculateGrowthBonusRecursive( $subUser, $lineNumber + 1, max($userPercent, $subUserPercent) // Höherer Wert als Blocker ); $totalBonus += $deeperBonus['total']; $details = array_merge_recursive($details, $deeperBonus['details']); $breakawayUsers = array_merge($breakawayUsers, $deeperBonus['breakaways']); } } return [ 'total' => $totalBonus, 'details' => $details, 'breakaways' => $breakawayUsers, 'user_qualification' => $userQual, 'user_percent' => $userPercent, 'start_line' => $startLine, ]; } ``` --- ### 3. Hilfsmethoden #### getSubUsersAtLine() **Zweck:** Holt alle direkten Sub-User auf einer bestimmten Ebene ```php private function getSubUsersAtLine( BusinessUserItemOptimized $user, int $lineNumber ): array { $subUsers = []; // Rekursive Suche durch businessUserItems $this->collectUsersAtLine( $user->businessUserItems, 1, // Aktuelle Ebene relativ zum User $lineNumber, $subUsers ); return $subUsers; } private function collectUsersAtLine( array $items, int $currentLine, int $targetLine, array &$result ): void { if ($currentLine > $targetLine) { return; // Zu tief } foreach ($items as $item) { if ($currentLine === $targetLine) { $result[] = $item; } else { // Rekursiv tiefer $this->collectUsersAtLine( $item->businessUserItems ?? [], $currentLine + 1, $targetLine, $result ); } } } ``` --- ## Code-Anpassungen ### Neue Dateien erstellen #### 1. GrowthBonusConfig.php **Pfad:** `/app/Services/BusinessPlan/GrowthBonusConfig.php` **Zweck:** - Zentrale Konfiguration für Qualifikationsstufen - Bonus-Prozentsätze - Start-Ebenen **Struktur:** ```php growth_bonus_total; } public function getGrowthBonusDetails(): array { return $this->growth_bonus_details; } public function getQualificationLevel(): ?string { return $this->qualification_level; } public function setGrowthBonusData(array $data): void { $this->growth_bonus_total = $data['total'] ?? 0.0; $this->growth_bonus_details = $data['details'] ?? []; $this->growth_bonus_breakaways = $data['breakaways'] ?? []; $this->qualification_level = $data['user_qualification'] ?? null; $this->growth_bonus_percent = $data['user_percent'] ?? 0.0; $this->growth_bonus_start_line = $data['start_line'] ?? 0; } ``` **In `__get()` Methode erweitern:** ```php public function __get(string $name) { switch ($name) { // ... bestehende Cases ... case 'growth_bonus_total': return $this->growth_bonus_total; case 'growth_bonus_details': return $this->growth_bonus_details; case 'qualification_level': return $this->qualification_level; case 'growth_bonus_percent': return $this->growth_bonus_percent; case 'growth_bonus_start_line': return $this->growth_bonus_start_line; case 'growth_bonus_breakaways': return $this->growth_bonus_breakaways; // ... rest ... } } ``` --- #### 2. TreeCalcBotOptimized.php **Neue Property hinzufügen:** ```php // Nach Zeile ~35 (bei anderen Dependencies) private GrowthBonusCalculator $growthBonusCalculator; ``` **Constructor erweitern:** ```php public function __construct( int $month, int $year, string $initFrom = 'member', bool $forceLiveCalculation = false, ?BusinessUserRepository $repository = null, ?TreeHtmlRenderer $renderer = null, ?LoggerInterface $logger = null, ?GrowthBonusCalculator $growthBonusCalculator = null // NEU ) { // ... bestehender Code ... // NEU $this->growthBonusCalculator = $growthBonusCalculator ?? new GrowthBonusCalculator(null, $this->logger); } ``` **Neue Methode nach `calculateAllBusinessUsers()`:** ```php /** * Berechnet Growth Bonus für alle Business-Users * WICHTIG: Muss NACH calculateAllBusinessUsers() aufgerufen werden */ private function calculateGrowthBonusForAll(): void { $startTime = microtime(true); $this->logger->info("Starting Growth Bonus calculation for " . count($this->businessUsers) . " business users"); $totalBonusAmount = 0; $usersWithBonus = 0; foreach ($this->businessUsers as $businessUser) { try { // Berechne Growth Bonus $bonusData = $this->growthBonusCalculator->calculateGrowthBonus($businessUser); // Speichere in BusinessUser $businessUser->setGrowthBonusData($bonusData); if ($bonusData['total'] > 0) { $totalBonusAmount += $bonusData['total']; $usersWithBonus++; $this->logger->info( "Growth Bonus for user {$businessUser->user_id}: " . number_format($bonusData['total'], 2) . " ({$bonusData['user_qualification']})" ); } } catch (\Exception $e) { $this->logger->error( "Error calculating growth bonus for user {$businessUser->user_id}: " . $e->getMessage() ); continue; } } $endTime = microtime(true); $executionTime = round(($endTime - $startTime) * 1000, 2); $this->logger->info( "Completed Growth Bonus calculations in {$executionTime}ms. " . "Users with bonus: {$usersWithBonus}. " . "Total bonus amount: " . number_format($totalBonusAmount, 2) ); } ``` **In `buildFreshStructure()` erweitern:** ```php private function buildFreshStructure(): void { $this->loadRootUsers(); $this->loadParentsUsers(); $this->loadParentlessUsers(); // Berechne Punkte und Qualifikationen $this->calculateAllBusinessUsers(); $this->calculateAllParentlessUsers(); // NEU: Growth Bonus berechnen $this->calculateGrowthBonusForAll(); } ``` **In `initBusinesslUserDetail()` erweitern:** ```php public function initBusinesslUserDetail(User $user, bool $forceLiveCalculation = false): void { try { // ... bestehender Code bis calcQualPP() ... $this->businessUser->calcQualPP(); // NEU: Growth Bonus berechnen $bonusData = $this->growthBonusCalculator->calculateGrowthBonus($this->businessUser); $this->businessUser->setGrowthBonusData($bonusData); $this->logger->info( "Growth Bonus for user {$user->id}: " . number_format($bonusData['total'], 2) ); } catch (\Exception $e) { // ... bestehender Error Handler ... } } ``` **Neue öffentliche Getter-Methode:** ```php /** * Gibt detaillierte Growth Bonus Informationen zurück */ public function getGrowthBonusDetails(): array { if (!$this->businessUser) { return []; } return [ 'total' => $this->businessUser->growth_bonus_total, 'qualification' => $this->businessUser->qualification_level, 'bonus_percent' => $this->businessUser->growth_bonus_percent, 'start_line' => $this->businessUser->growth_bonus_start_line, 'details_by_line' => $this->businessUser->growth_bonus_details, 'breakaways' => $this->businessUser->growth_bonus_breakaways, ]; } ``` --- #### 3. UserBusinessStructure Model **Migration erstellen für neue Felder:** ```php Schema::table('user_business_structures', function (Blueprint $table) { $table->json('growth_bonus_data')->nullable()->after('structure'); }); ``` **Beim Speichern der Struktur Growth Bonus mit speichern:** ```php // In der Methode die die Struktur speichert $structure->growth_bonus_data = json_encode([ 'total_bonus_paid' => $totalBonusAmount, 'users_with_bonus' => $usersWithBonus, 'calculation_date' => now(), // Optional: Details pro User 'user_bonuses' => $this->collectGrowthBonusData(), ]); ``` --- ## Datenbank-Schema ### Erweiterte Tabelle: user_business_structures ```sql ALTER TABLE user_business_structures ADD COLUMN growth_bonus_data JSON NULL AFTER structure; ``` **JSON-Struktur:** ```json { "total_bonus_paid": 15750.5, "users_with_bonus": 47, "calculation_date": "2025-11-26 10:30:00", "user_bonuses": [ { "user_id": 123, "qualification": "gold", "bonus_total": 1250.75, "start_line": 7, "lines_calculated": [7, 8, 9, 10, 11], "breakaways": [456, 789] }, { "user_id": 456, "qualification": "silber", "bonus_total": 450.25, "start_line": 7, "lines_calculated": [7, 8, 9], "breakaways": [] } ] } ``` --- ## Testing-Strategie ### Unit Tests #### 1. GrowthBonusConfig Test ```php class GrowthBonusConfigTest extends TestCase { /** @test */ public function it_returns_correct_config_for_bronze() { $config = GrowthBonusConfig::getConfig('bronze'); $this->assertEquals(7, $config['start_line']); $this->assertEquals(1.0, $config['bonus_percent']); } /** @test */ public function it_returns_correct_config_for_platin3() { $config = GrowthBonusConfig::getConfig('platin3'); $this->assertEquals(9, $config['start_line']); $this->assertEquals(4.0, $config['bonus_percent']); } } ``` --- #### 2. GrowthBonusCalculator Test ```php class GrowthBonusCalculatorTest extends TestCase { /** @test */ public function it_calculates_differential_bonus_correctly() { // Arrange: Gold User mit Bronze in Ebene 7 $goldUser = $this->createUserWithQualification('gold', [ 'qual_kp' => 5000, 'qual_pp' => 15000, ]); $bronzeSubUser = $this->createSubUser($goldUser, 7, 'bronze', [ 'sales_volume' => 10000, ]); $calculator = new GrowthBonusCalculator(); // Act $result = $calculator->calculateGrowthBonus($goldUser); // Assert // Gold 2% - Bronze 1% = 1% Differenz // 10000 * 0.01 = 100 $this->assertEquals(100, $result['total']); $this->assertEquals('gold', $result['user_qualification']); $this->assertEquals(2.0, $result['user_percent']); } /** @test */ public function it_stops_at_breakaway() { // Arrange: Gold User mit Gold Sub-User (gleiche Qualifikation) $goldUser = $this->createUserWithQualification('gold'); $goldSubUser = $this->createSubUser($goldUser, 7, 'gold', [ 'sales_volume' => 20000, ]); $calculator = new GrowthBonusCalculator(); // Act $result = $calculator->calculateGrowthBonus($goldUser); // Assert: Keine Bonus (Breakaway) $this->assertEquals(0, $result['total']); $this->assertCount(1, $result['breakaways']); $this->assertEquals($goldSubUser->user_id, $result['breakaways'][0]['user_id']); } /** @test */ public function it_calculates_multi_level_differential() { // Arrange: Diamant mit mehreren Sub-Usern $diamantUser = $this->createUserWithQualification('diamant'); // Ebene 7: Bronze (10000 Umsatz) $bronzeUser = $this->createSubUser($diamantUser, 7, 'bronze', [ 'sales_volume' => 10000, ]); // Ebene 7: Silber (15000 Umsatz) $silberUser = $this->createSubUser($diamantUser, 7, 'silber', [ 'sales_volume' => 15000, ]); // Ebene 8: Unter Bronze (5000 Umsatz) $subBronze = $this->createSubUser($bronzeUser, 1, null, [ 'sales_volume' => 5000, ]); $calculator = new GrowthBonusCalculator(); // Act $result = $calculator->calculateGrowthBonus($diamantUser); // Assert // Diamant = 2.5% // Bronze: (2.5% - 1.0%) * 10000 = 150 // Silber: (2.5% - 1.5%) * 15000 = 150 // Sub unter Bronze: (2.5% - 1.0%) * 5000 = 75 (Bronze blockt teilweise) // Total: 375 $this->assertEquals(375, $result['total']); } } ``` --- ### Integration Tests ```php class TreeCalcBotGrowthBonusIntegrationTest extends TestCase { /** @test */ public function it_calculates_growth_bonus_in_full_structure() { // Arrange: Erstelle komplette Test-Struktur $this->seedTestStructure(); // Act $calc = new TreeCalcBotOptimized(11, 2025); $calc->initStructureAdmin(); // Assert $users = $calc->getItems(); $totalGrowthBonus = 0; foreach ($users as $user) { $totalGrowthBonus += $user->growth_bonus_total; } $this->assertGreaterThan(0, $totalGrowthBonus); $this->assertDatabaseHas('user_business_structures', [ 'month' => 11, 'year' => 2025, 'completed' => true, ]); } } ``` --- ## Performance-Überlegungen ### 1. Komplexität **Worst Case:** - O(n \* d) wobei n = Anzahl User, d = Durchschnittliche Tiefe - Bei 10.000 Usern und Tiefe 15: ~150.000 Operationen **Optimierungen:** - Early Breakaway Detection (stoppt Zweig bei Blocker) - Memoization von Qualifikationen (nicht mehrfach berechnen) - Batch-Processing für große Strukturen --- ### 2. Caching-Strategie **Level 1: Qualifikations-Cache** ```php // In GrowthBonusCalculator private array $qualificationCache = []; private function determineQualificationLevel( BusinessUserItemOptimized $user ): ?string { $userId = $user->user_id; if (isset($this->qualificationCache[$userId])) { return $this->qualificationCache[$userId]; } $qual = $this->calculateQualification($user); $this->qualificationCache[$userId] = $qual; return $qual; } ``` **Level 2: Sub-User Cache** ```php // Cache für getSubUsersAtLine() private array $subUserCache = []; private function getSubUsersAtLine( BusinessUserItemOptimized $user, int $lineNumber ): array { $cacheKey = $user->user_id . '_' . $lineNumber; if (isset($this->subUserCache[$cacheKey])) { return $this->subUserCache[$cacheKey]; } $subUsers = $this->collectSubUsers($user, $lineNumber); $this->subUserCache[$cacheKey] = $subUsers; return $subUsers; } ``` --- ### 3. Memory-Management **Monitoring:** ```php private function calculateGrowthBonusForAll(): void { $startMemory = memory_get_usage(); foreach ($this->businessUsers as $businessUser) { // Berechnung... // Memory-Check alle 100 User if ($this->processedCount % 100 === 0) { $this->checkMemoryUsage('growthBonusCalculation'); } } $endMemory = memory_get_usage(); $memoryUsed = $this->formatBytes($endMemory - $startMemory); $this->logger->info("Growth Bonus calculation used: {$memoryUsed}"); } ``` --- ## Edge Cases & Fehlerbehandlung ### Edge Case 1: User ohne Sub-User **Problem:** User hat Qualifikation, aber keine Team-Mitglieder **Lösung:** ```php if (empty($user->businessUserItems)) { return [ 'total' => 0, 'details' => [], 'note' => 'No sub-users to calculate bonus from', ]; } ``` --- ### Edge Case 2: Zirkuläre Referenzen **Problem:** User A sponsort B, B sponsort C, C sponsort A (Daten-Fehler) **Lösung:** ```php private array $processingStack = []; private function calculateGrowthBonusRecursive( BusinessUserItemOptimized $user, ... ): array { if (in_array($user->user_id, $this->processingStack)) { $this->logger->error("Circular reference detected for user: {$user->user_id}"); return ['total' => 0, 'details' => []]; } $this->processingStack[] = $user->user_id; // Berechnung... array_pop($this->processingStack); } ``` --- ### Edge Case 3: Fehlende Qualifikationsdaten **Problem:** User hat keine qual_kp/qual_pp Werte **Lösung:** ```php private function determineQualificationLevel( BusinessUserItemOptimized $user ): ?string { $qualKp = $user->qual_kp ?? 0; $qualPp = $user->qual_pp ?? 0; if ($qualKp === 0 && $qualPp === 0) { $this->logger->warning( "User {$user->user_id} has no qualification data" ); return null; } // Berechnung... } ``` --- ### Edge Case 4: Start-Ebene nicht erreicht **Problem:** User hat Qualifikation, aber Struktur ist nicht tief genug **Lösung:** ```php $maxLine = max(array_keys($user->business_lines)); if ($maxLine < $config['start_line']) { return [ 'total' => 0, 'details' => [], 'note' => "Structure not deep enough (max: {$maxLine}, required: {$config['start_line']})", ]; } ``` --- ## UI/Frontend Integration ### Controller-Methode (Beispiel) ```php // In BusinessPointsController public function showGrowthBonus(Request $request) { $userId = auth()->id(); $month = $request->input('month', date('m')); $year = $request->input('year', date('Y')); $calc = new TreeCalcBotOptimized($month, $year, 'member'); $calc->initStructureUser($userId); $growthBonusDetails = $calc->getGrowthBonusDetails(); return view('business.growth-bonus', [ 'bonusTotal' => $growthBonusDetails['total'], 'qualification' => $growthBonusDetails['qualification'], 'bonusPercent' => $growthBonusDetails['bonus_percent'], 'startLine' => $growthBonusDetails['start_line'], 'detailsByLine' => $growthBonusDetails['details_by_line'], 'breakaways' => $growthBonusDetails['breakaways'], ]); } ``` --- ### View (Blade Template Beispiel) ```blade
Dein Bonus-Prozentsatz: {{ $bonusPercent }}%
Bonus gilt ab Ebene: {{ $startLine }}
| Partner | Qualifikation | Umsatz | Dein % | Deren % | Differenz | Bonus |
|---|---|---|---|---|---|---|
| {{ $user['user_name'] }} | {{ ucfirst($user['qualification'] ?? 'Keine') }} | {{ number_format($user['volume'], 2) }} | {{ $user['your_percent'] }}% | {{ $user['their_percent'] }}% | {{ $user['differential'] }}% | {{ number_format($user['bonus_amount'], 2) }} € |
Folgende Partner haben die gleiche oder höhere Qualifikation und blocken daher den Tiefenbonus in ihrem Zweig: