mivita/dev/2026-07-22/deploy-2026-09-14.md
Kevin 96545364f1 Team-Statistik = Team-Struktur (Provisionsbaum, Umschalter) und Laufstand-Fix in user_sales_volumes
K11 aus dem September-Protokoll (Tickets C1 und C2 im Entwicklungsplan), live seit 14.09.2026.

C1 – Statistik rechnet wie die Provision
- Standard-Baum ist der Provisionsbaum (Mitglieder wie TreeCalcBot: Mitgliedsebene,
  Abrechnungskonto, active_date; ausgefilterte Knoten kappen ihren Unterbaum, max. 21 Linien).
  Gesamte Downline bleibt per Umschalter erreichbar, Wahl in der Session.
- Punkte-Definition: Provisionspunkte (TP + Shop), reine Karrierepunkte ausgewiesen statt
  eingerechnet, negative Monatssalden je User wie in der Provision auf 0 gekappt.
- Abgeschlossene Monate nehmen die Mitglieder je Linie aus dem gespeicherten Provisionslauf
  (user_businesses), Snapshots je Baum-Definition (Payload-Version 3, Migration).
- Struktur-Modal: Gesamt aus den Linien-Punkten mit Dezimalen statt der int-Spalte total_pp.
- Messlauf statistics:compare-trees (Downline / Provisionsbaum / Struktur je VIP, Laufstand-Drift).

C2 – Laufstand-Race (Provision zu niedrig bei zeitgleichen Buchungen)
- Neuberechnung liest die Zeilen des Beraters sperrend (FOR UPDATE), Index (user_id, year, month).
- Paid-Pfad der Payone- und Stripe-Webhooks als DB::transaction mit 3 Anlaeufen; Deadlock und
  Lock-Timeout aus der Rechnungserstellung werden nicht mehr geschluckt.
- Sicherheitsnetz sales:repair-running-totals taeglich 02:45 vor dem Provisionslauf;
  abgeschlossene Monate werden nicht angefasst (Entscheid 14.09.: keine Nachzahlung fuer 06/08).

Doku: Entwicklungsplan zum Protokoll, Deploy-Notiz 14.09. (inkl. Migrations-Stolperstein:
Migrationen tragen Werte als Literale, nicht als Konstanten aus dem Anwendungscode).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 15:31:32 +00:00

6.3 KiB

Deploy 14.09.2026 — Team-Statistik = Team-Struktur, Laufstand-Fix

Das Paket zu K11 aus dem Entwicklungsplan zum September-Protokoll (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.
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).