# Deploy 14.09.2026 — Team-Statistik = Team-Struktur, Laufstand-Fix Das Paket zu K11 aus dem [Entwicklungsplan zum September-Protokoll](../_tickets/entwicklungsplan-protokoll-september-2026.md) (Tickets C1 und C2) ist auf Produktion. Upload per SFTP, anschließend Migration, Snapshot-Neuaufbau, Laufstand-Reparatur und Cache-Leerung im Terminal. **Die Zahlen von Team-Statistik und Team-Struktur stimmen seitdem überein** (Rückmeldung Kevin, 14.09.). ## Was live gegangen ist | Ticket | Inhalt | | --- | --- | | C1 | Die Team-Statistik rechnet standardmäßig mit dem **Provisionsbaum** und den **Provisionspunkten** — denselben Mitgliedern und Punkten wie die Team-Struktur. Die bisherige Sicht (gesamte Downline) bleibt per Umschalter erreichbar. Für abgeschlossene Monate kommen die Mitglieder je Linie aus dem gespeicherten Provisionslauf. | | C2 | Laufstand-Race in `user_sales_volumes` behoben: sperrendes Lesen bei der Neuberechnung, Deadlock-Wiederholung in beiden Zahlungs-Webhooks, tägliches Sicherheitsnetz um 02:45 vor dem Provisionslauf. | ```bash php artisan migrate php artisan backoffice:store-statistics-snapshots --force php artisan sales:repair-running-totals php artisan config:clear && php artisan view:clear && php artisan cache:clear ``` **Stolperstein beim Migrieren.** Der erste `migrate`-Lauf brach mit „Undefined constant `BackofficeDashboardService::TREE_COMMISSION`" ab: die Migration hatte die Baum-Namen aus der Service-Klasse gelesen, und der PHP-Prozess auf dem Server hielt noch die alte Klasse (Opcache). Es wurde nichts halb angewendet — der Fehler fiel beim Aufbau des Blueprints, vor jedem SQL. Die Migration trägt die Werte jetzt als Literale; Regel für die Zukunft: **Migrationen dürfen nicht vom Anwendungscode abhängen.** Nach Pull, `optimize:clear` und PHP-FPM-Reload lief sie sauber durch. ## Was sich in den Daten geändert hat **Migrationen.** - `backoffice_statistics_snapshots` bekommt die Spalte `tree` (`commission` / `downline`); bestehende Snapshots werden als `downline` gekennzeichnet, der Unique-Index läuft jetzt über (`user_id`, `year`, `month`, `tree`). - `user_sales_volumes` bekommt den Index (`user_id`, `year`, `month`), damit die sperrende Lesart nur den Monatsbereich des Beraters sperrt. **Keine vorhandene Zeile wurde verändert.** **Snapshot-Neuaufbau.** `backoffice_statistics_snapshots` wurde für alle abgeschlossenen Monate und beide Baum-Definitionen neu geschrieben (Payload-Version 3). Die Provisionsbaum-Variante nimmt die Mitglieder je Linie aus `user_businesses` des jeweiligen Provisionslaufs. **Laufstand-Reparatur September.** `sales:repair-running-totals` hat die Laufstände von **2 Usern** (848 und 1543, zusammen **216 Punkte**) neu gerechnet. Nur die kumulativen Spalten der letzten Zeile wurden korrigiert; die Einzelbuchungen waren vollständig. Der laufende Monat rechnet in der Struktur live, die Provision für September wird damit richtig. **Nicht angefasst: Juni und August.** Beide Monate hatten je einen User mit Laufstand-Drift (66 bzw. 82 Punkte) und sind so abgerechnet worden. **Entscheid 14.09.: keine Nachzahlung, keine Nachberechnung.** Die Laufstände bleiben, wie sie in die Provision eingegangen sind; der Nachtlauf fasst abgeschlossene Monate nicht an (manuell nur mit `--force`). **Nicht angefasst:** Provisionen (`user_businesses`), Gutschriften, Rechnungen, Marketingplan. ## Was das für die Zahlen bedeutet Die Kachel „Umsatz (Punkte)" der Team-Statistik zeigt jetzt die **Provisionspunkte**. Gegenüber vorher ist sie **kleiner**, aus drei Gründen, die alle bewusst sind: 1. **Nur Provisionsbaum.** Berater ohne Mitgliedsebene oder ohne Abrechnungskonto zählen nicht, und ihr Unterbaum ebenfalls nicht — genau wie in der Provisionsberechnung. Die Ansicht zeigt, wie viele Berater der Downline deshalb außen vor sind. 2. **Reine Karrierepunkte (KP) zählen nicht** (Neupartner-Einschreibung, manuelle KP-Gutschriften). Sie stehen als eigene Zahl unter der Tabelle: „Nicht enthalten: X Punkte ohne Payline-Wirkung". 3. **Negative Monatssalden werden auf 0 gekappt**, wie in der Provision. Auch das steht als eigene Zahl: „Nicht abgezogen: Y Punkte aus negativen Monatssalden". Wer die bisherige Sicht braucht, wählt oben „Gesamte Downline". Die Wahl bleibt in der Sitzung erhalten und steckt auch im CSV-Dateinamen. Beim Messlauf vor dem Deploy lagen die VIP-Konten im Provisionsbaum bis auf die Laufstand-Drift exakt auf der Struktur. Für August ergab sich nach Umstellung auf die gespeicherte Struktur nur noch eine Differenz von 82 Punkten — genau der Drift-User dieses Monats. ## Woran man merken würde, dass etwas nicht stimmt - **Team-Statistik, Provisionsbaum, laufender Monat:** Summe der Linien muss mit dem Modal „Linie / Punkte / Provision" auf der Struktur-Seite auf den Cent übereinstimmen. Prüfung: `php artisan statistics:compare-trees 9 2026` (Spalte „Diff PB-Struktur" muss 0,00 sein). - **Abgeschlossener Monat:** Abzeichen „Monat abgeschlossen · Stand eingefroren" plus Hinweis, dass die Mitglieder je Linie aus dem gespeicherten Provisionslauf stammen. - **Nachtlauf 02:45:** `sales:repair-running-totals` schreibt bei Drift eine Warnung ins `cron`-Log. Über mehrere Nächte sollte dort „keine Abweichung" stehen — das ist der Nachweis, dass die Sperre auf MySQL wirkt (die Test-Suite läuft auf SQLite und kann den Race nicht nachstellen). - **Zahlungs-Webhooks:** Ein Deadlock würde die ganze Paid-Transaktion bis zu dreimal wiederholen. Erkennbar im `payone`- bzw. `stripe`-Log nur, wenn alle drei Anläufe scheitern; dann stellt der Anbieter erneut zu. ## Offen - **C2, Stufe 3** (Provision aus der Zeilensumme statt aus dem Laufstand der letzten Zeile) gehört zum Ledger-Plan `dev/2026-09-10-business-plan/`. - Rückfrage **R4** (negative Monatssalden in der Provision) ist eine fachliche Entscheidung und wurde nicht angefasst. - Unverändert: die vorbestehenden Testfehler auf dem Branch (92 Fehlschläge in Unit-Tests ohne Datenbank-Setup, z. B. `TreeCalcBotOptimizedTest`, `ConcurrentPaymentTest`; alle mit „no such table" oder `BadMethodCallException` bereits im Setup). Gehören in ein eigenes Aufräumpaket. Nicht in diesem Commit und weiterhin nicht committet: die Arbeit aus anderen Themen im selben Arbeitsbaum (DHL, DATEV, Newsletter-Listen, Berater-Übertragung, DSGVO-Löschung, `shipped_at`, Kundenabo-Legenden). --- ## Nachtrag 14.09.2026 — Übersicht brach mit „Undefined variable $selectedTree" ab Kurz nach dem Deploy meldete Produktion auf `/user/backoffice/statistics` eine ViewException. Die Link-Closure für das grüne „+N"-Badge (neue Abos im Monat) in `index.blade.php` band die neue Variable `$selectedTree` nicht per `use` ein. Der Fehler trat nur auf, wenn in einer Linie neue Abos gezählt wurden — die Tests hatten die Übersicht bis dahin nie mit gefüllten Zeilen gerendert. **Behebung:** `use (..., $selectedTree)` an der Closure, dazu ein Regressionstest, der die Übersicht mit einem neuen Teamabo rendert und den Badge-Link samt `tree=`-Parameter prüft (`BackofficeStatisticsDrilldownTest`). Ohne den Fix schlägt er mit genau dieser Meldung fehl. **Deployment:** nur `resources/views/user/backoffice/statistics/index.blade.php`, danach `php artisan view:clear`. Keine Migration, keine Daten.