mivita/dev/Growth-Bonus.md
2026-01-23 17:35:23 +01:00

1583 lines
39 KiB
Markdown

# 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
<?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
<?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:**
```php
// 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:**
```php
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:**
```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
<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)
1. **Code Review dieses Konzepts**
- Stakeholder-Abstimmung
- Anpassung von Prozentsätzen/Kriterien
- Freigabe für Entwicklung
2. **Testdaten erstellen**
- Realistische Test-Strukturen
- Edge Cases abdecken
- Performance-Test-Daten (10.000+ User)
---
### Phase 2: Entwicklung (Woche 2-3)
1. **Tag 1-2:** GrowthBonusConfig implementieren
2. **Tag 3-5:** GrowthBonusCalculator implementieren
3. **Tag 6-7:** Integration in TreeCalcBotOptimized
4. **Tag 8-9:** BusinessUserItemOptimized erweitern
5. **Tag 10-11:** Unit Tests schreiben
6. **Tag 12-14:** Integration Tests & Bug Fixes
---
### Phase 3: Testing (Woche 4)
1. **Tag 1-3:** QA Testing
- Manuelle Tests mit Test-Strukturen
- Edge Cases testen
- Performance-Tests
2. **Tag 4-5:** Bug Fixes & Optimierungen
---
### Phase 4: Staging (Woche 5)
1. **Migration auf Staging-Umgebung**
- Database Migration ausführen
- Code deployen
- Monitoring einrichten
2. **Testberechnungen mit echten Daten**
- Vergangenen Monat berechnen
- Ergebnisse mit erwarteten Werten vergleichen
- Performance-Monitoring
---
### Phase 5: Production Rollout (Woche 6)
1. **Soft Launch**
- Feature zunächst nur für Admins sichtbar
- Berechnung im Hintergrund laufen lassen
- Werte validieren
2. **Full Launch**
- Feature für alle User aktivieren
- Kommunikation an User
- Support-Team briefen
---
## Monitoring & Logging
### Key Metrics
```php
// 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)
1. **Qualifikationskriterien:**
- Sind die Beispiel-Werte für min_kp und min_pp korrekt?
- Gibt es weitere Kriterien (z.B. Mindestanzahl Direkt-Partner)?
2. **Umsatz-Basis:**
- Wird Bonus auf `sales_volume_points_TP_sum` berechnet?
- Oder auf einen anderen Wert?
3. **Zeitraum:**
- Gilt der Bonus rückwirkend für vergangene Monate?
- Ab wann startet die Berechnung?
4. **Auszahlung:**
- Wird der Bonus automatisch ausgezahlt?
- Oder nur angezeigt/berechnet?
5. **Breakaway-Regel:**
- Gilt Breakaway auch bei temporärer Qualifikation?
- Oder nur bei "gefestigter" Qualifikation?
6. **Maximaler Bonus:**
- Gibt es ein Cap (z.B. max. 10.000 € pro Monat)?
- Oder wirklich unbegrenzt?
7. **Frontend:**
- Soll Growth Bonus im Dashboard prominent angezeigt werden?
- Separate Seite oder Integration in bestehendes Dashboard?
---
## Nächste Schritte
1.**Konzept-Review durch Stakeholder**
- Geschäftslogik bestätigen
- Offene Fragen klären
- Go/No-Go Entscheidung
2.**Freigabe für Entwicklung**
- Ressourcen allokieren
- Sprint planen
- Tickets erstellen
3.**Entwicklung starten**
- GrowthBonusConfig
- GrowthBonusCalculator
- Integration in bestehenden Code
4.**Testing & QA**
- Unit Tests
- Integration Tests
- Performance Tests
5.**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