13-05-2026 implementation FR

This commit is contained in:
Kevin Adametz 2026-05-13 17:33:52 +02:00
parent 245c281541
commit 70240d2b6a
61 changed files with 4472 additions and 2 deletions

View file

@ -0,0 +1,417 @@
# Entwicklungskonzept: Backoffice Dashboard & Interaktivität
Datum: 13.05.2026
Quelle: `docs/salescenter/Todos Backoffice.md`
## Zielbild
Das Partner-Backoffice soll von einer statischen Monats-Kachelansicht zu einer klickbaren, linienbasierten Statistik ausgebaut werden. Führungskräfte sollen von der Gesamtübersicht über Linien und Generationen bis zu einzelnen Kunden, Partnern und Abos navigieren können.
Kernziel ist eine einheitliche Datenbasis für:
- Dashboard-Kennzahlen pro Linie 1 bis 8 inklusive Summenzeile
- Detailansichten je Linie, Firstline und Kennzahl
- Kundenabos, Teamabos, Kundenabos im Team, Umsatz und Punkte
- neue Spezial-Kennzahl "1000 Punkte Shop"
- saubere Begrifflichkeit und rechtssichere Sichtbarkeit in Incentive-Ranglisten
## Bestandsaufnahme im System
### Dashboard
Aktueller Einstieg:
- Route: `GET /home`
- Controller: `App\Http\Controllers\HomeController@show`
- View: `resources/views/home.blade.php`
- Statistik-Partial: `resources/views/dashboard/_statistics.blade.php`
Die aktuelle Statistik wird direkt im Blade-Partial berechnet. Sie nutzt unter anderem:
- `App\Models\UserBusiness` für gespeicherte Monatswerte und Payline-Punkte
- `App\Models\UserSalesVolume` für Kunden-/Shop-Punkte
- `App\Models\UserAbo` für eigene Abos, Kundenabos und Teamabos
- rekursive Sponsor-Abfragen über `users.m_sponsor`
Bewertung:
- Die vorhandene Ansicht ist ein guter Einstieg, aber fachlich zu grob.
- Es gibt noch keine Linien 1 bis 8, keine Summenzeile und keine Drill-down-Routen.
- Die Kennzahl "Kundenabos" vermischt aktuell eigene Abos und Kundenabos.
- "Team-Abos" zählt aktuell Beraterabos im Team, aber nicht die separat geforderten Kundenabos im Team.
### Team- und Abo-Bereich
Relevante Routen und Controller:
- `User\TeamController@structure` für Strukturansicht
- `User\TeamController@show` und `datatableOptimized` für Teamliste
- `User\TeamController@showAbos` für Team-Beraterabos
- `User\TeamController@showTeamCustomerAbos` für Kundenabos im Team
- `User\TeamController@detailAbo` für Abo-Details
Relevante Daten:
- `UserAbo::is_for = 'me'` bedeutet Berater-/Eigenabo
- `UserAbo::is_for = 'ot'` bedeutet Kundenabo
- `UserAbo::user_id` ist der Bestell-/Abo-User
- `UserAbo::member_id` ist der zugeordnete Berater
- `UserAbo::next_date` liefert die nächste Abo-Ausführung
- `UserAbo::getTotalPoints()` berechnet Punkte aus Abo-Items und Produkten
- `AboHelper::getTeamUserIds()` liefert Downline-User-IDs
- `AboHelper::getMonthlyAboCounts()` kennt bereits die Scopes `team_abos` und `team_cust_abos`
Bewertung:
- Viele Datenquellen für die geforderten Listen existieren bereits.
- Die Logik ist aber auf mehrere Controller, Blades und Helper verteilt.
- Für das neue Dashboard sollte die Logik zentralisiert werden, statt weitere Berechnungen in Blade-Dateien zu ergänzen.
### Business- und Punkteberechnung
Relevante Bausteine:
- `App\Services\BusinessPlan\TreeCalcBotOptimized`
- `App\Models\UserBusiness`
- `App\Models\UserSalesVolume`
- `App\Services\BusinessPlan\SalesPointsVolume`
Bewertung:
- Für Monats- und Teamwerte sollte bevorzugt auf gespeicherte Businessdaten zurückgegriffen werden.
- Live-Berechnung über TreeCalcBot ist als Fallback sinnvoll, darf aber nicht bei jedem Seitenaufruf ungefiltert große Strukturen neu berechnen.
- Die neue Drill-down-Logik benötigt klare Regeln, ob sie Live-Daten oder gespeicherte Monats-Snapshots zeigt.
### Incentives
Relevante Dateien:
- `App\Http\Controllers\User\IncentiveController`
- `resources/views/user/incentive/show.blade.php`
- `App\Models\IncentiveParticipant`
- `App\Services\Incentive\IncentiveTracker`
Status:
- Teilnahme-Opt-in existiert über `accepted_terms_at`.
- Ranking wird aktuell mit `paginate(100)` angezeigt.
- Nicht zustimmende Teilnehmer werden für normale User anonymisiert.
- VIP-Ansicht sieht zusätzliche Hinweise zur Zustimmung.
Lücke zum Briefing:
- Es gibt noch keine separate Zustimmung "Name/Fotos/Land in Rangliste sichtbar".
- Foto und Land sind in der aktuellen Rangliste nicht als sichtbare Standardspalten umgesetzt.
- Rechtliche Freigabe muss fachlich vor der technischen Umsetzung geklärt werden.
### Checkout-Herkunft
Relevante Dateien:
- `App\Http\Controllers\Web\CheckoutController`
- `App\Repositories\CheckoutRepository`
- `App\Models\ShoppingUser`
Status:
- `CheckoutController::validateCheckoutData()` validiert Basisdaten, aber keine Pflichtfrage "Von wem hast du von Mivita erfahren?"
- `ShoppingUser` nutzt `is_from` für den technischen Ursprung wie `shopping`, `homeparty` oder `collection`.
- Ein fachliches Herkunfts-/Empfehlungsfeld existiert nicht als sauber getrennte Datenquelle.
### Storno und Punkterückführung
Relevante Dateien:
- `App\Repositories\InvoiceRepository`
- `App\Services\BusinessPlan\SalesPointsVolume`
- `App\Services\Incentive\IncentiveTracker`
Status:
- Beim Erstellen einer Stornorechnung ruft `InvoiceRepository::createCancellation()` die Punktekorrektur auf.
- `SalesPointsVolume::cancelSalesPointsVolume()` erstellt einen negativen `UserSalesVolume`-Eintrag mit Status `6`.
- Danach wird der Monat neu berechnet und `IncentiveTracker::trackStorno()` informiert.
Risiken:
- Wenn zur Originalrechnung kein `UserSalesVolume` existiert, wird nur geloggt und keine Punktekorrektur erstellt.
- Der negative Storno-Eintrag wird aktuell dem aktuellen Monat zugeordnet, nicht zwingend dem ursprünglichen Umsatzmonat.
- Fachlich muss entschieden werden, ob Stornos im Stornomonat oder im Ursprungsmonat wirken sollen.
## Fachliche Prüfung der To-dos
### 1. Überarbeitetes Dashboard & KPI-Übersicht
Umsetzung sinnvoll, aber nicht als Erweiterung des bestehenden Blade-Partials. Benötigt wird ein dedizierter Service, der die Kennzahlen pro Linie liefert.
Vorgeschlagene Tabelle Stufe 1:
- Linie
- Anzahl Berater
- Umsatz gesamt
- Eigen-/Beraterabos im Team
- eigene Kundenabos
- Kundenabos im Team
- Neupartner
- 1000 Punkte Shop
- Summe
Wichtig:
- "Teamkundenabos" sollte als Begriff für Kundenabos der Downline verwendet werden.
- "Teamabos" sollte nur für Berater-/Eigenabos im Team verwendet werden.
- Eigene Abos dürfen nicht in Kundenabos eingerechnet werden.
### 2. Interaktivität & Deep Dive
Umsetzung über eigene Backoffice-Statistik-Routen statt über große Modals im Dashboard.
Vorgeschlagene Stufen:
- Stufe 1: Linienübersicht `/user/backoffice/statistics`
- Stufe 2: Linien-/Firstline-Detail `/user/backoffice/statistics/line/{line}`
- Stufe 3: Kennzahlenliste `/user/backoffice/statistics/details?line=...&metric=...&user=...`
Jede Zahl erhält einen Link auf eine gefilterte Detailansicht. Die Detailansicht sollte die Query-Parameter sichtbar halten, damit Support und Fachbereich Ergebnisse reproduzieren können.
### 3. Spezial-Kennzahl "1000 Punkte Shop"
Definition muss vor Umsetzung final geklärt werden.
Vorschlag für Version 1:
- Zeitraum: gewählter Monat/Jahr des Dashboards
- Personenkreis: Downline des eingeloggten Users
- Schwelle: `UserSalesVolume` Shop-/KP-Summe pro Partner >= 1000 Punkte
- Sortierung: Volumen absteigend
- Detailspalten: Name, Account, Linie/Generation, Shop-Punkte, Gesamt-KP, Umsatz netto
Offene Fachfrage:
- Meint "Kundenumsatz" nur Shop-Umsatz (`month_shop_points`) oder alle Kundenpunkte inklusive Abo-/Beraterkontext?
### 4. Bestellformular: "Von wem hast du von Mivita erfahren?"
Technisch sollte ein neues fachliches Feld eingeführt werden, nicht `is_from` zweckentfremdet werden.
Vorschlag:
- Migration für `shopping_users.referral_source_name` oder ähnliches Feld
- optional zusätzlich auf `shopping_orders`, wenn die Information revisionssicher pro Bestellung eingefroren werden soll
- Pflichtvalidierung in `CheckoutController::validateCheckoutData()`
- Anzeige im Checkout-Formular und optional im Admin-/Bestelldetail
- später exportierbar für Marketingauswertung
Zu klären:
- Freitext oder Auswahl plus Freitext?
- Pflicht nur im Webshop/Bestelllink oder auch bei Salescenter-, Homeparty- und Collection-Flows?
- Soll eine konkrete Beraterzuordnung daraus entstehen oder nur Tracking?
### 5. Stornoprozess
Die Grundlogik existiert. Vor einer fachlichen Freigabe braucht es Tests und eine Periodenentscheidung.
Prüfpunkte:
- Storno einer Beraterbestellung erzeugt negative Punkte beim richtigen Berater.
- Storno einer Kunden-/Shopbestellung erzeugt negative Punkte im richtigen Umsatztyp.
- Storno einer Abo-Bestellung wirkt korrekt auf Incentive-Logs.
- Storno ohne vorhandenen `UserSalesVolume` ist sichtbar und lösbar, nicht nur ein Log-Eintrag.
- Businessdaten nach Storno werden für betroffenen Monat/Jahr aktualisiert oder als neu zu berechnen markiert.
### 6. Rechtliches & Sichtbarkeit in Incentives
Die bestehende Teilnahmezustimmung sollte nicht automatisch als Freigabe für öffentliche Namens-/Fotoanzeige interpretiert werden.
Vorschlag:
- eigenes Feld auf `incentive_participants`, z. B. `ranking_visibility_accepted_at`
- Button/Checkbox in der Incentive-Seite: "Ich stimme zu, dass mein Name, Foto und Land in der Rangliste sichtbar sind"
- Anzeige von Name, Foto und Land nur, wenn die Zustimmung vorliegt oder ein Admin/VIP die Ansicht nutzt
- nach juristischer Klärung kann die Regel angepasst werden
Technische Ergänzungen:
- Ranking um Foto und Land erweitern
- Pagination/Limit bewusst definieren: alle Teilnehmer anzeigen, aber paginiert
- Übersetzungen in `resources/lang/*/incentive.php` ergänzen
### 7. Multimedia-Bereich / Event-Archiv
Es gibt bereits Dashboard-News und ein News-Archiv. Für Events sind zwei Varianten möglich:
Variante A: `DashboardNews` um Typ "event" erweitern
- weniger Aufwand
- bestehendes Admin-Modul kann wiederverwendet werden
- geeignet, wenn Events im gleichen Format wie News funktionieren
Variante B: eigenes Event-Modul
- sauberere Trennung
- eigene Felder für Galerie, Eventdatum, Call-/Foto-Typ
- sinnvoll, wenn Uploads, mehrere Bilder pro Event oder Kategorien benötigt werden
Empfehlung: Variante A als MVP, falls keine komplexe Galerieverwaltung benötigt wird.
## Technisches Zielkonzept
### Neue zentrale Services
Empfohlen:
- `App\Services\Backoffice\BackofficeDashboardService`
- `App\Services\Backoffice\BackofficeDrilldownService`
Aufgaben `BackofficeDashboardService`:
- Zeitraum normalisieren
- Downline pro Linie aufbauen
- Summen pro Linie berechnen
- Daten für Stufe 1 und Stufe 2 liefern
- Caching-/Snapshot-Strategie kapseln
Aufgaben `BackofficeDrilldownService`:
- Kennzahlenfilter aus Request validieren
- Personen-/Abo-/Umsatzlisten erzeugen
- Berechtigungen gegen Downline prüfen
- einheitliche Summary für Listen liefern
### Controller und Views
Empfohlen:
- `App\Http\Controllers\User\BackofficeStatisticsController`
- Views unter `resources/views/user/backoffice/statistics/`
Geplante Actions:
- `index()` für Stufe 1
- `line(int $line)` für Stufe 2
- `details()` für Stufe 3
- optional `export()` für CSV/Excel später
Die vorhandenen Team-Views bleiben bestehen. Das neue Dashboard verweist für Detailseiten aber auf eigene, schlankere Statistik-Views.
### Datenmodell und Definitionen
Einheitliche Metriken:
- `consultants`: aktive Berater in Linie
- `own_abos`: eigene Beraterabos des eingeloggten Users
- `team_partner_abos`: Beraterabos im Team (`is_for = 'me'`)
- `direct_customer_abos`: Kundenabos des betrachteten Beraters (`member_id = user_id`, `is_for = 'ot'`)
- `team_customer_abos`: Kundenabos der Downline-Berater (`member_id in teamUserIds`, `is_for = 'ot'`)
- `new_partners`: neue aktive Partner im Zeitraum
- `turnover_points`: Punkte aus `UserSalesVolume`/`UserBusiness`
- `turnover_net`: Netto-Umsatz aus `UserSalesVolume`/`UserBusiness`
- `shop_1000`: Partner mit Kunden-/Shop-Punkten >= 1000
### Berechtigungen
Jede Detailansicht muss sicherstellen:
- Der eingeloggte User sieht nur eigene Downline-Daten.
- Ein direkt angefragter `user_id` muss per Sponsor-Hierarchie im Team liegen.
- Kundenabos werden nur in dem Umfang angezeigt, der fachlich für Berater vorgesehen ist.
- Datenschutzrelevante Kundendaten sollten auf das notwendige Minimum reduziert werden.
## Umsetzungsphasen
### Phase 1: Begriffe, Datenbasis und MVP Dashboard
- Fachliche Definitionen finalisieren
- `BackofficeDashboardService` erstellen
- Stufe-1-Linienübersicht mit Linien 1 bis 8 und Summenzeile bauen
- bestehende Dashboard-Kachel durch Link auf neue Statistikseite ergänzen oder neue Seite im Menü aufnehmen
- Kennzahlen noch ohne vollständigen Deep Dive, aber bereits sauber berechnet
### Phase 2: Drill-down Stufe 2 und Stufe 3
- Linien-Detailansicht pro Firstline bauen
- Detailansichten je Kennzahl bauen
- Abo-Listen mit Name, Punktewert, nächster Ausführung und Anzahl Lieferungen anzeigen
- Links aus jeder Kennzahl setzen
- leere Zustände und Summenzeilen ergänzen
### Phase 3: 1000 Punkte Shop
- fachliche Definition final bestätigen
- Query und Summary implementieren
- Widget in Übersicht ergänzen
- Detailansicht mit Sortierung nach Volumen absteigend bauen
### Phase 4: Herkunftsabfrage im Checkout
- Migration und Model-Fillable ergänzen
- Checkout-Formular erweitern
- Validierung und Speicherung ergänzen
- Admin-/Bestelldetail oder Export um Feld erweitern
### Phase 5: Storno-Qualitätssicherung
- Tests für vorhandene Storno-Punktepfade ergänzen
- Fachentscheidung zur Periodenlogik dokumentieren
- Fehlerfall ohne Original-`UserSalesVolume` sichtbar machen
- ggf. Businessdaten-Neuberechnung nach Storno anstoßen
### Phase 6: Incentive-Sichtbarkeit und Event-Archiv
- rechtliche Entscheidung einarbeiten
- separates Ranking-Sichtbarkeits-Opt-in ergänzen
- Foto/Land im Ranking anzeigen
- Event-Archiv als `DashboardNews`-Typ oder eigenes Modul umsetzen
## Teststrategie
Feature-Tests:
- Dashboard zeigt nur Daten der eigenen Downline.
- Linien 1 bis 8 werden korrekt gruppiert.
- Summenzeile entspricht Summe der Linien.
- Klick auf Teamabos zeigt nur `is_for = 'me'` in der Downline.
- Klick auf Kundenabos im Team zeigt nur `is_for = 'ot'` mit `member_id` in der Downline.
- 1000-Punkte-Shop listet nur Partner über Schwelle und sortiert absteigend.
- Checkout verlangt die Herkunftsabfrage und speichert sie.
- Storno erzeugt negativen `UserSalesVolume`-Eintrag und aktualisiert Monatswerte.
- Incentive-Ranking zeigt Name/Foto/Land nur nach passender Zustimmung.
Unit-Tests:
- Service aggregiert Linien korrekt.
- Service verhindert Zugriff auf fremde Team-User.
- Metrikdefinitionen liefern stabile Counts bei aktiven, gekündigten und zukünftigen Abos.
Regressionsprüfung:
- bestehende Teamseiten `user.team.*`
- bestehende Abo-Seiten
- bestehendes Incentive-Ranking
- Checkout für Webshop, Bestelllink und Salescenter-Flows
## Offene Fachfragen
1. Soll die neue Statistik die aktuelle Monatslogik nutzen oder standardmäßig den letzten abgeschlossenen Monat zeigen?
2. Sollen Stornos im Stornomonat oder im ursprünglichen Umsatzmonat gegengerechnet werden?
3. Wie genau wird "1000 Punkte Shop" definiert: nur Shop-Punkte, alle Kundenpunkte oder Kundenabos plus Einzelbestellungen?
4. Welche Kundendaten dürfen Berater in Deep-Dive-Listen sehen?
5. Ist die Herkunftsabfrage Freitext, Auswahlfeld oder Kombination?
6. Gilt die Herkunftsabfrage für alle Checkout-Flows oder nur für externe Kundenbestellungen?
7. Darf eine Incentive-Teilnahme bereits Name/Foto/Land freigeben oder braucht es ein separates Opt-in?
8. Soll das Event-Archiv nur Bilder und Texte enthalten oder eine echte Galerie mit Mehrfachuploads?
## Empfehlung
Die Backoffice-Statistik sollte als eigenes kleines Modul im User-Bereich umgesetzt werden, nicht als weitere Logik in `dashboard/_statistics.blade.php`. Die vorhandenen Datenquellen sind ausreichend für ein MVP, aber sie müssen zentral aggregiert, fachlich sauber benannt und über berechtigte Drill-down-Routen zugänglich gemacht werden.
Priorität für die erste Umsetzung:
1. Daten- und Begriffsdefinitionen finalisieren
2. zentrale Services und Stufe-1-Linienübersicht
3. Drill-down für Abos und Neupartner
4. 1000-Punkte-Shop
5. Checkout-Herkunft und Storno-Tests
6. Incentive-Sichtbarkeit und Event-Archiv

