Hier ist die technische Zusammenfassung des Marketingplans (MIVITA) Das System ist eine Mischung aus **Unilevel-Plan** (feste % auf Ebenen) und **Differenz-Bonus** (Tiefenbonus ab einer gewissen Stufe) mit qualifikationsbedingten Struktur-Anforderungen. ### 1. Grundbegriffe & Variablen - **Punkte (Points):** Interne Währung (1 Punkt ≈ 1 Euro). - **PV (Persönliches Volumen):** - `Eigene Bestellungen` - `+` Bestellungen von Kunden (Shop) - `+` Starterkits von direkten Firstlines (je 100 Punkte) - `+` Abos (Eigene + Kunden + Firstlines). - **Payline (Gruppenvolumen):** Die Summe der Punkte aus dem Team in einer bestimmten Tiefe (je nach Level 3 bis 8 Ebenen). - **Wichtig:** Wenn `PV > Erforderliches PV`, fließen die _überschüssigen_ PV-Punkte in die Payline-Berechnung mit ein. - **Schecksicherung:** Ein gesonderter PV-Wert, der erreicht sein muss, um Provisionen zu erhalten (meist identisch mit Qualifikations-PV, Ausnahme: _Bronze Member_ = 690 Punkte). --- ### 2. Die Level-Logik (State Machine) Ein User hat einen `current_rank`. Um diesen zu erreichen, müssen im aktuellen Monat folgende Bedingungen (`WHERE`) erfüllt sein. Die Prüfung erfolgt von oben (höchstes Level) nach unten. | Level ID | Level Name | Min. PV | Min. Payline Points | Payline Tiefe (für Quali) | Struktur-Voraussetzung (Linien) | | :------- | :------------------- | :--------------------------- | :------------------ | :------------------------ | :-------------------------------------------------- | | 1 | Junior Berater | 150 | 0 | - | - | | 2 | Aktiv Junior Berater | 250 | 500 | 3 Ebenen | - | | 3 | Berater | 350 | 1.000 | 4 Ebenen | - | | 4 | Aktiv Berater | 450 | 2.500 | 6 Ebenen\* | - | | 5 | Vertriebspartner | 600 | 5.000 | 6 Ebenen | - | | 6 | Vertriebsleiter | 600 | 9.000 | 6 Ebenen | **Sonderregel:** 2-Monats-Bestätigung (siehe unten) | | 7 | Bronze Member | 600 (**690** zur Auszahlung) | 18.000 | 6 Ebenen | - | | 8 | Silber Member | 600 | 30.000 | 6 Ebenen | - | | 9 | Gold Member | 600 | 50.000 | 6 Ebenen | - | | 10 | Diamant Member | 600 | 100.000 | 6 Ebenen | - | | 11 | Platin Member\* | 600 | 250.000 | 7 Ebenen | Min. 1 Linie mit "Gold Member" | | 12 | Platin Member\*\* | 600 | 500.000 | 7 Ebenen | Min. 1x Gold-Linie AND 1x Diamant-Linie | | 13 | Platin Member\*\*\* | 600 | 1.000.000 | 8 Ebenen | Min. 1x Gold, 1x Diamant, 1x Platin\* | _Hinweis zu Aktiv Berater:_ Im Text steht Header "6 Ebenen", im Fließtext "5 Ebenen". Da ab Aktiv Berater die Payline Sprünge macht, würde ich sicherheitshalber 6 Ebenen scannen oder das im Business klären. Im Zweifel gilt die höhere Zahl für die Qualifikation. --- ### 3. Provisions-Berechnung (Commission Engine) Wenn der Status feststeht, wird die Provision berechnet. Es gibt drei Töpfe: #### A. Sofortrabatt / Eigenumsatz-Rückvergütung Basierend auf dem Status bekommt der Berater % auf seinen **eigenen** Umsatz zurückerstattet (bzw. als Rabatt beim Einkauf). - Junior: 20% - Aktiv Junior: 25% - Berater: 30% - Aktiv Berater: 31% - Vertriebspartner: 32% - Vertriebsleiter bis Platin: 33% #### B. Unilevel Provision (Passive Teamprovision) Feste Prozentsätze auf den Umsatz der Downline, begrenzt auf eine bestimmte Tiefe. | Level | Ebene 1 | Ebene 2 | Ebene 3 | Ebene 4 | Ebene 5 | Ebene 6 | Ebene 7 | Ebene 8 | | :--------------- | :------ | :------ | :------ | :------ | :------ | :------ | :------ | :------ | | Junior | 6% | 3% | 1% | - | - | - | - | - | | Aktiv Junior | 6% | 4% | 2% | - | - | - | - | - | | Berater | 6% | 5% | 3% | 2% | - | - | - | - | | Aktiv Berater | 6% | 5% | 4% | 2% | 1% | - | - | - | | Vertriebspartner | 6% | 6% | 5% | 3% | 2% | 1% | - | - | | Vertriebsleiter | 6% | 6% | 6% | 4% | 2% | 1% | - | - | | Bronze | 7% | 7% | 7% | 5% | 3% | 3% | - | - | | Silber | 8,5% | 8,5% | 8,5% | 5,5% | 3,5% | 3,5% | - | - | | Gold | 9% | 9% | 9% | 6% | 4% | 4% | - | - | | Diamant | 9,5% | 9,5% | 9,5% | 6,5% | 4,5% | 4,5% | - | - | | Platin\* | 10% | 10% | 10% | 7% | 6% | 5% | 4% | - | | Platin\*\* | 10,5% | 10,5% | 10,5% | 7,5% | 6,5% | 5,5% | 4,5% | - | | Platin\*\*\* | 11% | 11% | 11% | 8% | 8% | 7% | 6% | 5% | #### C. Tiefenbonus (Infinity Differential Bonus) Ab **Bronze Member** gibt es einen Bonus, der theoretisch "unendlich" tief geht, aber durch gleichrangige Downlines geblockt wird (Differenzbonus). - **Start-Ebene:** - Bronze bis Diamant: Bonus gilt ab Ebene 7. - Platin\*: Bonus gilt ab Ebene 8. - Platin\*\*\*: Bonus gilt ab Ebene 9. - **Bonus-Höhe (Maximal):** - Bronze: 1% - Silber: 1,5% - Gold: 2% - Diamant: 2,5% - Platin\*: 3% - Platin\*\*: 3,5% - Platin\*\*\*: 4% - **Logik (Differenz):** - Du bist Gold (Anspruch 2%). - In einer Linie ist unter dir ein Bronze (Anspruch 1%). - Auf dessen Umsätze (ab seiner Ebene 7) erhältst du nur noch die Differenz: `2% - 1% = 1%`. - Wenn unter dir jemand auch Gold ist: `2% - 2% = 0%` (Breakaway). --- ### 4. Spezielle Developer-Regeln (Edge Cases) Hier sind die Fallstricke für deinen Algorithmus: 1. **Vertriebsleiter "2-Monats-Regel" (Seite 7):** - Wenn man das Level das _erste Mal_ erreicht: Man bekommt den Titel, wird aber provisionstechnisch noch wie das vorherige Level (Vertriebspartner) abgerechnet. - Erst bei Wiederholung (2. Mal erreicht): Abrechnung nach Vertriebsleiter-Sätzen. - _Implikation DB:_ Du brauchst im User-Model Felder wie `vertriebsleiter_first_reached_date` oder einen History-Log, um zu prüfen, ob es das erste Mal ist. 2. **Struktur-Check für Platin (Seite 17+):** - Für Platin reicht Umsatz nicht. Es muss geprüft werden: `Has user in direct line (any depth within line?) with Rank >= Gold`. - Text sagt: "Linie mit einem Gold Member". Das bedeutet meistens: Irgendwo in diesem Bein (Ast) muss einer den Status haben, nicht zwingend die Firstline. _Das ist rechenintensiv!_ 3. **Jahresbonus (Platin\***):\*\* - 1% vom gesamten Firmenumsatz wird gesammelt. - Ausgeschüttet im Januar des Folgejahres an alle Platin\*\*\*. - Aufgeteilt nach Anzahl der Qualifizierten ("Share"-System). 4. **Überschuss-Punkte:** - Formel für Payline: `SUM(Downline Points) + MAX(0, Own_PV - Required_PV)`. - Beispiel Aktiv Junior: Braucht 250 PV. Hat er 300 PV, zählen 50 Punkte zusätzlich in seine Payline-Summe. # TreeCalcBotOptimized - Technische Dokumentation ## Übersicht Die `TreeCalcBotOptimized` Klasse ist eine optimierte Implementierung zur Berechnung und Verwaltung von Multi-Level-Marketing (MLM) Business-Strukturen. Sie berechnet Punkte, Qualifikationen und Provisionen für ein hierarchisches Vertriebsnetzwerk. ## Hauptzweck Der Algorithmus: - Lädt und verarbeitet MLM-Strukturbäume (Sponsor-Ketten) - Berechnet Verkaufsvolumen-Punkte über mehrere Ebenen (Lines) - Ermittelt Qualifikationsstufen basierend auf Performance - Unterstützt Caching über gespeicherte Strukturen für Performance - Bietet Live-Berechnungen für aktuelle Daten --- ## Architektur & Design Patterns ### Design Patterns 1. **Repository Pattern**: `BusinessUserRepository` trennt Datenzugriff von Businesslogik 2. **Renderer Pattern**: `TreeHtmlRenderer` trennt Darstellung von Berechnung 3. **Dependency Injection**: Alle Dependencies können injiziert werden (Testbarkeit) 4. **Iterator Pattern**: Stack-basierte Traversierung für Memory-Effizienz ### Optimierungen - **N+1 Problem gelöst**: Eager Loading von Relations im Repository - **Memory-Management**: Stack-basierte Algorithmen statt Rekursion - **Lazy Loading**: Strukturen werden nur bei Bedarf berechnet - **Caching**: Gespeicherte Strukturen in `UserBusinessStructure` Tabelle --- ## Datenstruktur ### Haupt-Properties ```php private stdClass $date; // Berechnungszeitraum (Monat/Jahr) private string $initFrom; // Kontext: 'member' oder 'admin' private array $businessUsers; // Root-Level Business-User (Top Sponsoren) private array $parentless; // User ohne Sponsor (Waisen) private ?BusinessUserItemOptimized $businessUser; // Einzelner Detail-User private ?BusinessUserItemOptimized $sponsor; // Sponsor des Users private array $processedUserIds; // Verhindert Duplikate/Endlosschleifen private bool $forceLiveCalculation; // Erzwingt Neuberechnung (ignoriert Cache) ``` ### Abhängigkeiten ```php private BusinessUserRepository $repository; // Datenzugriff (DB-Queries) private TreeHtmlRenderer $renderer; // HTML-Ausgabe private LoggerInterface $logger; // Logging & Monitoring ``` --- ## Initialisierung & Konstruktor ### Constructor ```php __construct( int $month, // Monat (1-12) int $year, // Jahr (2020 - aktuelles Jahr + 1) string $initFrom = 'member', // Kontext bool $forceLiveCalculation = false, // Cache-Bypass ?BusinessUserRepository $repository = null, ?TreeHtmlRenderer $renderer = null, ?LoggerInterface $logger = null ) ``` **Ablauf:** 1. **Validierung**: `validateInput()` prüft Monat (1-12) und Jahr (2020-aktuell+1) 2. **Datumsinitialisierung**: Erstellt `stdClass` mit Start-/Enddatum des Monats 3. **Dependency Injection**: Injiziert oder erstellt Dependencies --- ## Haupt-Workflows ### 1. Admin-Struktur Initialisierung **Methode:** `initStructureAdmin(bool $check = true, bool $forceLiveCalculation = false)` **Zweck:** Lädt die komplette MLM-Struktur für Admin-Übersichten **Ablauf:** ``` ┌─────────────────────────────────────┐ │ initStructureAdmin() │ └───────────┬─────────────────────────┘ │ ├─── forceLiveCalculation = true? │ └─> buildFreshStructure() ──────┐ │ │ └─── Stored Structure vorhanden? │ ├─> JA: loadStoredStructure() │ └─> NEIN: buildFreshStructure() │ │ ┌────────────────────────────────┘ │ v buildFreshStructure(): ├─> loadRootUsers() ├─> loadParentsUsers() ├─> loadParentlessUsers() ├─> calculateAllBusinessUsers() └─> calculateAllParentlessUsers() ``` **Details:** 1. **Gespeicherte Struktur** (`loadStoredStructure`): - Lädt aus `UserBusinessStructure` Tabelle - Validiert Vollständigkeit der Daten - Berechnet fehlende Werte nach 2. **Frische Struktur** (`buildFreshStructure`): - Lädt Root-User (Top-Sponsoren ohne eigenen Sponsor) - Lädt rekursiv alle Sub-Strukturen - Lädt parentlose User (Waisen) - Berechnet alle Punkte und Qualifikationen --- ### 2. User-Struktur Initialisierung **Methode:** `initStructureUser(int $userId, bool $forceLiveCalculation = false)` **Zweck:** Lädt die Struktur für einen spezifischen User (Member-Ansicht) **Ablauf:** ``` ┌─────────────────────────────────────┐ │ initStructureUser(userId) │ └───────────┬─────────────────────────┘ │ ├─> User laden (mit Relations) ├─> BusinessUserItem erstellen ├─> User zu processedUserIds hinzufügen │ ├─── forceLiveCalculation? │ ├─> JA: Live-Berechnung │ │ ├─> loadParentsUsers() │ │ ├─> loadSponsorUser() │ │ └─> calcQualPP() für alle │ │ │ └─> NEIN: Stored Structure? │ ├─> JA: loadStoredParentsUsers() │ └─> NEIN: wie Live-Berechnung │ └─> Fertig ``` **Besonderheiten:** - Lädt nur relevante Upline (Sponsoren-Kette) - Lädt Downline (Team-Struktur unter dem User) - Berechnet Qualifikationen nur bei Bedarf --- ### 3. Business-User Details **Methode:** `initBusinesslUserDetail(User $user, bool $forceLiveCalculation = false)` **Zweck:** Detaillierte Berechnung für einen einzelnen User **Ablauf:** ``` ┌─────────────────────────────────────┐ │ initBusinesslUserDetail(user) │ └───────────┬─────────────────────────┘ │ ├─> BusinessUserItem erstellen ├─> makeUserFromModel(user, forceLiveCalculation) ├─> checkSponsor(user) │ ├─── Daten gespeichert UND !forceLiveCalculation? │ └─> NEIN: │ ├─> readParentsBusinessUsers() (rekursive Struktur) │ ├─> calculateUserPointsOptimized() (Punkte in Linien) │ └─> calcQualPP() (Qualifikation) │ └─> Fertig ``` --- ## Berechnungsalgorithmen ### 1. Punkte-Berechnung (calculateUserPointsOptimized) **Zweck:** Berechnet Verkaufsvolumen-Punkte über alle Ebenen (Lines) **Problem:** Original-Code verwendete Rekursion → Stack Overflow bei großen Strukturen **Lösung:** Stack-basierter Depth-First Algorithmus **Algorithmus:** ``` Input: businessUserItems (Array von Sub-Usern) startLine (Start-Ebene, z.B. 1) businessUserToUpdate (Parent-User zum Updaten) Phase 1: SAMMELN (Depth-First Order) ┌────────────────────────────────────────┐ │ collectionStack = [alle Root-Items] │ └────────────┬───────────────────────────┘ │ v ┌─────────────────────────┐ │ While collectionStack: │ │ ├─> Item nehmen (FIFO) │ │ ├─> Zu processingStack │ │ └─> Kinder hinzufügen │ │ (in umgekehrter │ │ Reihenfolge) │ └─────────────────────────┘ Phase 2: SORTIEREN ┌────────────────────────────────────────┐ │ Sortiere processingStack nach Tiefe: │ │ Tiefste Items zuerst │ └────────────┬───────────────────────────┘ Phase 3: VERARBEITEN ┌────────────────────────────────────────┐ │ For each Item in processingStack: │ │ ├─> Business Line initialisieren │ │ ├─> Punkte aus sales_volume_points_TP │ │ ├─> addBusinessLinePoints(line, pts) │ │ └─> addTotalTP(pts) │ └────────────────────────────────────────┘ ``` **Beispiel:** ``` Struktur: A (Root) ├── B (Line 1) │ ├── D (Line 2) │ └── E (Line 2) └── C (Line 1) └── F (Line 2) Verarbeitungsreihenfolge (Depth-First): 1. D (Line 2, Depth 2) → 100 Punkte 2. E (Line 2, Depth 2) → 150 Punkte 3. B (Line 1, Depth 1) → 200 Punkte 4. F (Line 2, Depth 2) → 120 Punkte 5. C (Line 1, Depth 1) → 180 Punkte Ergebnis für User A: business_lines[1] = 380 Punkte (B + C) business_lines[2] = 370 Punkte (D + E + F) total_points_TP = 750 Punkte ``` **Kritische Aspekte:** - **Depth-First Order**: Wichtig für korrekte Punkteaggregation - **Duplikate verhindern**: `processedUserIds` Array - **Memory-Effizienz**: Stack statt Rekursion (kein Stack Overflow) --- ### 2. Qualifikations-Berechnung (calcQualPP) **Methode:** `BusinessUserItemOptimized::calcQualPP()` (delegiert) **Zweck:** Ermittelt die Qualifikationsstufe des Users **Berechnungsgrundlagen:** 1. **qual_kp** (Kunden-Punkte): - Direkte Käufe/Verkäufe des Users - Basis für persönliche Qualifikation 2. **qual_pp** (Payline-Punkte): - Summe der Punkte aus dem Team - Basis für Team-Qualifikation 3. **business_lines**: - Array mit Punkten pro Ebene (Line) - Line 1 = Direkte Partner - Line 2-5 = Weitere Ebenen - Line 6+ = Growth Bonus Ebenen **Qualifikationskriterien** (typisch im MLM): - Bronze: 1.000 KP + 2.000 PP - Silber: 2.500 KP + 5.000 PP - Gold: 5.000 KP + 15.000 PP - Platin: 10.000 KP + 50.000 PP - (Werte beispielhaft, tatsächliche Logik in BusinessUserItemOptimized) **Zusätzliche Berechnungen:** - `next_qual_user_level`: Nächste erreichbare Qualifikationsstufe - `next_can_user_level`: Potenzielle Qualifikationsstufe - Verwendet für UI (grüne Pfeile in Struktur-Ansicht) --- ### 3. Growth Bonus Berechnung **Methode:** `getGrowthBonus()` **Zweck:** Berechnet Bonuszahlungen ab Ebene 6 **Logik:** ```php if (count(business_lines) > 6) { return array_slice(business_lines, 6); // Ebenen 7, 8, 9, ... } ``` **Verwendung:** - Infinity-Bonus für Top-Leader - Zahlt auf alle Ebenen ab 6+ - Nur bei entsprechender Qualifikation --- ## Datenfluss & Performance ### Root-User Laden (loadRootUsers) **Optimierung:** Eager Loading im Repository ```php // Repository lädt mit Relations: $users = User::whereNull('sponsor_id') ->with(['business_user', 'orders', 'subscriptions']) ->get(); // Vermeidet N+1 Queries foreach ($users as $user) { // Alle Relations bereits geladen $businessUserItem->makeUserFromModel($user); } ``` **Memory-Monitoring:** - Prüft Speicherverbrauch vor jedem User - Warnung bei > 80% Memory-Limit - Erzwingt Garbage Collection bei > 90% --- ### Parent-User Laden (loadParentsUsers) **Rekursiver Aufbau:** ``` Root-User A ├─> readParentsBusinessUsers() ├─> Lädt alle direkten Kinder (Line 1) └─> Für jedes Kind: readParentsBusinessUsers() └─> Lädt Enkel (Line 2) └─> Rekursiv in die Tiefe ``` **Performance-Aspekte:** - Kann bei großen Strukturen lange dauern - Verwendet `processedUserIds` zur Duplikats-Vermeidung - Unterbricht bei Zirkelbezügen (Sponsor-Loops) --- ### Parentless-User (loadParentlessUsers) **Zweck:** Findet "Waisen" (User ohne Sponsor) **Abfrage:** ```php // Alle User die nicht in der Haupt-Struktur sind $parentlessUsers = User::whereNotIn('id', $processedUserIds) ->whereNull('sponsor_id') ->orWhereHas('sponsor', function($q) { $q->where('deleted_at', '!=', null); }) ->get(); ``` **Use Cases:** - Fehlerhafte Datenimporte - Gelöschte Sponsoren - System-Test-Accounts --- ## Caching & Persistierung ### Gespeicherte Strukturen (UserBusinessStructure) **Tabellen-Schema:** ```php user_business_structures { id month year structure (JSON) // Root-User mit komplettem Tree parentless (JSON) // Parentlose User completed (bool) // Struktur vollständig berechnet created_at updated_at } ``` **Vorteile:** - Admin-Struktur muss nur 1x pro Monat berechnet werden - Massive Performance-Verbesserung (Sekunden statt Minuten) - Konsistente Daten für Reports **Nachteile:** - Daten können veraltet sein - Bei Änderungen: forceLiveCalculation = true nötig --- ### Validierung gespeicherter Daten **Methode:** `validateAndRecalculateIfNeeded()` **Prüfung:** ```php function isBusinessUserIncomplete($businessUser): bool { // Prüfe grundlegende Felder $salesVolumeSum = $businessUser->sales_volume_points_sum; $qualKp = $businessUser->qual_kp; // Prüfe Level-Qualifikationsdaten $nextQualUserLevel = $businessUser->next_qual_user_level; $nextCanUserLevel = $businessUser->next_can_user_level; // Unvollständig wenn Daten fehlen return ($salesVolumeSum === null || $salesVolumeSum === 0) && ($qualKp === null || $qualKp === 0) || (empty($nextQualUserLevel) && empty($nextCanUserLevel)); } ``` **Nachberechnung:** - Nur für unvollständige User - Erhält Integrität bei partiellen Caches - Logged für Monitoring --- ## Fehlerbehandlung & Logging ### Logging-Strategien **Info-Level:** ```php "Loading stored business structure for 11/2025" "Building fresh business structure for 11/2025" "Loaded 127 root users. Memory used: 15.3 MB" ``` **Warning-Level:** ```php "User not found: 12345" "High memory usage: 85% (340 MB / 400 MB)" "Could not load sponsor for user 999" ``` **Error-Level:** ```php "Error initializing admin structure: Invalid month: 13" "Error calculating business user 456: Division by zero" ``` **Debug-Level:** ```php "Processed user 789 at line 3 with 250.5 points" "Loaded 15 parent users for user 321" ``` --- ### Exception-Handling **Strategie:** ```php try { $this->buildFreshStructure(); } catch (\Exception $e) { $this->logger->error("Error: " . $e->getMessage()); throw $e; // Re-throw für Controller } ``` **Partial Failures:** ```php foreach ($businessUsers as $user) { try { $user->calcQualPP(); } catch (\Exception $e) { $this->logger->error("Error for user {$user->id}"); continue; // Fahre fort mit nächstem User } } ``` --- ## Memory-Management ### Memory-Monitoring **Methode:** `checkMemoryUsage(string $operation, $identifier = null)` **Schwellwerte:** - **80% Memory**: Warning-Log - **90% Memory**: Erzwingt Garbage Collection **Implementierung:** ```php $currentMemory = memory_get_usage(); $memoryLimit = parseMemoryLimit(ini_get('memory_limit')); // z.B. "512M" $memoryPercent = ($currentMemory / $memoryLimit) * 100; if ($memoryPercent > 80) { $this->logger->warning("High memory usage in {$operation}"); } if ($memoryPercent > 90) { gc_collect_cycles(); // Force Garbage Collection } ``` --- ### Stack vs. Rekursion **Problem mit Rekursion:** ```php // ALT: Rekursiver Ansatz (Stack Overflow bei großen Strukturen) function calculateRecursive($items) { foreach ($items as $item) { // Berechnung if ($item->children) { calculateRecursive($item->children); // Rekursion } } } ``` **Lösung mit Stack:** ```php // NEU: Iterativer Ansatz (Memory-effizient) function calculateIterative($items) { $stack = $items; while (!empty($stack)) { $item = array_shift($stack); // Berechnung if ($item->children) { $stack = array_merge($stack, $item->children); } } } ``` **Vorteile:** - Kein Stack Overflow - Kontrolle über Memory - Bessere Performance bei großen Strukturen --- ## HTML-Rendering ### Delegation an TreeHtmlRenderer **Methoden:** ```php public function makeHtmlTree(): string → $renderer->renderTree($businessUsers) public function makeParentlessHtml(): string → $renderer->renderParentless($parentless) public function makeSponsorHtml(): string → $renderer->renderSponsor($sponsor) ``` **Separation of Concerns:** - Berechnung: TreeCalcBotOptimized - Darstellung: TreeHtmlRenderer - Ermöglicht alternative Renderer (JSON, PDF, etc.) --- ## API & Public Methods ### Initialisierungsmethoden | Methode | Zweck | Use Case | | -------------------------------- | ------------------------- | --------------- | | `initStructureAdmin()` | Komplette Struktur | Admin-Dashboard | | `initStructureUser($userId)` | User-spezifische Struktur | Member-Bereich | | `initBusinesslUserDetail($user)` | Einzelner User Details | User-Profil | --- ### Getter-Methoden | Methode | Rückgabe | Beschreibung | | --------------------------- | -------- | ----------------------------- | | `getItems()` | array | Alle Root-BusinessUsers | | `getTotalUserCount()` | int | Gesamtanzahl User in Struktur | | `getGrowthBonus()` | array | Ebenen 6+ für Infinity-Bonus | | `getKeybyLine($line, $key)` | mixed | Spezifischer Wert einer Ebene | | `isParentless()` | bool | Gibt es parentlose User? | --- ### Static Methods | Methode | Zweck | | ----------------------------- | ---------------------------------------- | | `isFromStored($month, $year)` | Prüft ob gespeicherte Struktur existiert | --- ### Utility Methods | Methode | Zweck | | ------------------------- | --------------------------------- | | `addProcessedUserId($id)` | Markiert User als verarbeitet | | `isUserProcessed($id)` | Prüft ob User bereits verarbeitet | --- ## Abhängigkeiten & Klassen ### BusinessUserRepository **Verantwortlichkeiten:** - Datenbankabfragen für User - Eager Loading von Relations - Abfragen von gespeicherten Strukturen **Wichtige Methoden:** ```php getRootUsers(): Collection // Top-Sponsoren getUserWithRelations($id): ?User // User mit Relations getParentlessUsers($excludeIds): Generator // Waisen getSponsorForUser($userId): ?User // Sponsor eines Users getStoredStructure(): ?UserBusinessStructure // Cached Structure ``` --- ### BusinessUserItemOptimized **Verantwortlichkeiten:** - Repräsentiert einen Business-User - Hält Berechnungsdaten (Punkte, Qualifikation) - Verwaltet Sub-User (businessUserItems) **Wichtige Properties:** ```php $user_id // User ID $b_user // Business User Model $sponsor // Sponsor (BusinessUserItemOptimized) $businessUserItems // Array von Sub-Usern $sales_volume_points_TP_sum // Total Points (Verkaufsvolumen) $sales_volume_points_sum // Sales Volume Summe $qual_kp // Qualifikation Kundenpunkte $qual_pp // Qualifikation Payline-Punkte $business_lines // Array [line => points] $next_qual_user_level // Nächste Qualifikation $next_can_user_level // Potenzielle Qualifikation ``` **Wichtige Methoden:** ```php makeUserFromModel(User $user, bool $forceLiveCalculation) // Initialisierung readParentsBusinessUsers(bool $forceLiveCalculation) // Lädt Sub-User calcQualPP() // Berechnet Qualifikation addBusinessLinePoints($line, $points) // Fügt Punkte hinzu addTotalTP($points) // Fügt Total-Punkte hinzu ``` --- ### TreeHtmlRenderer **Verantwortlichkeiten:** - Generiert HTML für Tree-Darstellung - Formatiert User-Daten für UI - Styling & Icons --- ## Workflow-Beispiele ### Beispiel 1: Admin lädt Struktur für November 2025 ```php // Initialisierung $treeCalc = new TreeCalcBotOptimized(11, 2025, 'admin'); // Check für gespeicherte Struktur $treeCalc->initStructureAdmin(check: true, forceLiveCalculation: false); // Ausgabe $html = $treeCalc->makeHtmlTree(); $parentlessHtml = $treeCalc->makeParentlessHtml(); $totalUsers = $treeCalc->getTotalUserCount(); echo "Struktur mit {$totalUsers} Usern geladen."; ``` **Ablauf (wenn gespeichert):** 1. Prüft `user_business_structures` Tabelle 2. Lädt JSON-Daten 3. Validiert Vollständigkeit 4. Rendert HTML (< 1 Sekunde) **Ablauf (wenn nicht gespeichert):** 1. Lädt alle Root-User aus DB 2. Lädt rekursiv alle Sub-User 3. Berechnet Punkte für alle User 4. Berechnet Qualifikationen 5. Rendert HTML (10-60 Sekunden, je nach Größe) --- ### Beispiel 2: Member lädt eigene Struktur ```php // Initialisierung $treeCalc = new TreeCalcBotOptimized(11, 2025, 'member'); // User-spezifische Struktur $treeCalc->initStructureUser(userId: 12345, forceLiveCalculation: false); // Sponsor $sponsorHtml = $treeCalc->makeSponsorHtml(); // Team-Struktur $teamHtml = $treeCalc->makeHtmlTree(); ``` **Ablauf:** 1. Lädt User 12345 mit Relations 2. Lädt Upline (Sponsoren-Kette nach oben) 3. Lädt Downline (Team-Struktur nach unten) 4. Berechnet nur relevante Daten für diesen User 5. Rendert HTML (2-10 Sekunden) --- ### Beispiel 3: Live-Berechnung erzwingen ```php // Für aktuelle Daten (z.B. laufender Monat) $treeCalc = new TreeCalcBotOptimized(11, 2025, 'admin'); $treeCalc->initStructureAdmin( check: false, // Ignoriere gespeicherte Daten forceLiveCalculation: true // Erzwinge Neuberechnung ); // Alle Daten frisch berechnet $html = $treeCalc->makeHtmlTree(); ``` **Use Cases:** - Aktueller laufender Monat - Nach Datenimport/-änderung - Debugging/Testing --- ## Performance-Charakteristiken ### Laufzeit-Komplexität **Admin-Struktur (Complete Tree):** - Best Case: O(1) - Gespeicherte Struktur laden - Worst Case: O(n) - n = Anzahl aller User - Typisch: 10-60 Sekunden für 1000-10000 User **User-Struktur (Subtree):** - Best Case: O(1) - Gespeicherte Struktur - Worst Case: O(d \* b) - d = Tiefe, b = Durchschnittliche Breite - Typisch: 2-10 Sekunden für Struktur mit 100-1000 Sub-Usern --- ### Speicher-Komplexität **Stack-basierter Algorithmus:** - O(n) - n = Anzahl User in Verarbeitung - Max. Speicher: ~100 MB für 10.000 User - Bei Rekursion: O(d) - d = Max. Tiefe (Stack Overflow-Risiko) --- ### Datenbankabfragen **Optimiert (mit Eager Loading):** ``` Queries für 1000 User: - loadRootUsers: 1 Query (alle Root-User) - loadParentsUsers: ~10-50 Queries (chunked) - Total: ~50-100 Queries ``` **Unoptimiert (ohne Eager Loading):** ``` Queries für 1000 User: - N+1 Problem: ~3000-5000 Queries - Laufzeit: 10x langsamer ``` --- ## Konfiguration & Limits ### Memory Limit **Empfohlen:** - Kleine Strukturen (< 1000 User): 256 MB - Mittlere Strukturen (1000-5000 User): 512 MB - Große Strukturen (> 5000 User): 1024 MB **php.ini:** ```ini memory_limit = 512M max_execution_time = 300 # 5 Minuten ``` --- ### Logging-Konfiguration **Log-Channels:** ```php 'channels' => [ 'business_calculations' => [ 'driver' => 'daily', 'path' => storage_path('logs/business-calc.log'), 'level' => 'info', 'days' => 14, ], ], ``` --- ## Testing & Debugging ### Unit Tests **Test-Cases:** ```php // Test: Punkte-Berechnung korrekt testCalculateUserPointsOptimized() { $user = createUserWithTeam(3 levels, 10 users); $calc = new TreeCalcBotOptimized(11, 2025); $calc->initStructureUser($user->id); assertEquals(expected_points, $user->business_lines[1]); } // Test: Keine Duplikate testNoDuplicateProcessing() { $calc = new TreeCalcBotOptimized(11, 2025); $calc->addProcessedUserId(123); assertTrue($calc->isUserProcessed(123)); assertFalse($calc->isUserProcessed(456)); } ``` --- ### Performance-Testing **Benchmark:** ```php $startTime = microtime(true); $startMemory = memory_get_usage(); $calc = new TreeCalcBotOptimized(11, 2025, 'admin'); $calc->initStructureAdmin(); $endTime = microtime(true); $endMemory = memory_get_usage(); echo "Zeit: " . ($endTime - $startTime) . "s\n"; echo "Memory: " . (($endMemory - $startMemory) / 1024 / 1024) . " MB\n"; ``` --- ### Debug-Modus **Aktivierung:** ```php $logger = new DebugLogger('debug'); // Debug-Level Logging $calc = new TreeCalcBotOptimized( month: 11, year: 2025, initFrom: 'admin', forceLiveCalculation: true, logger: $logger ); ``` **Debug-Output:** ``` [DEBUG] Processed user 789 at line 3 with 250.5 points [DEBUG] Loaded 15 parent users for user 321 [INFO] Completed calculations for all business users in 1234.56ms ``` --- ## Bekannte Limitierungen & Edge Cases ### 1. Zirkelbezüge **Problem:** User A sponsort User B, User B sponsort User A **Lösung:** `processedUserIds` Array verhindert Endlosschleifen ```php if ($this->isUserProcessed($userId)) { return; // Skip bereits verarbeitete User } ``` --- ### 2. Gelöschte User **Problem:** Sponsor gelöscht, aber Referenz existiert noch **Lösung:** Soft-Deletes & Parentless-User Handling ```php // Parentless-Query berücksichtigt gelöschte Sponsoren User::whereNull('sponsor_id') ->orWhereHas('sponsor', function($q) { $q->whereNotNull('deleted_at'); }) ``` --- ### 3. Große Strukturen **Problem:** > 10.000 User → Memory-Probleme **Lösung:** - Chunked Loading - Memory-Monitoring - Garbage Collection - Ggf. Queue-basierte Verarbeitung --- ### 4. Zeitzonenprobleme **Problem:** User in verschiedenen Zeitzonen **Lösung:** Alle Berechnungen in UTC ```php $this->date->start_date = Carbon::parse($year . '-' . $month . '-1') ->setTimezone('UTC') ->format('Y-m-d H:i:s'); ``` --- ## Best Practices & Empfehlungen ### 1. Caching nutzen **DO:** ```php // Für vergangene Monate: Cache nutzen $calc = new TreeCalcBotOptimized(10, 2025); $calc->initStructureAdmin(check: true, forceLiveCalculation: false); ``` **DON'T:** ```php // Laufender Monat: Immer Live-Berechnung $calc = new TreeCalcBotOptimized(11, 2025); $calc->initStructureAdmin(check: false, forceLiveCalculation: true); ``` --- ### 2. Fehlerbehandlung **DO:** ```php try { $calc->initStructureUser($userId); } catch (\Exception $e) { Log::error("Error loading structure for user {$userId}", [ 'exception' => $e->getMessage() ]); // Fallback oder User-Feedback } ``` **DON'T:** ```php // Fehler ignorieren oder unbehandelt lassen $calc->initStructureUser($userId); ``` --- ### 3. Dependency Injection **DO:** ```php // Testbar durch Dependency Injection $mockRepo = Mockery::mock(BusinessUserRepository::class); $calc = new TreeCalcBotOptimized(11, 2025, repository: $mockRepo); ``` **DON'T:** ```php // Hardcoded Dependencies class TreeCalcBot { public function __construct() { $this->repo = new BusinessUserRepository(); // Nicht testbar } } ``` --- ## Wartung & Monitoring ### Logs überwachen **Wichtige Log-Patterns:** ```bash # High Memory Usage grep "High memory usage" storage/logs/business-calc.log # Error Patterns grep "Error calculating" storage/logs/business-calc.log # Performance Issues (> 60 Sekunden) grep "Completed calculations" storage/logs/business-calc.log | awk '{print $NF}' ``` --- ### Performance-Metriken **KPIs:** - Durchschnittliche Berechnungszeit pro User - Memory Peak Usage - Anzahl DB-Queries - Cache Hit Rate **Monitoring:** ```php // In Controller oder Command $metrics = [ 'users_processed' => $calc->getTotalUserCount(), 'execution_time' => $executionTime, 'memory_peak' => memory_get_peak_usage(), 'cache_used' => $storedStructure ? 'yes' : 'no' ]; Log::info('Structure calculation completed', $metrics); ``` --- ## Migration von alter Version ### TreeCalcBot → TreeCalcBotOptimized **Breaking Changes:** 1. **Static Method `addUserID()`** → Deprecated ```php // ALT TreeCalcBot::addUserID($userId); // NEU $calc->addProcessedUserId($userId); ``` 2. **Constructor-Signatur** ```php // ALT new TreeCalcBot($month, $year); // NEU (mit optionalen Dependencies) new TreeCalcBotOptimized($month, $year, $initFrom, $forceLiveCalculation); ``` 3. **HTML-Rendering** - Intern an TreeHtmlRenderer delegiert - Public API bleibt gleich --- ## Zusammenfassung ### Hauptmerkmale ✅ **Performance-optimiert** - Stack-basiert statt Rekursion - Eager Loading (N+1 gelöst) - Memory-Management - Caching ✅ **Wartbar** - Separation of Concerns - Dependency Injection - Umfangreiches Logging - Fehlerbehandlung ✅ **Skalierbar** - Handhabt > 10.000 User - Chunked Processing - Garbage Collection - Memory-Monitoring ✅ **Testbar** - Dependency Injection - Repository Pattern - Unit-testbar --- ### Typische Use Cases | Use Case | Methode | Cache | Laufzeit | | --------------------- | --------------------------------- | ----- | -------- | | Admin-Monatsabschluss | `initStructureAdmin()` | Ja | < 1s | | Admin-Live-Dashboard | `initStructureAdmin(force: true)` | Nein | 10-60s | | Member-Dashboard | `initStructureUser()` | Ja | < 2s | | User-Profil-Details | `initBusinesslUserDetail()` | Ja | 2-5s | --- ### Erweiterungsmöglichkeiten **Mögliche Verbesserungen:** 1. **Queue-basierte Verarbeitung** - Große Strukturen in Background-Jobs - Progress-Tracking für User - Retry-Mechanismen 2. **Inkrementelles Update** - Nur geänderte User neu berechnen - Delta-Updates statt Full-Recalculation 3. **Distributed Caching** - Redis für schnelleren Zugriff - Cache-Invalidierung bei Änderungen 4. **API-Endpoints** - RESTful API für externe Zugriffe - JSON-Export für Drittanwendungen 5. **Real-time Updates** - WebSocket-Integration - Live-Aktualisierung ohne Page Reload --- ## Glossar **Business User**: User mit MLM-Vertriebsaktivitäten **Sponsor**: Derjenige der einen User geworben hat (Upline) **Line/Ebene**: Hierarchieebene im MLM (1 = direkt, 2 = Enkel, etc.) **Parentless**: User ohne Sponsor (Waisen) **TP (Total Points)**: Gesamtpunkte Verkaufsvolumen **KP (Kunden-Punkte)**: Persönliche Verkäufe **PP (Payline-Punkte)**: Team-Verkäufe für Provisionsberechtigung **Growth Bonus**: Provisions-Ebenen ab 6+ **Forced Live Calculation**: Erzwingt Neuberechnung, ignoriert Cache **Eager Loading**: Lädt Relations in einer Query (vs. Lazy Loading) --- ## Kontakt & Support Bei Fragen oder Problemen: - Code Review: Siehe BusinessUserRepository, BusinessUserItemOptimized - Logging: `storage/logs/business-calc.log` - Performance Issues: Memory-Limit erhöhen, Caching prüfen - Bugs: Exception-Stack-Trace analysieren --- **Letzte Aktualisierung:** November 2025 **Version:** TreeCalcBotOptimized v2.0 **Status:** Production Ready ✅