Statistik-Änderungen 2026-08-19 (dev/2026-07-22/entwicklungsplan-2026-08-19.md, alle 8 Pakete gemäß beantworteten F-Fragen): - N1 Monatsstatistik: Team-Cluster (Firstlines klickbar -> neue Seite user_team_firstlines, Teampartner gesamt/Payline [nur aktive], Abos Payline mit +N) + Level-Kachel (Quali-Stufe, fehlende Payline-/persönliche Punkte, Karriere-Level); LiveCommissionService-Snapshot v3 reicht Level-/Quali-Daten durch - N2 Abo-Kacheln auf Home monatsbezogen (aboStockCount-Rückrechnung über cancel/deleted/Status-Historie; VIP-Statistik bleibt tagesaktuell) - N3 VIP-Statistik 'Recruiter des Monats' (Neupartner-Definition wie KPI, rekursives Ketten-Volumen als Gesamtpunkte inkl. Starterpaket) + Drilldown/CSV-Spalte 'Punkte gesamt' - N4 Prognose-Block 'Level & Provision laut Prognose (Stand 25.)' mit unverbindlich-Kennzeichnung (ebenengenaue offene Abo-Punkte + TP-Laufstände) - N5 Checkout-Herkunft: Dropdown raus, Pflicht-Freitext (lokalisierte Fehlermeldung) - N6 Berater-Profilfoto (user_accounts.profile_image, Einwilligung als Sprachkey-Platzhalter, DSGVO-Löschung) + teaminterne Top-3-Karte (TopResultsService, Scope-Schalter) - N7 USt-Optionen: 'nicht umsatzsteuerpflichtig' zuerst + '§ 19 UStG'-Textfix - N8 Jahresgebühr-Nachfassliste je Berater (AnnualFeeDueService, überfällig/1-3 Monate, Sidenav) Kontroll-Review (8 Perspektiven) angewendet: next-Level-Fallback im Steady-State (resolveNextLevel — sonst 'Höchstes Level erreicht' für Normalfälle), _calculated-Punktebasis bei mehr Paylines, keine Wiederbelebung deaktivierter Abos in der Monatsrückrechnung, Payline-Abo-Kachel monatskonsistent, Top-3-Nachrücker; Performance: BackofficeDashboardService als Singleton mit lineBuckets-Memo (+flushLineBucketsMemo), level_forecast 6h-Cache; Reuse: User-Scopes firstlinesOf/newPartnerIn, SalesVolumePoints::totalPointsByUser, Slim::storeCroppedImage (3 Upload-Kopien vereinigt), activeAbo-Scope statt Inline-Prädikate, Form::open-Konvention, Return-Types Repair-Härtung (Kundenentscheide): VP-Nachtrepair dedupliziert über shopping_order_id (Live-Pfad schreibt sie jetzt mit), zählende Alt-Zeilen-Erkennung, Vormonats-Scan für Monatswechsel, Firmenaccount-Buchungen (config mivita.company_user_id) werden automatisch auf den Berater umgebucht (in place bzw. per Gegenbuchung bei geschlossenem Monat; Echtfall Anna Schall Order 58797 -> Christina Schuder), Berater-zu-Berater nur Warnung; Incentive-Repair: m_sponsor=NULL löscht nicht mehr, FK-Repair vor Stale-Bereinigung, gebatchte Lookups, zentrales Incentive::qualificationWindow(); .env aus dem Git-Tracking entfernt (Pflege per SFTP) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
26 KiB
Entwicklungsplan — Statistik-Änderungen vom 2026-08-19
Basis: dev/2026-07-22/Statistik-Aenderungen-19-08-2026.pdf (Kundin, Folge-Feedback zur Umsetzung vom 2026-07-24).
Ist-Stand-Analyse: 2026-08-19.
Umsetzungsstand (2026-08-19): alle 8 Pakete gemäß den beantworteten F-Fragen umgesetzt — Details je Paket im Abschnitt „Umgesetzt". Zusammenfassung:
- N7 ✅ USt-Optionen getauscht (Werte unverändert), Text „§ 19 UStG" repariert (de/fr).
- N5 ✅ Checkout: Dropdown raus, Freitext Pflichtfeld; Altdaten/Marker kompatibel; Admin-Fallback-Kette. Tests:
CheckoutOrderSourceValidationTest.- N8 ✅ Neue Seite „Jahresgebühr" für jeden Berater (F8: eigenes Team):
AnnualFeeDueService+ Routeuser_team_annual_fee+ Sidenav-Eintrag, Gruppen überfällig/≤1/≤2/≤3 Monate. Tests:AnnualFeeDueServiceTest.- N1 ✅ Monatsstatistik: neues „Team"-Cluster (Firstlines klickbar → neue Seite
user_team_firstlines, Teampartner gesamt [nur aktive, F1], Teampartner Payline [aktive, konsistent], Abos Payline mit „+N neu") + Level-Block (Quali-Stufe des Monats, fehlende Payline-/persönliche Punkte, Karriere-Level als Zusatz, F2). Basis:LiveCommissionService-Snapshot v2 reicht jetztqual_level/next_level/payline_points_qual_kp/personal_pointsdurch (Cache-Keylive_business_v2_*).- N2 ✅ Abo-Kacheln monatsbezogen:
TeamAboOverviewService::aboStockCount()rechnet für vergangene Monate den Bestand zum Monatsende zurück (start/cancel/deleted + Status-Historie); laufender Monat bleibt live; VIP-Statistik bleibt tagesaktuell (F3). Näherung vor 07/2026 dokumentiert.- N3 ✅ „Recruiter des Monats" auf der VIP-Statistik (Neupartner-Definition wie KPI, F4a) mit rekursivem Ketten-Volumen als Gesamtpunkte (Starterpaket zählt damit); aufklappbare Detailzeilen; Neupartner-Drilldown + CSV zusätzlich mit Spalte „Punkte gesamt" (F4b).
- N4 ✅ Prognose-Block „Level & Provision laut Prognose (Stand 25.)" mit „unverbindlich"-Kennzeichnung (F5):
PaylineForecastService::levelAndCommissionForecast()— Ebenen-aufgelöste offene Abo-Punkte + heutige TP-Laufstände, Level-Erreichung inkl. persönlichem Volumen, Provisionsschätzung mit den Sätzen des laut Prognose erreichten Levels (Shop/WB fortgeschrieben).- N6 ✅ Profilfoto (
user_accounts.profile_image, Upload mit Einwilligungs-Checkbox im Profil, DSGVO-Löschung integriert; Einwilligungstext als Sprachkey-Platzhalter — finalen Text liefert die Kundin nach, F7c) + Karte „Die besten Ergebnisse des Monats" teamintern (F7a) auf dem Home-Dashboard: Top 3 persönliches Volumen / Neupartner / neue Abos (Kundenabos + Eigenabo, F7b) mit Foto bzw. Initialen-Avatar, 6-h-Cache, Scope-Schalter (TopResultsService::SCOPE) für eine spätere firmenweite Variante. Tests:TopResultsServiceTest.
Wie ich das Briefing verstehe
Das Feedback betrifft diesmal überwiegend die Monatsstatistik-Card auf dem Home-Dashboard (für alle Berater), dazu drei Punkte außerhalb der Statistik (Checkout-Herkunftsfeld, Steuerdaten-Formular, Jahresgebühr-Nachfassliste). Es zerfällt in 8 Pakete:
| # | Paket | Bereich | Einstufung | Aufwand |
|---|---|---|---|---|
| N1 | Monatsstatistik: 7 neue Kennzahlen (Firstlines klickbar, Teampartner, Payline-Zahlen, Level) | Home | Erweiterung | mittel |
| N2 | Abo-Kacheln beim Monatswechsel monatsbezogen statt eingefroren | Home | Verhaltensänderung | mittel |
| N3 | „Recruiter des Monats" — Neupartner-Ranking je Sponsor inkl. Volumen + Starterpaket | VIP-Statistik | neue Liste + Zählweise-Fix | mittel |
| N4 | Prognose: „fehlen zum nächsten Level X Punkte" + voraussichtliche Provision | Home | Erweiterung, komplexester Punkt | groß |
| N5 | Checkout-Herkunft: Dropdown raus, nur Freitext | Shop-Checkout | kleine Änderung | klein |
| N6 | Teampartner-Foto + „Die besten Ergebnisse des Monats" (3× Top 3) | Home + Profil | neues Feature | mittel–groß |
| N7 | Steuerdaten: Reihenfolge der USt-Optionen tauschen | Profil/Wizard | Mini-Änderung | sehr klein |
| N8 | Jahresgebühr-Nachfassliste (fällig in 3/2/1 Monaten + überfällig) | VIP-Statistik | neue Liste | klein–mittel |
Empfohlene Reihenfolge: N7 → N5 → N8 → N1 → N3 → N2 → N6 → N4 (Quick-Wins zuerst; N4 zuletzt, weil er auf N1-Bausteinen aufsetzt und die meisten Klärungen braucht).
N1 — Monatsstatistik: neue Kennzahlen
Briefing: Übersichtsliste ergänzen um: Firstlines (Anzahl, klickbar → Liste mit Umsatz, Telefon, Mail), Teampartner insgesamt, Teampartner in der Payline, Abos insgesamt Payline, Neue Abos in der Payline, aktuelles Level, „zum nächsten Level fehlen … Punkte" (+ ggf. fehlendes persönliches Volumen und was die Provision dann wäre).
Ist-Stand (gut vorbereitet):
- Firstline-IDs werden im HomeController.php:127-132 bereits berechnet (für „Direkte Neupartner"), die Anzahl wird aber nicht angezeigt.
- Teampartner gesamt:
BackofficeDashboardService::teamUserIds()/activeTeamUserIds()vorhanden. - Payline-Mitglieder + Abo-Zähler:
PaylineForecastService::aboTurnover()liefert seit Julimembers,team_abo_count,team_customer_abo_count— wird vom Home-Dashboard nur nicht aufgerufen (nur die Team-Abo-Seite nutzt es). - „Neue Abos in der Payline": existiert nicht, aber trivial —
newAboCount()-Logik auf die Payline-Mitglieder-IDs einschränken (Eigen-Abos überuser_id, Kundenabos übermember_id,start_dateim Monat). - Level:
LiveCommissionService::snapshot()berechnet die Quali-Daten (qual_user_level,next_qual/next_can_user_levelmit_calculated_*-Feldern) bereits im Live-Lauf, wirft sie aber weg — nurpaylineswird durchgereicht (LiveCommissionService.php:117-128). Eine „Punkte bis zum nächsten Level"-Differenz wird heute nirgends berechnet (auch der Marketingplan zeigt nur die statischen Anforderungen).
Umsetzungsvorschlag:
- Neues KPI-Cluster „Team" auf der Monatsstatistik-Card: Firstlines · Teampartner gesamt · Teampartner Payline · Abos Payline (Teamabos + Kundenabos) · „+N neue Abos Payline" — alle Werte aus vorhandenen Services, gecacht im bestehenden 6-h-Muster.
- Firstlines klickbar: neue leichte Liste (Modal oder eigene Seite unter
user/team) für alle Berater — Spalten: Name, Karriere-Level, Umsatz (Monats-Gesamtpunkte), Telefon, E-Mail, aktiv seit / Account-Status. Die VIP-Drilldown-Infrastruktur ist VIP-only; für Firstlines (eigene direkte Partner) ist eine eigene, nicht-VIP-Route unbedenklich. - Level-Kachel: „Aktuelles Level (Monat): BRONZE — zum nächsten Level SILBER fehlen X Payline-Punkte" + falls die persönliche Anforderung (
qual_kp) noch nicht erfüllt ist: „dafür fehlen außerdem Y Punkte persönliches Volumen". Rechnung aus dem Snapshot:nextLevel.qual_pp − payline_points_qual_kpbzw.nextLevel.qual_kp − sales_volume_points_KP_sum. Dazu Anzeige der Provisionssätze des nächsten Levels („dann z. B. Ebene 1–3 je 7 %") — die exakte „was wäre die Provision"-Rechnung gehört zu N4. - Voraussetzung:
LiveCommissionService::buildSnapshot()um die Level-/Punktefelder erweitern (kein zweiter Baum-Lauf, nur Durchreichen).
Offene Fragen: F1 (aktive vs. alle Teampartner), F2 (welches „Level" gemeint ist — siehe unten).
N2 — Abo-Kacheln beim Monatswechsel monatsbezogen
Briefing: Beim Monatswechsel auf der Monatsstatistik passen sich die Kennzahlen an, die 4 Abo-Kacheln (Eigene Abos, Eigene Kundenabos, Teamabos, Team-Kundenabos) aber nicht — sie sollen die Zahlen des gewählten Monats zeigen.
Befund bestätigt: TeamAboOverviewService::teamKpis()/personalAboKpis() zählen den heutigen activeAbo-Bestand; month/year fließen nur in die „+N neu"-Badges. Auch die Team-Mitgliederbasis ist der heutige Strukturbaum. Es gibt keinerlei Home-Snapshot.
Wichtiger Kontext: Für die VIP-Statistik wurde 2026-07 das Gegenteil entschieden (F7: Abo-Bestände immer tagesaktuell, Snapshot wird live überlagert). Der neue Wunsch gilt dem Home-Dashboard — das ist kein Widerspruch, sollte aber bewusst entschieden werden (→ F3).
Umsetzungsvorschlag (Variante A, empfohlen): Rückrechnung über Abo-Daten — Bestand zum Monatsende M:
start_date <= Ende(M) UND nicht vor Ende(M) beendet (cancel_date/deleted_at/user_abo_status_histories nach Ende(M) oder Abo heute aktiv). Das ist ohne neue Tabellen möglich und für die Vergangenheit eine gute Näherung; seit Juli 2026 protokolliert user_abo_status_histories Statuswechsel, wodurch die Rückrechnung ab da exakt wird. Einschränkung dokumentieren: Für Monate vor Juli 2026 können Deaktivierungen ohne cancel_date fehlen (Zahlen dann leicht zu hoch).
Variante B (verworfen): monatliche Bestands-Snapshots je User — für ALLE Berater (Home ist nicht VIP-only) unverhältnismäßig teuer.
Umsetzung: teamKpis()/personalAboKpis() bekommen einen „Stichtag"-Modus (laufender Monat = live wie bisher; vergangener Monat = Rückrechnung zum Monatsende). Struktur (Team-Mitglieder) bleibt pragmatisch die heutige.
N3 — „Recruiter des Monats" (VIP-Statistik)
Briefing: Neben „Neue Kundenabos" eine Liste der neuen Teampartner je Sponsor, sortiert nach Anzahl: Name | Anzahl gesponserte Neupartner | neues Volumen. Klick auf die Anzahl → wer wurde gesponsert + dessen aktueller Umsatz; hat der Neue selbst schon Neupartner geworben, zählt deren Umsatz dazu. Und: die Starterpaket-Punkte zählen zum Volumen — „werden jetzt noch nicht gezählt".
Ist-Stand / Aufklärung Starterpaket:
- Ein Sponsor-Ranking existiert nicht; Blaupause ist
newCustomerAboRanking(). - Die Starterpaket-Punkte des Neupartners werden systemisch verbucht (Status 1, voll umsatz- und payline-wirksam, 200/400 P. je nach Kit). Was die Kundin vermutlich sieht: Die Spalte „Kundenpunkte" im Neupartner-Drilldown nutzt
externalPoints()(nur Shop-/Kundenumsatz, Status 2) — die Eigenbestellung Starterpaket (Status 1) fällt dort raus. → Fix: im Drilldown und im neuen Ranking „Volumen" als Gesamtpunkte (eigene + Kundenpunkte,totalPoints()-Logik) rechnen, dann ist das Starterpaket automatisch drin. - Zwei konkurrierende Neupartner-Definitionen im System: Statistik (
active_dateim Monat + aktiver Account) vs. Incentive (bezahlte Starterpaket-Bestellung,created_at). → F4.
Umsetzungsvorschlag:
- Neue Karte „Recruiter des Monats" auf der VIP-Statistik (Scope: eigenes Team, inkl. „ich" wie bei den anderen Listen): je Sponsor Anzahl Neupartner des Monats + Volumen = Summe der Monats-Gesamtpunkte aller seiner Neupartner, plus rekursiv die Gesamtpunkte der von diesen Neupartnern im selben Monat geworbenen weiteren Neupartner (Kettenlogik wie im Beispiel der Kundin).
- Drilldown (Klick auf Anzahl): Liste der geworbenen Personen mit Monats-Gesamtpunkten, darunter eingerückt deren eigene Neupartner mit Punkten (eine Rekursionsebene sichtbar, Summe vollständig rekursiv).
- Nebenbei: Neupartner-Drilldown-Spalte „Kundenpunkte" um „Punkte gesamt (inkl. Starterpaket)" ergänzen oder ersetzen (→ F4b).
- Landet mit im Monats-Snapshot (monatsbezogene Liste, kein Live-Overlay nötig).
N4 — Prognose: nächstes Level + voraussichtliche Provision
Briefing: Bei der Prognose ein Feld „laut Prognose fehlen zum nächsten Level (z. B. Bronze Member) noch 2300 Punkte" und „voraussichtliche Provision laut Prognose: 1200 € (sofern das persönliche Volumen von Y erreicht wird)".
Ist-Stand: Die Prognose-Kacheln rechnen basePoints (Kundenumsatz + Payline-Punkte heute) + offene Abo-Punkte bis Termin — als eine Gesamtzahl, ohne Linien-/Ebenen-Zuordnung. Die Provisionsrechnung braucht aber Punkte pro Ebene (Sätze pr_line_1..8 je Level) und die Level-Qualifikation braucht die Trennung „persönliches Volumen (qual_kp)" vs. „Payline-Punkte (qual_pp)".
Umsetzungsvorschlag (zweistufig):
- Stufe 1 — Level-Prognose (solide machbar): Die offenen Abo-Ausführungen sind Payline-Mitgliedern zuordenbar (Ebene bekannt aus
lineBuckets), eigene/eigene Kundenabos zählen aufs persönliche Volumen. Damit: prognostiziertepayline_points_qual_kpund prognostiziertes persönliches Volumen zum 25. → daraus „laut Prognose fehlen zum nächsten Level X noch N Punkte" bzw. „nächstes Level wird laut Prognose erreicht, sofern das persönliche Volumen von Y P. erreicht wird". - Stufe 2 — Provisions-Prognose (Näherung, klar kennzeichnen): prognostizierte Punkte je Ebene × Provisionssätze des laut Prognose erreichten Levels (+ ggf. Wachstumsbonus ausklammern oder mit heutigem Wert fortschreiben). Das ist eine Schätzung — Rangverschiebungen im Team, 60%-Regeln etc. bleiben außen vor. Im UI als „voraussichtlich, unverbindlich" kennzeichnen (wie die bestehenden Live-Badges).
- Technisch: Erweiterung von
PaylineForecastService(Ebenen-aufgelösteupcoming-Punkte) +LiveCommissionService(Level-Daten durchreichen, s. N1.4). Kein neuer Baum-Lauf.
Risiko/Empfehlung: größter Einzelaufwand des Pakets; erst nach N1 umsetzen und die Genauigkeitserwartung vorher mit der Kundin abstimmen (→ F5).
N5 — Checkout-Herkunft: nur noch Freitext
Briefing: Statt Dropdown („Sonstiges/Event-Messe sagt uns nichts") nur das Freitextfeld stehen lassen, damit Kunden den konkreten Namen/die konkrete Messe eintragen.
Ist-Stand: Kunden-Checkout hat Dropdown customer_order_source (7 Optionen, Pflicht) plus optionales Freitextfeld customer_order_source_comment — beides an shopping_orders (checkout.blade.php:556-578, Optionen in ShoppingOrder.php:192-200). Es gibt keine Auswertung/Statistik dazu; Admin zeigt den Wert nur in der Bestell-Detailansicht. Achtung: CreateAboReplacementOrder nutzt das Comment-Feld als technischen Marker — Spalten dürfen nicht entfallen.
Umsetzungsvorschlag: Dropdown aus dem Checkout entfernen, Freitextfeld wird das (einzige) Herkunftsfeld — als Pflichtfeld mit sprechendem Label („Wie bist du auf uns aufmerksam geworden? Bitte konkret: Name der Person, Messe, …"). DB-seitig: customer_order_source bleibt bestehen (Bestandsdaten + Marker), neue Bestellungen speichern nur noch den Freitext. Admin-Detailansicht zeigt künftig den Freitext. Aufwand klein. → F6 (Pflichtfeld ja/nein).
N6 — Teampartner-Foto + „Die besten Ergebnisse des Monats"
Briefing: (a) Feld, in dem jeder Teampartner ein Foto hochlädt; (b) auf der Startseite „Die besten Ergebnisse des Monats": Top 3 persönliches Volumen, Top 3 meiste Neupartner, Top 3 meiste neue Abos — je mit Foto + Name + Wert. Teamintern oder firmenintern ist laut Kundin noch offen.
Ist-Stand: Es gibt kein Berater-Profilbild. Das einzige Foto ist das Shop-Bild (user_shops.filename, Slim-Cropper-Upload, nur mit eigenem Shop, Anzeige nur im öffentlichen Shop). Ranglisten-Datenquellen existieren teilweise (Neupartner je Sponsor → N3; neue Kundenabos je Berater → vorhandene Liste; persönliches Monatsvolumen je User → user_sales_volumes-Aggregat, firmenweit bisher nirgends).
Umsetzungsvorschlag:
- Profilfoto als eigenes Feature (unabhängig vom Shop): Spalte an
user_accounts(oder kleine Tabelle), Upload im Profil (user_form/Wizard) mit dem vorhandenen Cropper-Muster ausUserShopController, Fallback Initialen-Avatar, DSGVO: Aufnahme inGdprDeletionService+ Hinweis, dass das Foto team-/firmenintern angezeigt wird (Einwilligung beim Upload). - Top-3-Karte auf dem Home-Dashboard: drei Spalten (Volumen / Neupartner / neue Abos), je Top 3 mit Foto, Vor-/Nachname, Wert. Datenlogik: persönliches Volumen = Monats-Gesamtpunkte je Berater; Neupartner = N3-Zählung; neue Abos = neue Kundenabos + neues Eigenabo je Berater (→ F7b). Nächtliche Berechnung/Cache (firmenweit ein Ranking für alle) statt Live-Query pro Aufruf.
- Scope-Schalter teamintern/firmenintern von Anfang an vorsehen (eine Konstante/Setting), Start laut Rückfrage-Ergebnis (→ F7a).
N7 — Steuerdaten: Reihenfolge der USt-Optionen
Briefing: „nicht umsatzsteuerpflichtig" soll an erster Stelle stehen.
Ist-Stand: Hardcodiertes Array in HTMLHelper.php:399-411 (1 => umsatzsteuerpflichtig, 2 => nicht umsatzsteuerpflichtig), gespeichert als user_accounts.taxable_sales. Reihenfolge tauschen = Array-Reihenfolge ändern, Werte bleiben unverändert (1/2 werden downstream von Gutschriften/DATEV interpretiert, Wert 3 wird intern für Ausland gesetzt — nicht anfassen).
Mitzunehmende Nebenbefunde: Der Text taxable_sales_2 ist abgeschnitten und nutzt $ statt § („Kleinunternehmer im Sinne von $ 19") → korrigieren auf „… im Sinne von § 19 UStG)". Aufwand: Minuten. Keine offenen Fragen.
N8 — Jahresgebühr-Nachfassliste
Briefing: Liste aller Teampartner (komplettes Team), bei denen die Jahresgebühr in 3/2/1 Monaten fällig oder überfällig ist — Spalten Vorname, Nachname, Mail, Telefon, Ebene, Level — „damit wir nachfassen können".
Ist-Stand: users.payment_account = „bezahlt bis"; Verlängerung +1 Jahr bei Zahlung; Erinnerungs-Mails macht der Cron payments:check-accounts (21/14/2 Tage vorher), 2 Wochen nach Ablauf deaktiviert user:cleanup, nach 2 Monaten wird konvertiert/gelöscht. Einen „läuft bald ab"-Report gibt es weder für Admin noch Berater — nur binäre aktiv/inaktiv-Filter in der Team-Liste.
Umsetzungsvorschlag: Neue Karte/Unterseite auf der VIP-Statistik „Jahresgebühr ausstehend": komplettes Team, gruppiert in überfällig / fällig ≤ 1 Monat / ≤ 2 Monate / ≤ 3 Monate (aus payment_account), Spalten Vorname, Nachname, Mail, Telefon (aus user_accounts), Ebene (Linie aus lineBuckets), Karriere-Level, Fälligkeitsdatum; CSV-Export wie bei den anderen Drilldowns. Wichtig: Anzeige „überfällig" nur bis zur Cleanup-Grenze (nach 2 Monaten verschwinden die User systemisch) — mit Hinweis darauf. → F8 (nur VIP oder jeder Berater fürs eigene Team?).
Offene Fragen an die Kundin (vor bzw. während der Umsetzung)
| # | Frage | Empfehlung |
|---|---|---|
| F1 | N1: „Teampartner insgesamt" — nur aktive Berater (wie die „Berater"-Spalte der Statistik) oder alle inkl. inaktiver? | aktive |
| nur Aktive | ||
| F2 | N1/N4: „Aktuelles Level" — die monatliche Qualifikationsstufe (bestimmt Payline/Provision, dazu passt „es fehlen X Punkte") oder das Karriere-Level (steht bereits in der Mitgliedschafts-Card)? | Quali-Stufe des Monats, mit Karriere-Level als Zusatzinfo |
| JA | ||
| F3 | N2: Home zeigt künftig Monats-Bestand (Rückrechnung; vor 07/2026 Näherung) — VIP-Statistik bleibt wie 07/2026 entschieden tagesaktuell? | ja, so lassen; sonst in einem Zug mit umstellen |
| JA | ||
| F4 | N3: Neupartner-Definition fürs Ranking — wie die Statistik-KPI (aktiv geworden im Monat) oder wie das Incentive (bezahltes Starterpaket im Monat)? (b) Neupartner-Drilldown: Spalte „Kundenpunkte" ersetzen durch „Punkte gesamt" oder beide zeigen? |
(a) wie Statistik-KPI, damit Zahlen zusammenpassen; (b) beide |
| a Wie KPI | ||
| F5 | N4: Provisions-Prognose ist eine gekennzeichnete Schätzung (Rangverschiebungen/Boni nicht exakt) — reicht das? | ja, mit „unverbindlich"-Kennzeichnung |
| Ja mit Kennzeichnung | ||
| F6 | N5: Freitext-Herkunft als Pflichtfeld? | ja, Pflicht mit Beispiel-Platzhalter |
| Ja | ||
| F7 | N6: (a) Top 3 teamintern oder firmenintern? (b) „meiste neue Abos" = neue Kundenabos (+ Eigenabo) je Berater? (c) Foto-Einwilligungstext | (a) firmenintern hinter Schalter, (b) neue Kundenabos + Eigenabo, (c) Einwilligung beim Upload |
| a teamintern | ||
| b neue Kundenabos + Eigenabo | ||
| c Einwilligung beim Upload - lagern wird aber nach | ||
| F8 | N8: Jahresgebühr-Liste nur VIP-Statistik oder für jeden Berater (eigenes Team)? | zunächst VIP |
| eigenes team |
Kontroll-Review (2026-08-19, /code-review high mit Fix-Anwendung)
Nach der Umsetzung lief eine mehrstufige Review (8 Finder-Perspektiven) über den gesamten Diff. Angewendete Fixes:
Korrektheit:
- Level-Kachel/Prognose zeigten für Normalfälle „Höchstes Level erreicht":
qual_user_level_nextist im Steady-State (Quali == Karriere-Level) bewusst null —LiveCommissionService::resolveNextLevel()fällt jetzt aufnext_qual_user_level/next_can_user_level(direkt nächster Karriere-Level, inkl._calculated_*) zurück. Verifiziert an Steady-State-User (Berater → Aktiv Berater). - „fehlen X Punkte" bei mehr Paylines im nächsten Level überhöht: Basis ist jetzt
_calculated_payline_points_qual_kpdes NÄCHSTEN Levels (rechnet mit dessen Paylines/KP-Überschuss). - Historischer Abo-Bestand (N2) konnte deaktivierte Abos „wiederbeleben", wenn ein späteres Ereignis (z. B. Löschung) nach dem Stichtag lag: die Rückrechnung prüft jetzt zusätzlich, ob das letzte Statusereignis bis zum Stichtag deaktivierend war (inkl. Reaktivierungs-Logik). Test erweitert.
- Top-3-Karte schrumpfte, wenn ein Top-Kandidat keinen Account-Datensatz hat: Nachrücker füllen jetzt auf.
- Payline-Abo-Kachel widersprach beim Monatswechsel den N2-Kacheln (live vs. Monatsbestand): zählt jetzt ebenfalls monatsbezogen über
aboStockCount(). - Checkout-Pflichtfeld-Meldung (N5) war hardcodiert deutsch → Sprachkey
order.source_comment_required(de/en/es/fr).
Performance:
lineBuckets()-Baum-Traversierung lief pro Dashboard-Request 3–5×: jetzt Singleton + Prozess-Memo (flushLineBucketsMemo()für Batch/Struktur-Änderungen;storeSnapshot()flusht selbst).levelAndCommissionForecast()war als einzige neue Kennzahl ungecacht → 6-h-Cache (level_forecast_*).- Snapshot-Cache-Key auf
live_business_v3_*(Payload-Format geändert).
Wiederverwendung/Konventionen:
- Neue User-Scopes
firstlinesOf()undnewPartnerIn()— EINE Definition für Firstlines (Kachel, Seite, Top 3) und Neupartner (KPI, Recruiter-Ranking, Top 3) statt 3–4 Kopien. SalesVolumePoints::totalPointsByUser()als gemeinsamer Batch-Helper (Firstlines-Seite, Recruiter-Volumen, Top-3-Volumen).- Slim-Cropper-Speicherlogik nach
Slim::storeCroppedImage()extrahiert (Profilfoto + beide Shop-Uploads nutzen sie). activeAbo-Scope statt handgeschriebener Prädikate (Payline-Zähler, Top 3, N2-Rückrechnung);formatDate()/getFullName(false)statt Inline-Kopien; Firstlines-Filter aufForm::open(Projektregel); Return-Types ergänzt.- Neue Tests:
TeamFirstlinesPageTest,ProfileImageTest(die einzigen bis dahin ungetesteten neuen Endpunkte).
Nachtrag (gleicher Tag, vom Kunden freigegeben) — die drei gemeldeten Punkte sind umgesetzt:
- ✅ VP-Nachtreparatur gehärtet: Der Live-Pfad (
Payment::addSponsorBuyingPoints→SalesPointsVolume::addSalesPointsVolume) schreibt jetzt dieshopping_order_idmit; der Repair dedupliziert primär darüber (immun gegen Umbenennungen), Alt-Zeilen ohne Referenz werden zählend je info-Text verbraucht (zwei gleiche Bestellungen ≠ eine). Scan-Fenster umfasst den Vormonat (Monatswechsel-Fälle heilen automatisch, Buchung weiterhin nur in den Zielmonat). Dedup ist sponsor-unabhängig mit zwei Fällen (Kundenentscheid 2026-08-19): Liegt die Buchung beim Firmenaccount (config('mivita.company_user_id'), User 6 — Registrierung über die Hauptwebseite ist ein User-Fehler, Punkte stehen immer dem Berater zu), wird automatisch umgebucht — im Zielmonat in place (eine Zeile, Syslog-Spur), aus einem abgeschlossenen Monat per neuer Buchung beim Berater + Gegenbuchung („VP-Storno") beim Firmenaccount im Zielmonat (der alte Monat bleibt unangetastet). Liegt sie bei einem anderen Berater (echte Umhängung), wird weiterhin nur gewarnt (skipped_other_sponsor+ Command-Tabelle + Log). Am Echtfall Anna Schall (Order 58797) auf dem Testsystem ausgeführt: Juli-Zeile bei #6 unverändert, Christina Schuder (#1241) +200 P. im August mit Order-Referenz, Firmenaccount −200 P. im August; Folgelauf idempotent. Käufer/Sponsoren und Bestandszeilen werden gebatcht geladen (N+1 weg). 5 neue Tests, 12/12 grün. - ✅ Incentive-Reparatur gehärtet:
removeStaleSponsorTrackings()überspringtm_sponsor = NULL(nur echter Sponsorwechsel löscht, mit Log-Spur);repairForeignKeys()läuft jetzt VOR der Stale-Bereinigung (Waisen-Logs ohne FK werden erst angebunden und dann konsistent mitbereinigt — vorher Doppelzählung möglich); N+1-Queries in Stale-Check undrebuildFromSourceTables()durch gebatchte Lookups ersetzt; Qualifikationsfenster zentral über neue HelperIncentive::qualificationWindow()/qualificationWindowContains()+ Scope (vorher 4 unabhängige Kopien — die Fehlerklasse des Dino-Portaro-Randtag-Bugs). Kleine dokumentierte Abweichung:registered_atbeim Rebuild =MIN(created_at)der Starterkit-Orders (statt „erste nach id"). 6 neue Tests (u. a. NULL-Sponsor bleibt erhalten, Randtag 23:15 zählt, End-to-End ohne Doppelzählung nach Umhängung). - ✅
.envaus dem Git-Tracking entfernt (git rm --cached;.gitignoreenthielt sie bereits,.env.exampleexistiert). Die Datei bleibt lokal/auf dem Server liegen — Pflege wie bisher per SFTP (vom Kunden bestätigt). Empfehlung bleibt: die bereits historisierten Keys (PAYONE, Stripe-Test, DHL) bei Gelegenheit rotieren.
Technische Querschnittsthemen
LiveCommissionService::buildSnapshot()erweitern (Level-/Punktefelder durchreichen) ist gemeinsame Voraussetzung für N1 und N4 — einmal bauen, zweimal nutzen. Cache-Key-Version erhöhen, damit alte 6-h-Caches nicht mit schmalem Payload kollidieren.- Performance Home-Dashboard: alle neuen Kennzahlen in die bestehenden 6-h-Caches integrieren (
home_payline_*-Muster); keine zusätzlichen Baum-Läufe. - Tests: je Paket Pest-Feature-Tests analog der Juli-Suite (
BackofficeStatisticsListsTest-Muster, Helfer mit eindeutigem Prefix, keineroute()-Nutzung). - Unkommittetes WIP beachten:
SponsorBuyingPointsRepairService+RepairSponsorBuyingPoints(Nacht-Repair der Sponsor-VP-Zeilen) liegen ungetrackt im Arbeitsverzeichnis und berühren dieselbe Status-5-Logik wie N3 — vor Beginn von N3 committen/abstimmen.