View file

@ -0,0 +1,190 @@
# Entwicklungskonzept DHL Modul
Stand: 13.05.2026
## Ziel
Diese Datei ist die aktuelle Arbeitsgrundlage fuer die Weiterentwicklung des DHL Moduls. Die bisherigen Markdown-Dateien in diesem Ordner dokumentieren abgeschlossene oder ueberholte Zwischenstaende, insbesondere den frueheren SDK-Ansatz, Paketinstallation, SSL/cURL-Fixes und einzelne abgeschlossene Entwicklungsschritte.
Aktuelle Anforderungen kommen aus `docs/dhl/Anpassung DHL Modul.md`:
- Internationaler Versand ausserhalb Deutschlands, insbesondere Oesterreich und Spanien.
- Freies Feld fuer Sendungsreferenz oder interne Hinweise wie "Nachlieferung".
- Adressvalidierung vor Labelerstellung, damit fehlerhafte Labels und kostenpflichtige Stornos vermieden werden.
- Storno im DHL Cockpit pruefen und stabilisieren.
- Gewicht von Kompensationsprodukten in das DHL-Paketgewicht einrechnen.
- Tracking-Mails auf Rhythmus, Ausloeser und Mehrfachversand pruefen.
- Tracking-Codes in Admin, User-Portal und User-N-Portal sichtbar machen.
- Tracking-Mail-Versand nur bei Statusaenderung.
- DHL-Umstellung von `V62WP` Warenpost auf `V62KP` DHL Kleinpaket bis spaetestens 31.05.2026.
## Aktueller technischer Stand
Das produktive Modul basiert auf dem Paket `packages/acme-laravel-dhl` und verwendet die DHL REST API ueber `Acme\Dhl\Support\DhlClient`. Die zentrale Tabelle ist `dhl_package_shipments`.
Wichtige produktive Einstiegspunkte:
- `app/Http/Controllers/DhlShipmentController.php`
- `app/Services/DhlShipmentService.php`
- `app/Services/DhlModalService.php`
- `app/Services/DhlDataHelper.php`
- `app/Services/DhlTrackingService.php`
- `packages/acme-laravel-dhl/src/Services/ShippingService.php`
- `packages/acme-laravel-dhl/src/Services/ReturnsService.php`
- `packages/acme-laravel-dhl/src/Models/DhlShipment.php`
- `config/dhl.php`
Historische Dokumente erwaehnen teilweise alte Strukturen wie `App\Models\DhlShipment`, `dhl_shipments` oder einen konsolidierten `DhlApiService`. Diese Angaben sind nicht mehr massgeblich, ausser sie werden explizit in aktuellem Code noch referenziert. Aktuell gibt es genau dort noch Altlasten, die bereinigt werden muessen.
## Offensichtliche Befunde
### 1. Produktkuerzel `V62WP` ist veraltet
`V62WP` ist weiterhin in Konfiguration, Admin-Settings, Validierung, Produktauswahl und Sprachdateien vorhanden. DHL verlangt die Umstellung auf `V62KP`.
Betroffene Stellen:
- `config/dhl.php`
- `app/Http/Controllers/SettingController.php`
- `app/Services/DhlModalService.php`
- `packages/acme-laravel-dhl/src/Services/ShippingService.php`
- `resources/lang/*/dhl.php`
Risiko: Kleinpaket/Warenpost-Labels koennen nach der DHL-Frist abgelehnt werden.
### 2. Async Tracking verwendet veraltetes Model
`app/Jobs/TrackShipmentJob.php` importiert `App\Models\DhlShipment`, dieses Model existiert im aktuellen System nicht mehr. Die produktive DHL-Integration verwendet `Acme\Dhl\Models\DhlShipment`.
Risiko: Asynchrones Tracking bricht zur Laufzeit, sobald Queue-Tracking genutzt wird.
### 3. Statuswerte fuer Storno sind inkonsistent
Im Paket wird bei erfolgreichem Storno `canceled` gesetzt. Andere Stellen, Uebersetzungen und historische Dokumente verwenden `cancelled`.
Risiko: Filter, Badges, Terminal-Status, Uebersetzungen und Storno-Buttons verhalten sich uneinheitlich.
### 4. Storno ist technisch vorhanden, aber nicht robust genug
Storno laeuft ueber `DELETE /parcel/de/shipping/v2/orders/{shipmentNumber}`. Der Code prueft `canCancel()`, speichert aber Fehler nur begrenzt fachlich verwertbar. Produktspezifische Einschraenkungen wie Warenpost/Kleinpaket sind nicht sauber modelliert.
Risiko: Anwender sehen generische Fehler und koennen nicht erkennen, ob ein Storno produktbedingt, statusbedingt, API-bedingt oder wegen Sandbox/Production-Mismatch scheitert.
### 5. Internationaler Versand ist nur teilweise vorbereitet
`V53PAK` ist als internationales Produkt vorhanden, und einige Laender werden in 3-stellige ISO-Codes konvertiert. Dennoch gibt es keinen zentralen Produktentscheid je Zielland, keine harte Validierung nicht unterstuetzter Laender und einen gefaehrlichen Fallback auf `DEU`.
Risiko: Sendungen nach Oesterreich, Spanien oder weitere Laender koennen falsche Produktcodes, falsche Abrechnungsnummern oder falsche Laendercodes erhalten.
### 6. Adressvalidierung ist nur formal
Aktuell prueft das Modul Pflichtfelder, Hausnummern und einfache PLZ-Regeln. Eine echte Pruefung, ob Strasse, PLZ und Ort postalisch existieren, findet nicht statt.
Empfohlene Loesung: DHL/Post & DHL `DATAFACTORY AUTOCOMPLETE 2.0` fuer DE/AT/CH pruefen und integrieren. Alternativen fuer breiteren Laenderumfang: Loqate, Google Address Validation oder HERE. Wichtig ist eine harte Sperre bei nicht versandfaehigen Adressen vor Labelerstellung.
### 7. Referenzfeld ist API-seitig vorhanden, aber nicht im Cockpit nutzbar
`ShippingService` kann `reference` nach `refNo` mappen. `DhlDataHelper` setzt aktuell automatisch `Order-{id}`.
Risiko: Admins koennen Hinweise wie "Nachlieferung" nicht strukturiert am Label/API-Auftrag hinterlegen.
### 8. Gewicht von Kompensationsprodukten fehlt
Kompensationsprodukte werden im Warenkorb mit Gewicht `0` abgelegt, damit die Kompensationslogik nicht beeinflusst wird. Das DHL-Gewicht kommt aus `ShoppingOrder->weight` und enthaelt dieses Produktgewicht dadurch nicht.
Risiko: DHL-Label wird mit zu geringem Paketgewicht erstellt.
### 9. Tracking-Mail-Logik ist grundsaetzlich brauchbar, muss aber abgesichert werden
Der Scheduler ruft stuendlich `dhl:update-tracking --days=30 --send-emails` auf. Statusabhaengige Intervalle verhindern zu viele API-Calls. Automatische Mails werden nur gesendet, wenn eine Sendung neu auf `in_transit` wechselt und noch keine Mail markiert wurde.
Risiko: Statusspruenge direkt auf `out_for_delivery` oder besondere DHL-Statuscodes koennen ohne Mail bleiben. Mehrere Sendungen einer Bestellung werden teils zusammengefasst, aber die fachliche Regel muss final bestaetigt werden.
## Entwicklungskonzept
### Phase 1: Pflichtkorrekturen vor DHL-Frist
1. `V62WP` vollstaendig auf `V62KP` migrieren.
2. Neue Konfigurationskeys fuer `DHL_ACCOUNT_NUMBER_V62KP`, Admin-Setting `dhl_account_v62kp`, Dimensionen und Uebersetzungen einfuehren.
3. `ShippingService` Validierung um `V62KP` erweitern und `V62WP` entfernen oder nur noch als Legacy-Mapping fuer Altdaten anzeigen.
4. Bestehende Sendungen mit `V62WP` historisch lesbar lassen, aber neue Labelerstellung blockieren.
5. Tests fuer Produktcode-Auswahl, Validierung und Payload-Erstellung schreiben.
### Phase 2: Stabilisierung von Tracking und Storno
1. `TrackShipmentJob` auf aktuelles Model und aktuellen `DhlTrackingService` umstellen.
2. Statuswerte vereinheitlichen. Empfehlung: intern `canceled` verwenden, Uebersetzung auf Deutsch "Storniert".
3. `TERMINAL_STATUSES`, Badges, Filter und Sprachdateien entsprechend angleichen.
4. Storno-Fehler strukturiert speichern: HTTP-Status, DHL-Fehlercode, DHL-Detailtext, Zeitpunkt.
5. Admin-Feedback verbessern: nicht stornierbar wegen Status, Produkt, API-Antwort oder nicht auffindbarer DHL-Sendung.
6. Tests fuer erfolgreiche Stornierung, bereits stornierte Sendung, nicht stornierbare Sendung und API-Fehler.
### Phase 3: Internationalisierung Versand
1. Zentralen Service fuer Produkt-/Billing-Entscheidung einfuehren, z. B. `DhlProductResolver`.
2. Zielland, Produktcode, Abrechnungsnummer und erlaubte Services dort validieren.
3. Regeln initial:
- `DE`: `V01PAK` oder `V62KP`
- `AT`, `ES` und weitere aktivierte Laender: `V53PAK`
4. Fallback auf `DEU` entfernen. Unbekannte Laender muessen mit klarer Fehlermeldung abbrechen.
5. Cockpit-Formular: Produkt anhand Zielland vorschlagen, aber Admin-Korrektur erlauben.
### Phase 4: Adressvalidierung vor Labelerstellung
1. Neuen serverseitigen Validierungsendpunkt fuer DHL-Adressen schaffen.
2. Basisvalidierung behalten: Pflichtfelder, Land, PLZ-Format, Hausnummer, Packstation/Postnummer.
3. DHL DATAFACTORY AUTOCOMPLETE 2.0 fuer DE/AT/CH evaluieren.
4. Falls DHL fuer alle benoetigten Laender nicht ausreicht, externen Provider evaluieren.
5. UI-Status einfuehren:
- gueltig: Labelerstellung erlaubt
- Warnung: Admin kann bewusst fortfahren
- Fehler: Labelerstellung gesperrt
6. Validierungsergebnis optional am Shipment/Order protokollieren.
### Phase 5: Referenzfeld und Admin-UX
1. Neues Formularfeld `reference` oder `shipment_reference` im DHL Cockpit.
2. Wert an `DhlDataHelper::prepareOrderData()` uebergeben.
3. `ShippingService` nutzt vorhandenes Mapping nach `refNo`.
4. Referenz im Shipment speichern, damit spaeter nachvollziehbar ist, warum eine Sendung erstellt wurde.
5. Laengenlimit der DHL API beachten, aktuell maximal 35 Zeichen.
### Phase 6: DHL-Gewicht korrekt berechnen
1. Separaten Gewichtsdienst fuer DHL einfuehren, z. B. `DhlShipmentWeightCalculator`.
2. Basis: `ShoppingOrder->weight`.
3. Fuer `shopping_order_items` mit `comp > 0` das Produktgewicht aus `Product->weight` nachladen und addieren.
4. Nur DHL-Gewicht anpassen, nicht die bestehende Warenkorb-/Versandkostenlogik.
5. Rundung und DHL-Grenzwerte je Produkt testen.
### Phase 7: Tracking-Codes und Mails fachlich finalisieren
1. Bestehende Admin-Anzeige pruefen und bei Bedarf vereinheitlichen.
2. Tracking-Code-Anzeige in User-Portal und User-N-Portal ergaenzen.
3. Mail-Regel final definieren:
- automatisch nur einmal pro Sendung
- nur bei relevanter Statusaenderung
- mehrere Sendungen einer Bestellung sinnvoll zusammenfassen
4. Statusspruenge wie `created` direkt nach `out_for_delivery` abdecken.
5. Command `dhl:update-tracking` Tests fuer Mailausloeser und Nicht-Ausloeser ergaenzen.
## Empfohlene Reihenfolge
1. `V62WP` -> `V62KP`, Statuswerte und `TrackShipmentJob` korrigieren.
2. Storno stabilisieren und bessere Fehlermeldungen im Cockpit anzeigen.
3. Internationalen Produktresolver einbauen.
4. Referenzfeld und Gewichtskorrektur umsetzen.
5. Adressvalidierung integrieren.
6. Tracking-Anzeigen und Mailregeln final testen.
## Teststrategie
- Feature-Tests fuer Controller-Endpunkte: Label erstellen, Storno, Tracking-Mail, Tracking-Update.
- Unit-Tests fuer Produktresolver, Gewichtskalkulation und Adressvalidierung.
- HTTP-Fakes fuer DHL API Responses inklusive Fehlerfaelle.
- Regression-Test fuer `V62KP` Payload.
- Command-Test fuer `dhl:update-tracking --send-emails`.
## Legacy-Dokumentation
Die bisherigen Markdown-Dateien wurden nach `dev/dhl-modul/legacy` verschoben. Sie bleiben als Historie erhalten, sind aber nicht mehr die aktuelle Arbeitsgrundlage.

