39 KiB
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
// 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
// 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):
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_linesmüssen bereits berechnet sein (auscalculateUserPointsOptimized)
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)
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
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
namespace App\Services\BusinessPlan;
class GrowthBonusConfig
{
private const QUALIFICATIONS = [...];
private const CRITERIA = [...];
public static function getConfig(string $level): ?array;
public static function getAllLevels(): array;
public static function getPercent(string $level): float;
public static function getStartLine(string $level): int;
public static function getCriteria(string $level): array;
}
2. GrowthBonusCalculator.php
Pfad: /app/Services/BusinessPlan/GrowthBonusCalculator.php
Zweck:
- Hauptlogik für Tiefenbonus-Berechnung
- Separation of Concerns (aus TreeCalcBotOptimized ausgelagert)
Struktur:
<?php
namespace App\Services\BusinessPlan;
use Psr\Log\LoggerInterface;
class GrowthBonusCalculator
{
private GrowthBonusConfig $config;
private LoggerInterface $logger;
public function __construct(
?GrowthBonusConfig $config = null,
?LoggerInterface $logger = null
);
// Hauptmethode
public function calculateGrowthBonus(
BusinessUserItemOptimized $user
): array;
// Qualifikation ermitteln
public function determineQualificationLevel(
BusinessUserItemOptimized $user
): ?string;
// Hilfsmethoden
private function calculateGrowthBonusRecursive(...);
private function getSubUsersAtLine(...);
private function collectUsersAtLine(...);
}
Bestehende Dateien anpassen
1. BusinessUserItemOptimized.php
Neue Properties hinzufügen:
// Nach Zeile ~40 (bei anderen Properties)
private ?string $qualification_level = null;
private float $growth_bonus_percent = 0.0;
private int $growth_bonus_start_line = 0;
private float $growth_bonus_total = 0.0;
private array $growth_bonus_details = [];
private array $growth_bonus_breakaways = [];
Neue Getter-Methoden:
public function getGrowthBonusTotal(): float
{
return $this->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:
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:
// Nach Zeile ~35 (bei anderen Dependencies)
private GrowthBonusCalculator $growthBonusCalculator;
Constructor erweitern:
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():
/**
* 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:
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:
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:
/**
* 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:
Schema::table('user_business_structures', function (Blueprint $table) {
$table->json('growth_bonus_data')->nullable()->after('structure');
});
Beim Speichern der Struktur Growth Bonus mit speichern:
// 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
ALTER TABLE user_business_structures
ADD COLUMN growth_bonus_data JSON NULL
AFTER structure;
JSON-Struktur:
{
"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
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
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
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
// 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
// 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:
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:
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:
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:
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:
$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)
// 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)
<div class="growth-bonus-container">
<h2>Tiefenbonus (Growth Bonus)</h2>
<div class="bonus-summary">
<div class="qualification-badge {{ $qualification }}">
{{ ucfirst($qualification) }}
</div>
<div class="bonus-amount">
<span class="label">Gesamtbonus:</span>
<span class="amount">{{ number_format($bonusTotal, 2) }} €</span>
</div>
<div class="bonus-info">
<p>Dein Bonus-Prozentsatz: <strong>{{ $bonusPercent }}%</strong></p>
<p>Bonus gilt ab Ebene: <strong>{{ $startLine }}</strong></p>
</div>
</div>
<div class="bonus-details">
<h3>Details nach Ebenen</h3>
@foreach($detailsByLine as $line => $users)
<div class="line-section">
<h4>Ebene {{ $line }}</h4>
<table class="bonus-table">
<thead>
<tr>
<th>Partner</th>
<th>Qualifikation</th>
<th>Umsatz</th>
<th>Dein %</th>
<th>Deren %</th>
<th>Differenz</th>
<th>Bonus</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user['user_name'] }}</td>
<td>
<span class="badge {{ $user['qualification'] }}">
{{ ucfirst($user['qualification'] ?? 'Keine') }}
</span>
</td>
<td>{{ number_format($user['volume'], 2) }}</td>
<td>{{ $user['your_percent'] }}%</td>
<td>{{ $user['their_percent'] }}%</td>
<td>{{ $user['differential'] }}%</td>
<td class="bonus-amount">
{{ number_format($user['bonus_amount'], 2) }} €
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endforeach
</div>
@if(count($breakaways) > 0)
<div class="breakaways-section">
<h3>Breakaways (Blocker)</h3>
<p>Folgende Partner haben die gleiche oder höhere Qualifikation und blocken daher den Tiefenbonus in ihrem Zweig:</p>
<ul class="breakaway-list">
@foreach($breakaways as $breakaway)
<li>
<strong>{{ $breakaway['user_name'] }}</strong>
(Ebene {{ $breakaway['line'] }}) -
<span class="badge {{ $breakaway['qualification'] }}">
{{ ucfirst($breakaway['qualification']) }}
</span>
({{ $breakaway['percent'] }}%)
</li>
@endforeach
</ul>
</div>
@endif
</div>
Migration & Rollout-Plan
Phase 1: Vorbereitung (Woche 1)
-
Code Review dieses Konzepts
- Stakeholder-Abstimmung
- Anpassung von Prozentsätzen/Kriterien
- Freigabe für Entwicklung
-
Testdaten erstellen
- Realistische Test-Strukturen
- Edge Cases abdecken
- Performance-Test-Daten (10.000+ User)
Phase 2: Entwicklung (Woche 2-3)
- Tag 1-2: GrowthBonusConfig implementieren
- Tag 3-5: GrowthBonusCalculator implementieren
- Tag 6-7: Integration in TreeCalcBotOptimized
- Tag 8-9: BusinessUserItemOptimized erweitern
- Tag 10-11: Unit Tests schreiben
- Tag 12-14: Integration Tests & Bug Fixes
Phase 3: Testing (Woche 4)
-
Tag 1-3: QA Testing
- Manuelle Tests mit Test-Strukturen
- Edge Cases testen
- Performance-Tests
-
Tag 4-5: Bug Fixes & Optimierungen
Phase 4: Staging (Woche 5)
-
Migration auf Staging-Umgebung
- Database Migration ausführen
- Code deployen
- Monitoring einrichten
-
Testberechnungen mit echten Daten
- Vergangenen Monat berechnen
- Ergebnisse mit erwarteten Werten vergleichen
- Performance-Monitoring
Phase 5: Production Rollout (Woche 6)
-
Soft Launch
- Feature zunächst nur für Admins sichtbar
- Berechnung im Hintergrund laufen lassen
- Werte validieren
-
Full Launch
- Feature für alle User aktivieren
- Kommunikation an User
- Support-Team briefen
Monitoring & Logging
Key Metrics
// Growth Bonus Metriken
[
'calculation_time_ms' => 4521,
'users_processed' => 1247,
'users_with_bonus' => 487,
'total_bonus_amount' => 125430.50,
'breakaways_found' => 123,
'max_line_calculated' => 15,
'memory_used_mb' => 85.3,
]
Alert Thresholds
Performance Alerts:
- Berechnungszeit > 60 Sekunden
- Memory Usage > 90%
- Fehlerrate > 5%
Business Alerts:
- Unerwartete Bonus-Summe (z.B. > 2x des Vormonats)
- Zu viele Breakaways (> 50% der User)
- Qualifikationen fehlen für > 10% der User
Dokumentation für User
Help-Artikel: "Was ist der Tiefenbonus?"
Überschrift: Wie funktioniert der Tiefenbonus?
Text:
Der Tiefenbonus (auch Growth Bonus oder Infinity Bonus genannt) belohnt qualifizierte Partner für den Aufbau tiefer Strukturen.
Wann bekomme ich Tiefenbonus?
Ab der Bronze-Qualifikation erhältst du einen prozentualen Bonus auf Umsätze ab einer bestimmten Ebene:
- Bronze bis Diamant: Ab Ebene 7
- Platin*: Ab Ebene 8
- Platin***: Ab Ebene 9
Wie hoch ist der Bonus?
- Bronze: 1%
- Silber: 1,5%
- Gold: 2%
- Diamant: 2,5%
- Platin*: 3%
- Platin**: 3,5%
- Platin***: 4%
Was ist ein Breakaway?
Wenn ein Partner in deiner Downline die gleiche oder eine höhere Qualifikation erreicht als du, wird er zum "Breakaway". Ab diesem Punkt erhältst du nur noch die Differenz zwischen deinem und seinem Bonus-Prozentsatz.
Beispiel:
Du bist Gold (2%) und hast einen Bronze-Partner (1%) in Ebene 7. → Du erhältst 1% Differenz auf dessen Umsätze (2% - 1% = 1%)
Du bist Gold (2%) und hast einen Gold-Partner (2%) in Ebene 7. → Du erhältst NICHTS (2% - 2% = 0%) - Breakaway!
Risiken & Mitigation
Risiko 1: Performance-Probleme
Risiko: Berechnung dauert zu lange bei großen Strukturen
Mitigation:
- Caching von Qualifikationen
- Early Breakaway Detection
- Optional: Async-Processing via Queue
Risiko 2: Fehlerhafte Berechnungen
Risiko: Falsche Bonus-Beträge durch Bug in Differenz-Logik
Mitigation:
- Umfangreiche Unit Tests
- Integration Tests mit echten Daten
- Manual Testing mit bekannten Strukturen
- Soft Launch nur für Admins
Risiko 3: Dateninkonsistenz
Risiko: Qualifikationen nicht aktuell oder fehlerhaft
Mitigation:
- Validierung vor Berechnung
- Fallback-Werte bei fehlenden Daten
- Logging von Inkonsistenzen
- Retry-Mechanismus
Offene Fragen (vor Implementierung klären)
-
Qualifikationskriterien:
- Sind die Beispiel-Werte für min_kp und min_pp korrekt?
- Gibt es weitere Kriterien (z.B. Mindestanzahl Direkt-Partner)?
-
Umsatz-Basis:
- Wird Bonus auf
sales_volume_points_TP_sumberechnet? - Oder auf einen anderen Wert?
- Wird Bonus auf
-
Zeitraum:
- Gilt der Bonus rückwirkend für vergangene Monate?
- Ab wann startet die Berechnung?
-
Auszahlung:
- Wird der Bonus automatisch ausgezahlt?
- Oder nur angezeigt/berechnet?
-
Breakaway-Regel:
- Gilt Breakaway auch bei temporärer Qualifikation?
- Oder nur bei "gefestigter" Qualifikation?
-
Maximaler Bonus:
- Gibt es ein Cap (z.B. max. 10.000 € pro Monat)?
- Oder wirklich unbegrenzt?
-
Frontend:
- Soll Growth Bonus im Dashboard prominent angezeigt werden?
- Separate Seite oder Integration in bestehendes Dashboard?
Nächste Schritte
-
✅ Konzept-Review durch Stakeholder
- Geschäftslogik bestätigen
- Offene Fragen klären
- Go/No-Go Entscheidung
-
⏳ Freigabe für Entwicklung
- Ressourcen allokieren
- Sprint planen
- Tickets erstellen
-
⏳ Entwicklung starten
- GrowthBonusConfig
- GrowthBonusCalculator
- Integration in bestehenden Code
-
⏳ Testing & QA
- Unit Tests
- Integration Tests
- Performance Tests
-
⏳ Deployment
- Staging
- Production Soft Launch
- Full Launch
Zusammenfassung
Der Tiefenbonus ist ein komplexes Feature, das:
✅ Belohnt:
- Aufbau tiefer Strukturen
- Qualifikations-Erreichen
- Langfristiges Engagement
✅ Herausforderungen:
- Performance bei großen Strukturen
- Korrekte Differenz-Berechnung
- Breakaway-Logik
- UI/UX für komplexe Daten
✅ Technische Umsetzung:
- Neue Klassen (Config, Calculator)
- Erweiterung bestehender Klassen
- Umfangreiche Tests
- Monitoring & Logging
✅ Business Value:
- Motiviert Partner zu Struktur-Aufbau
- Belohnt Führungskräfte
- Differenzierung zu Wettbewerb
Status: ✅ Konzept vollständig - Bereit für Review
Autor: AI Assistant
Datum: 26. November 2025
Version: 1.0