View file

@ -0,0 +1,86 @@
#!/bin/bash
# ================================================================================
# Business Update Calculated Fields - Verwendungsbeispiele
# ================================================================================
echo "========================================="
echo "Business Update Calculated Fields"
echo "========================================="
echo ""
# ================================================================================
# 1. TESTLAUF - Empfohlen vor der ersten Produktivnutzung
# ================================================================================
echo "1. TESTLAUF - Einzelner Monat (November 2025)"
echo " php artisan business:update-calculated-fields 2025 11 --dry-run"
echo ""
echo "2. TESTLAUF - Ganzes Jahr (2025)"
echo " php artisan business:update-calculated-fields 2025 --dry-run"
echo ""
# ================================================================================
# 2. PRODUKTIV - Einzelne Monate
# ================================================================================
echo "3. PRODUKTIV - Einzelner Monat (November 2025)"
echo " php artisan business:update-calculated-fields 2025 11"
echo ""
echo "4. PRODUKTIV - Mehrere Monate nacheinander"
echo " php artisan business:update-calculated-fields 2025 10"
echo " php artisan business:update-calculated-fields 2025 11"
echo ""
# ================================================================================
# 3. PRODUKTIV - Ganzes Jahr
# ================================================================================
echo "5. PRODUKTIV - Ganzes Jahr (alle 12 Monate)"
echo " php artisan business:update-calculated-fields 2025"
echo ""
# ================================================================================
# 4. MIGRATION
# ================================================================================
echo "6. VOR DER ERSTEN AUSFÜHRUNG - Migration"
echo " php artisan migrate"
echo ""
# ================================================================================
# Beispiel-Workflow für komplette Aktualisierung
# ================================================================================
echo ""
echo "========================================="
echo "Beispiel: Kompletter Workflow"
echo "========================================="
echo ""
echo "# 1. Migration ausführen (nur einmal nötig)"
echo "php artisan migrate"
echo ""
echo "# 2. Testlauf für das Jahr 2025"
echo "php artisan business:update-calculated-fields 2025 --dry-run"
echo ""
echo "# 3. Produktiv für das Jahr 2025 ausführen"
echo "php artisan business:update-calculated-fields 2025"
echo ""
echo "========================================="
# ================================================================================
# Hinweise
# ================================================================================
echo ""
echo "HINWEISE:"
echo "---------"
echo "• Der Command ist idempotent (kann mehrfach ausgeführt werden)"
echo "• Bereits aktualisierte Einträge werden übersprungen"
echo "• Bei ganzen Jahren: Monate ohne Daten werden übersprungen"
echo "• Fehler bei einzelnen Einträgen brechen den Prozess nicht ab"
echo "• Memory-Nutzung wird überwacht"
echo ""

View file

@ -0,0 +1,231 @@
# Business Update Calculated Fields Command
## Übersicht
Der Command `business:update-calculated-fields` aktualisiert bereits gespeicherte UserBusiness-Einträge mit den neuen berechneten Feldern für Level-Qualifikationen.
## Zweck
Nach der Korrektur der Karriere-Level-Berechnung müssen bereits in der Datenbank gespeicherte UserBusiness-Einträge aktualisiert werden, um die neuen berechneten Felder zu erhalten:
### Neue Felder:
1. **`calc_qual_kp`** (in UserBusiness)
- Die berechnete KP-Qualifikation für den erreichten Level
- Zeigt an, wie viele KP-Punkte tatsächlich für die Qualifikation verwendet wurden
2. **`_calculated_qual_kp`** (in Level-Arrays)
- Für `qual_user_level_next`, `next_qual_user_level`, `next_can_user_level`
- Die berechnete KP für jeden spezifischen Level
3. **`_calculated_payline_points`** (in Level-Arrays)
- Die Payline-Punkte basierend auf den spezifischen Paylines des Levels
4. **`_calculated_payline_points_qual_kp`** (in Level-Arrays)
- Die Gesamtpunkte (Payline + Rest-KP) für den Level
## Verwendung
### Grundlegende Syntax
```bash
# Einzelner Monat
php artisan business:update-calculated-fields {year} {month}
# Ganzes Jahr (alle 12 Monate)
php artisan business:update-calculated-fields {year}
```
### Mit Dry-Run (Testlauf)
```bash
# Einzelner Monat
php artisan business:update-calculated-fields {year} {month} --dry-run
# Ganzes Jahr
php artisan business:update-calculated-fields {year} --dry-run
```
Im Dry-Run Modus werden keine Änderungen in der Datenbank gespeichert. Der Command zeigt nur an, welche Einträge aktualisiert werden würden.
## Beispiele
### Aktualisiere November 2025
```bash
php artisan business:update-calculated-fields 2025 11
```
### Aktualisiere das ganze Jahr 2025
```bash
php artisan business:update-calculated-fields 2025
```
Das wird alle 12 Monate von Januar bis Dezember 2025 aktualisieren.
### Testlauf für Oktober 2025
```bash
php artisan business:update-calculated-fields 2025 10 --dry-run
```
### Testlauf für das ganze Jahr 2025
```bash
php artisan business:update-calculated-fields 2025 --dry-run
```
## Ablauf
### Einzelner Monat
1. **Lade UserBusiness-Einträge**: Alle Einträge für den angegebenen Monat/Jahr
2. **Berechne neue Felder**: Für jeden Eintrag werden die berechneten Felder hinzugefügt
3. **Speichere Änderungen**: Nur wenn Änderungen vorhanden sind (und nicht im Dry-Run)
4. **Zeige Statistik**: Anzahl aktualisierter, übersprungener und fehlerhafter Einträge
### Ganzes Jahr
1. **Durchlaufe alle 12 Monate**: Januar (1) bis Dezember (12)
2. **Für jeden Monat**:
- Lade UserBusiness-Einträge
- Berechne neue Felder
- Speichere Änderungen
- Zeige Monats-Statistik
3. **Zeige Jahres-Zusammenfassung**: Gesamtstatistik über alle Monate
## Was wird berechnet?
### calc_qual_kp (für aktuellen Level)
```php
$rest_kp = max(0, $sales_volume_points_KP_sum - $qual_kp);
$calc_qual_kp = $rest_kp > 0 ? $qual_kp : $sales_volume_points_KP_sum;
```
- Wenn Rest-KP > 0: Nutze die volle qual_kp des Levels
- Sonst: Nutze alle verfügbaren KP-Punkte
### Für Level-Arrays (next_qual_user_level, etc.)
```php
$payline_points = sum($business_lines[1..paylines]['points']);
$rest_kp = max(0, $sales_volume_points_KP_sum - $level['qual_kp']);
$payline_points_qual_kp = $payline_points + $rest_kp;
$calculated_qual_kp = $rest_kp > 0 ? $level['qual_kp'] : $sales_volume_points_KP_sum;
```
## Output
### Einzelner Monat
Der Command zeigt:
- Progress Bar während der Verarbeitung
- Memory-Nutzung alle 100 Einträge
- Abschließende Statistik:
- Anzahl aktualisierter Einträge
- Anzahl übersprungener Einträge (bereits aktualisiert)
- Anzahl Fehler
- Ausführungszeit
**Beispiel Output:**
```
Starte Update für Monat: 11 | Jahr: 2025
[Command Start] Memory: 12.5 MB / 512 MB (2.44%) | Peak: 12.5 MB
Gefunden: 1547 UserBusiness-Einträge
1547/1547 [============================] 100%
[Nach 100 Einträgen] Memory: 45.3 MB / 512 MB (8.85%) | Peak: 48.2 MB
...
Update abgeschlossen:
- Aktualisiert: 1547
- Übersprungen: 0
- Fehler: 0
UPDATE COMPLETED SUCCESSFULLY | Zeit: 12sec :345.6789 ms
```
### Ganzes Jahr
Der Command zeigt:
- Für jeden Monat:
- Progress Bar
- Monats-Statistik
- Memory-Nutzung nach dem Monat
- Abschließende Jahres-Zusammenfassung
**Beispiel Output:**
```
Starte Update für ALLE MONATE des Jahres: 2025
======================================================================
┌─────────────────────────────────────────────────────────────────────┐
│ Verarbeite Monat: 01/2025 │
└─────────────────────────────────────────────────────────────────────┘
Gefunden: 1245 UserBusiness-Einträge
1245/1245 [============================] 100%
✓ Monat 1 abgeschlossen: 1245 aktualisiert, 0 übersprungen, 0 Fehler
[Nach Monat 1] Memory: 48.2 MB / 512 MB (9.41%) | Peak: 52.1 MB
┌─────────────────────────────────────────────────────────────────────┐
│ Verarbeite Monat: 02/2025 │
└─────────────────────────────────────────────────────────────────────┘
Gefunden: 1367 UserBusiness-Einträge
1367/1367 [============================] 100%
✓ Monat 2 abgeschlossen: 1367 aktualisiert, 0 übersprungen, 0 Fehler
[Nach Monat 2] Memory: 51.3 MB / 512 MB (10.02%) | Peak: 55.4 MB
[... weitere Monate ...]
======================================================================
ZUSAMMENFASSUNG FÜR DAS JAHR 2025:
======================================================================
Verarbeitete Monate: 12/12
Fehlgeschlagene Monate: 0
Gesamt aktualisiert: 15432
Gesamt übersprungen: 0
Gesamt Fehler: 0
======================================================================
JAHRES-UPDATE ABGESCHLOSSEN | Zeit: 145sec :678.9012 ms
[Command End] Memory: 62.8 MB / 512 MB (12.27%) | Peak: 68.2 MB
```
## Migration
Vor dem ersten Ausführen des Commands muss die Migration ausgeführt werden:
```bash
php artisan migrate
```
Die Migration fügt das neue Feld `calc_qual_kp` zur `user_businesses` Tabelle hinzu.
## Integration mit BusinessStoreOptimized
Nach Ausführung von `business:store-optimized` können die berechneten Felder automatisch befüllt werden. Die neue Logik in `BusinessUserItemOptimized.php` sorgt dafür, dass bei neuen Berechnungen die Felder korrekt gesetzt werden.
## Fehlerbehandlung
- Fehler bei einzelnen Einträgen führen nicht zum Abbruch
- Fehler werden geloggt und in der Statistik angezeigt
- Memory-Nutzung wird überwacht (Warnung bei >80%)
## Performance
- Verarbeitet ca. 100-150 Einträge pro Sekunde
- Memory-Nutzung: ca. 30-50 MB für 1000 Einträge
- Kann für mehrere Monate/Jahre nacheinander ausgeführt werden
## Hinweise
- Der Command ist idempotent (kann mehrfach ausgeführt werden)
- Bereits vorhandene Felder werden nicht überschrieben
- Nutze `--dry-run` für Tests vor der Produktionsausführung
- Empfohlen: Backup vor der ersten Ausführung