# Development Backlog - 22.01.2026 ## Status-Legende - `[x]` Erledigt - `[ ]` Offen - `[!]` Hohe Priorität - `[?]` Klärungsbedarf --- ## ERLEDIGTE AUFGABEN ### [x] Produkt-Slugs anpassen - **Status:** Erledigt - **Beschreibung:** Slug kann direkt im Admin geändert werden - **URL:** https://gesundheit.mivita.care/produkte/black-friday-week ### [x] WWW-Redirect entfernen - **Status:** Erledigt - **Beschreibung:** Domain/Subdomain funktioniert ohne WWW-Prefix ### [x] Abo-Anpassungen (Protokoll Claudia) - **Status:** Erledigt - **Änderungen:** - Checkbox für AGB vor Abo-Abschluss - Änderungen erst nach 6 Ausführungen möglich - Nur Liefertag + Lieferadresse änderbar - Lieferadresse sync mit Benutzerdaten --- ## OFFENE AUFGABEN --- ### [X] 1. NEWS: Download-Center Verlinkung **Priorität:** Hoch ✅ **ERLEDIGT** **Bereich:** Dashboard / News **Problem:** Benutzer finden das Download-Center nicht. Nur Verweis reicht nicht. **Lösung implementiert:** ✅ Strukturiertes JSON-Feld `file_links` für Datei-Links pro Sprache ✅ Admin-Formular mit Select2-Dropdown zur Auswahl von DC-Dateien ✅ Mehrsprachige Unterstützung (DE, EN, ES) ✅ Schöne Button-Darstellung im Dashboard mit Icons ✅ Direkte Links zum Download-Center **Implementierte Dateien:** 1. **Migration:** `2026_01_23_120458_add_file_links_to_dashboard_news_table.php` - Neues JSON-Feld `file_links` in `dashboard_news` Tabelle 2. **Model:** `app/Models/DashboardNews.php` - `file_links` zu `$fillable` und `$casts` hinzugefügt - Neue Methoden: `getFileLinks($lang)`, `hasFileLinks($lang)` 3. **Admin-Formular:** `resources/views/admin/site/news/form.blade.php` - Datei-Link-Sektion für jede Sprache - Select2-Dropdown mit allen aktiven DC-Dateien - JavaScript zum Hinzufügen/Entfernen von Links - Dynamische Label-Eingabe 4. **Frontend-View:** `resources/views/dashboard/_news.blade.php` - Anzeige der Datei-Links als grüne Download-Buttons - **Direkter Download-Link** über `route('storage_file', [$file->id, 'dc_file', 'download'])` - Icons mit Ionicons - Responsive Darstellung 5. **Übersetzungen:** - DE: `resources/lang/de/backend.php` - EN: `resources/lang/en/backend.php` - ES: `resources/lang/es/backend.php` - Neue Keys: `file_links`, `file_links_hint`, `link_label`, `select_file`, `add_file_link` **JSON-Struktur:** ```json { "de": [ {"file_id": 123, "label": "Preisliste herunterladen"}, {"file_id": 456, "label": "Produktkatalog öffnen"} ], "en": [ {"file_id": 789, "label": "Download Price List"} ] } ``` **Verwendung im Admin:** 1. News bearbeiten → Zum jeweiligen Sprach-Tab scrollen 2. "Datei-Link hinzufügen" klicken 3. Label eingeben (z.B. "Preisliste herunterladen") 4. Datei aus Dropdown auswählen 5. Speichern → Links erscheinen prominent im Dashboard --- ### [X] 2. Points mit Dezimalstellen (DECIMAL statt INT) **Priorität:** Hoch **Bereich:** Marketingplan / Provisionsberechnung **Problem:** Punkte werden aktuell als INT gespeichert. Kommazahlen bei Produkten werden falsch berechnet/gerundet. **Anforderung:** - Alle Punkte-Felder auf DECIMAL umstellen - Berechnung im gesamten Marketingplan anpassen **Punkte-Ursprung (Produkte):** | Parameter | Wert | |-----------|------| | Model | `App\Models\Product` | | Feld | `points` (INT) → `DECIMAL(10,2)` | | Zusätzlich | `sponsor_buying_points`, `sponsor_buying_points_amount` | **Punkte-Aggregation (Sales Volume):** | Parameter | Aktuell | Neu | |-----------|---------|-----| | Model | `App\Models\UserSalesVolume` | - | | Tabelle | `user_sales_volumes` | - | | Datentyp | `INT` | `DECIMAL(10,2)` | **Betroffene Spalten `user_sales_volumes`:** ```sql ALTER TABLE user_sales_volumes MODIFY points DECIMAL(10,2), MODIFY month_points DECIMAL(10,2), MODIFY month_KP_points DECIMAL(10,2), MODIFY month_TP_points DECIMAL(10,2), MODIFY month_shop_points DECIMAL(10,2); ``` **Betroffene Spalten `user_business`:** ```sql ALTER TABLE user_business MODIFY sales_volume_points DECIMAL(10,2), MODIFY sales_volume_points_shop DECIMAL(10,2), MODIFY sales_volume_points_sum DECIMAL(10,2); ``` **Betroffene Spalten `products`:** ```sql ALTER TABLE products MODIFY points DECIMAL(10,2); ``` **Weitere Models mit `points`:** - `App\Models\ShoppingOrder` → `points` - `App\Models\ShoppingOrderItem` → `points` - `App\Models\ShoppingCollectOrder` → `points` - `App\Models\HomepartyUserOrderItem` → `points` **Betroffene Services:** - `app/Services/BusinessPlan/TreeCalcBotOptimized.php` - Alle Berechnungen in `app/Services/BusinessPlan/` **Migration erforderlich:** Ja (mehrere ALTER TABLE) --- ### [X] 3. Vorkasse: Verwendungszweck deutlich machen **Priorität:** Hoch **Bereich:** Checkout / Payment / E-Mail **Problem:** Kunden geben falschen Verwendungszweck an. System kann Zahlung nicht zuordnen. **Anforderung:** - Payone TXID als Verwendungszweck deutlich hervorheben - Hinweis im Checkout, in E-Mail und im Kundenkonto **Technische Details:** | Parameter | Wert | |-----------|------| | Clearingtype | `vor` (Vorkasse) | | Controller | `App\Http\Controllers\Pay\PayoneController` | | Checkout-View | `resources/views/web/templates/checkout.blade.php:898` | | Mail-Template | `resources/views/emails/order_*.blade.php` | | Kundenkonto | `resources/views/user/order/*.blade.php` | **Verwendungszweck-Feld (KORRIGIERT):** | Falsch | Richtig | |--------|---------| | `shopping_payments.reference` | `payment_transactions.txid` | **Zugriff auf TXID:** ```php // Model: App\Models\PaymentTransaction $transaction = PaymentTransaction::where('shopping_payment_id', $payment->id)->first(); $verwendungszweck = $transaction->txid; // Oder via transmitted_data JSON $txid = $transaction->transmitted_data['txid'] ?? null; ``` **Umsetzung:** 1. **Checkout:** Alert-Box mit Verwendungszweck-Hinweis bei Vorkasse-Auswahl 2. **E-Mail:** Hervorgehobener Block mit Bankdaten + TXID als Verwendungszweck 3. **Kundenkonto:** Info-Box bei unbezahlten Vorkasse-Bestellungen mit TXID **Beispiel-Text:** ``` WICHTIG: Bitte geben Sie als Verwendungszweck ausschließlich folgende Nummer an: [TXID: 123456789] Nur so kann Ihre Zahlung automatisch zugeordnet werden. ``` --- ### [X] 4. Paketbox/Packstation Feld **Status:** ✅ ERLEDIGT **Priorität:** Mittel **Bereich:** Adressverwaltung / Checkout **Anforderung:** - Neues Feld "DHL Postnummer" nur bei Lieferadresse - Automatische Erkennung: Wenn Postnummer angegeben → Packstation-Modus **Technische Details:** | Parameter | Wert | |-----------|------| | Tabelle | `shopping_users` | | Model | `App\Models\ShoppingUser` | **Durchgeführte Implementierung:** 1. ✅ **Migration erstellt und ausgeführt** - Datei: `database/migrations/2026_01_22_181707_add_shipping_postnumber_to_shopping_users_table.php` - Spalte: `shipping_postnumber VARCHAR(20) NULLABLE` 2. ✅ **Model angepasst** (`app/Models/ShoppingUser.php`) - Feld im `$fillable` Array - Methode `hasPostnumber()` hinzugefügt 3. ✅ **Checkout-Formular** (`resources/views/web/templates/checkout.blade.php`) - Eingabefeld für Postnummer nach Telefon-Feld - Placeholder: "12345678" - JavaScript-Validierung für Packstation-Format 4. ✅ **DHL Modal** (`resources/views/admin/dhl/modal_in_order_shipment.blade.php`) - Postnummer-Feld hinzugefügt 5. ✅ **DHL Service** (`app/Services/DhlModalService.php`) - Postnummer wird korrekt an DHL API übergeben 6. ✅ **Kundendetail-Ansicht** (`resources/views/admin/customer/_customer_detail.blade.php`) - Postnummer wird mit Badge angezeigt - Hinweistext für Packstation-Lieferung 7. ✅ **Kunden-Bearbeitungsformular** (`resources/views/admin/customer/_edit.blade.php`) - Postnummer-Feld direkt nach Telefon-Feld - Mit Hinweistext und Placeholder - Wird in User-Bereich und Admin-Bereich verwendet 8. ✅ **Lieferschein-PDF** (`resources/views/pdf/delivery.blade.php`) - Postnummer wird fett gedruckt in der Lieferadresse angezeigt - Format: "DHL Postnummer: 12345678" 9. ✅ **Alert-Box im Formular** (`resources/views/admin/customer/_edit.blade.php`) - Deutliche gelbe Warning-Box erscheint, wenn Postnummer ausgefüllt wird - Erklärt klar, dass bei Packstation-Lieferung: - Straße/Nr. = "Packstation [Nummer]" (z.B. "Packstation 145") - PLZ/Ort = Standort der Packstation (nicht Wohnadresse!) - JavaScript-gesteuerte Ein-/Ausblendung bei Input - Dismissable (kann vom User geschlossen werden) 10. ✅ **User-Account Formular** (`resources/views/user/user_form.blade.php`) - Migration für `user_accounts` Tabelle erstellt und ausgeführt - Datei: `database/migrations/2026_01_23_102622_add_shipping_postnumber_to_user_accounts_table.php` - Model `UserAccount` im `$fillable` Array erweitert - Postnummer-Feld nach shipping_phone hinzugefügt - Identische Alert-Box wie im Admin-Formular - Identisches JavaScript für Ein-/Ausblendung 11. ✅ **Checkout-Formular** (`resources/views/web/templates/checkout.blade.php`) - Alte kleine Info-Box (alert-info) ersetzt durch große Warning-Box - Identische gelbe Alert-Box wie in allen anderen Formularen - JavaScript bereits vorhanden (togglePackstationHint) - Wird automatisch ein-/ausgeblendet bei Input 12. ✅ **CheckoutRepository Datenübertragung** (`app/Repositories/CheckoutRepository.php`) - **Problem behoben:** Postnummer wurde nicht von UserAccount zu ShoppingUser übertragen - `shoppingUserByAuthUser()` erweitert (Zeile 350): Übertragung für eingeloggte User - `shoppingUserAuthData()` erweitert (Zeile 418 + 430): Übertragung für Salescenter-Bestellungen - Postnummer wird jetzt korrekt im Checkout-Formular angezeigt 13. ✅ **Anzeige-Views erweitert** - Postnummer wird überall angezeigt: - ✅ `admin/sales/_detail.blade.php` - Admin Bestelldetails - ✅ `admin/sales/_detail_homparty_user.blade.php` - Homeparty Bestelldetails - ✅ `portal/order/_detail.blade.php` - Portal Bestelldetails - ✅ `emails/checkout_status.blade.php` - Bestellstatus E-Mail - ✅ `emails/checkout.blade.php` - Checkout E-Mail (2 Stellen) - ✅ `admin/modal/is_like_member.blade.php` - Kundenzuordnung Modal (2 Stellen) - **Format:** Badge mit Icon + Hinweistext in Web-Views - **Format:** Fett gedruckt "DHL Postnummer: XXX" in E-Mails 14. ✅ **Formular-Views erweitert** - Postnummer-Eingabe überall möglich: - ✅ `portal/customer/_edit_form.blade.php` - Portal Kundenformular - Postnummer-Feld nach shipping_phone - Alert-Box mit JavaScript (togglePackstationAlert) - Identisch zu anderen Formularen - ✅ `user/order/shipping_me.blade.php` - Bestellung für mich selbst - Hidden field `shipping_postnumber` hinzugefügt (2 Stellen) - Für `same_as_billing` true/false Szenarien ## 🔍 **TIEFENPRÜFUNG DURCHGEFÜHRT - Weitere kritische Lücken gefunden und geschlossen!** 15. ✅ **KRITISCHE CONTROLLER/SERVICES KORRIGIERT** - Datenübertragung sichergestellt: - ✅ `app/Services/UserUtil.php` (Zeile 101) - ShoppingUser-Erstellung aus UserAccount - `shipping_postnumber` fehlte komplett! - ✅ `app/Services/AboOrderCart.php` (Zeilen 277 + 289) - Abo-Bestellungen: ShoppingUser aus UserAccount - `shipping_postnumber` fehlte an 2 Stellen (same_as_billing true/false) - ✅ `app/Services/PaymentHelper.php` (Zeile 115) - Payment ShoppingUser Update - `shipping_postnumber` fehlte komplett! 16. ✅ **WEITERE FEHLENDE VIEWS ERGÄNZT**: - ✅ `user/homeparty/_address.blade.php` (2 Stellen) - Homeparty Adressanzeige (billing + shipping) - Fett: "DHL Postnummer: XXX" - ✅ `user/order/payment/custom_payment.blade.php` - Custom Payment Bestelldetails - Badge mit Info-Text - ✅ `emails/custom_payment.blade.php` - Custom Payment E-Mail - Fett: "DHL Postnummer: XXX" **Übersetzungen:** - DE: `payment.dhl_postnumber` = "DHL Postnummer" - EN: `payment.dhl_postnumber` = "DHL Post Number" - ES: `payment.dhl_postnumber` = "Número de correo DHL" - **Neue Alert-Box Übersetzungen** in `resources/lang/{de,en,es}/payment.php`: - `packstation_alert_title` - `packstation_alert_intro` - `packstation_alert_street` - `packstation_alert_street_example` - `packstation_alert_location` - `packstation_alert_not_home` - `packstation_alert_footer` **DHL API Integration:** ```php if ($user->shipping_postnumber) { $recipient['postNumber'] = $user->shipping_postnumber; // shipping_address enthält "Packstation 145" (3-stellige Nummer!) } ``` **Wichtige Hinweise zur Packstation-Nummer:** - ⚠️ **Packstation-NUMMER ist 3-stellig** (100-999, steht auf gelbem Schild) - 📱 **DHL Postnummer ist 6-10-stellig** (separate Kundennummer in DHL App) - 🚫 **Häufiger Fehler:** Postnummer wird als Packstation-Nummer eingegeben - ✅ **Richtig:** "Packstation 145" (nicht "Packstation 12345") - 📄 **Anleitung:** `/dev/22-01-2026/packstation-anleitung.md` **Verbesserte Fehlermeldungen:** - Detaillierte Fehlermeldung bei ungültiger Packstation-Nummer - Frontend-Hinweise in allen Formularen aktualisiert (DE, EN, ES) - Logging mit allen relevanten Daten für besseres Debugging --- ### [X] 5. Set/Kit Produkte: Inhalte auflisten **Priorität:** Mittel **Bereich:** Produkte / Rechnungen / Lieferscheine **Problem:** Bei Sets/Kits werden enthaltene Einzelprodukte nicht aufgelistet. **Anforderung:** - Alle enthaltenen Produkte unter dem Set auflisten - Auf Rechnung und Lieferschein ausweisen - Admin-UI: Dropdown + Liste (wie bei Inhaltsstoffen) **Referenz-Implementierung (Inhaltsstoffe):** | Parameter | Wert | |-----------|------| | Pivot-Tabelle | `product_ingredients` | | Model | `App\Models\ProductIngredient` | | Relation | `Product::p_ingredients()` (belongsToMany) | **Neue Tabellen-Struktur (analog zu Inhaltsstoffen):** ```sql CREATE TABLE product_bundles ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, product_id BIGINT UNSIGNED NOT NULL, -- Das Set/Kit (Parent) bundle_product_id BIGINT UNSIGNED NOT NULL, -- Enthaltenes Produkt (Child) quantity INT UNSIGNED DEFAULT 1, pos INT UNSIGNED DEFAULT 0, -- Sortierung created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE, FOREIGN KEY (bundle_product_id) REFERENCES products(id) ON DELETE CASCADE, UNIQUE KEY unique_bundle (product_id, bundle_product_id) ); ``` **Neue Model-Relation in `Product.php`:** ```php public function bundleItems() { return $this->belongsToMany(Product::class, 'product_bundles', 'product_id', 'bundle_product_id') ->withPivot('quantity', 'pos') ->orderBy('pos'); } public function isBundle(): bool { return $this->bundleItems()->count() > 0; } ``` **Admin-UI (wie Inhaltsstoffe):** - Dropdown zur Produktauswahl - Listenansicht mit Menge und Sortierung - Vorlage: `resources/views/admin/product/` → Ingredients-Sektion kopieren **Betroffene Views:** - `resources/views/pdf/invoice.blade.php` → Bundle-Items unter Produkt auflisten - `resources/views/pdf/delivery.blade.php` → Bundle-Items auflisten - Shop-Produktdetailseiten --- ### [!] 6. Mehrsprachigkeit: Rechnungen, Provisionen, Lieferscheine **Priorität:** Hoch **Bereich:** PDF-Generierung / E-Mail **Anforderung:** - Deutsche Version bleibt primär (rechtlich bindend) - Zusätzliche Kopie in Landessprache (EN, ES, FR) - Sprache aus User-Einstellung (`users.locale`) **Technische Details:** | Parameter | Wert | |-----------|------| | PDF-Service | `App\Services\Invoice` | | Templates | `resources/views/pdf/*.blade.php` | | Mail | `App\Mail\MailInvoice` | | User-Sprache | `users.locale` (Spalte prüfen/anlegen) | **Betroffene Dokumente:** | Dokument | Datei | Status | |----------|-------|--------| | Rechnung | `invoice.blade.php` | [ ] | | Lieferschein | `delivery.blade.php` | [ ] | | Provisionsabrechnung | `credit_details.blade.php` | [ ] | | Stornorechnung | (neu) | [ ] | | Mitgliedschaftsverlängerung | E-Mail Template | [ ] | | Partnerantrag/Vertrag | PDF Template | [ ] | **Umsetzung:** ```php // Invoice Service erweitern public function generatePdf(Order $order, string $locale = 'de'): string { app()->setLocale($locale); // PDF generieren... } // Zwei PDFs generieren $pdfDE = $this->generatePdf($order, 'de'); $pdfUser = $this->generatePdf($order, $user->locale ?? 'de'); // Bei E-Mail beide anhängen (wenn unterschiedlich) if ($user->locale && $user->locale !== 'de') { $mail->attach($pdfDE, ['as' => 'Rechnung-DE.pdf']); $mail->attach($pdfUser, ['as' => 'Invoice-' . strtoupper($user->locale) . '.pdf']); } ``` **Speicherung:** - Beide PDFs im System speichern (Bestellungen-Ansicht) - Zusätzliche Spalten in `user_invoices`: `file_localized`, `locale` --- ### [!] 7. Stornorechnungen mit Punktekorrektur **Priorität:** Hoch **Bereich:** Admin / Rechnungswesen / Marketingplan **Problem:** - Storno-Button fehlt im Admin - Punkte werden bei Storno NICHT abgezogen (in gesamter MLM-Struktur) **Anforderung:** - Button "Stornorechnung erstellen" neben Rechnung - Negativbetrag im Rechnungskreis - Punkte in gesamter MLM-Struktur korrigieren (Upline!) - Mehrsprachigkeit beachten **Technische Details:** | Parameter | Wert | |-----------|------| | Model | `App\Models\UserInvoice` | | Storno-Felder | `cancellation`, `cancellation_id`, `cancellation_date` | | Punkte-Model | `App\Models\UserSalesVolume` | | Business-Model | `App\Models\UserBusiness` | | Admin-View | `resources/views/admin/order/*.blade.php` | **Punktekorrektur-Logik:** ```php // 1. Original-SalesVolume finden $salesVolume = UserSalesVolume::where('order_id', $order->id)->first(); // 2. Punkte negieren (neuer Eintrag mit negativen Werten) UserSalesVolume::create([ 'user_id' => $salesVolume->user_id, 'order_id' => $order->id, 'month' => $salesVolume->month, 'year' => $salesVolume->year, 'month_points' => -$salesVolume->month_points, 'month_KP_points' => -$salesVolume->month_KP_points, 'status' => 6, // Neuer Status: 'cancelled' ]); // 3. Upline-Struktur durchlaufen und korrigieren // → TreeCalcBotOptimized neu berechnen oder separater CancellationService ``` **Betroffene Tabellen:** ``` user_sales_volumes → Negativer Eintrag hinzufügen user_business → Monatsdaten neu berechnen (oder Neuberechnung triggern) ``` **Admin-Route:** ```php Route::post('/admin/invoice/{id}/cancel', [InvoiceController::class, 'cancel']); ``` --- ### [ ] 8. Französisch hinzufügen **Priorität:** Mittel **Bereich:** Lokalisierung **Anforderung:** - Neue Sprachdateien: `resources/lang/fr/` - Monatsstatistik übersetzen - Vorkasse-Texte übersetzen **Technische Details:** | Parameter | Wert | |-----------|------| | Verzeichnis | `resources/lang/fr/` | | Vorlage | `resources/lang/de/` kopieren | **Dateien erstellen:** ``` resources/lang/fr/ ├── abo.php ├── backend.php ├── cal.php ├── customer.php ├── email.php ├── home.php ├── marketingplan.php ├── navigation.php ├── payment.php └── team.php ``` **Umsetzung:** ```bash cp -r resources/lang/de resources/lang/fr # Dann alle Dateien übersetzen ``` --- ### [!] 9. Gutschriften: Falsche Punkteberechnung **Priorität:** Hoch **Bereich:** Marketingplan / Team-Ansicht **Problem:** Gutschriften werden nicht korrekt zu Punkten addiert. Unterschiedliche Anzeige für Admin vs. User. **Beispiel:** - Monika Kunz: Admin sieht 625 Punkte, User sieht 1115 Punkte (Dezember) - Differenz: 490 Punkte → vermutlich Gutschrift nicht berücksichtigt **Technische Details:** | Parameter | Wert | |-----------|------| | Service | `App\Services\BusinessPlan\TreeCalcBotOptimized` | | Status | `UserSalesVolume.status = 4` (credit/Gutschrift) | | Model | `App\Models\UserSalesVolume` | **Status-Mapping:** ```php 0 => 'not_assigned' 1 => 'advisor_order' 2 => 'shoporder' 3 => 'shoporder_pending' 4 => 'credit' // ← Gutschrift 5 => 'registration' ``` **Debugging-Schritte:** 1. Query für User mit `status = 4` im betroffenen Monat prüfen: ```php UserSalesVolume::where('user_id', $userId) ->where('month', 12)->where('year', 2025) ->where('status', 4)->get(); ``` 2. Berechnung in `getPointsKPSum()` / `getPointsTPSum()` validieren 3. Team-View Query vs. Admin-View Query vergleichen 4. Prüfen ob `status_points` korrekt gesetzt ist **Vermutete Ursache:** - Admin-Query filtert `status = 4` aus - User-Query inkludiert alle Status --- ### [!] 10. Nicht zugeordnete Zahlungen/Punkte **Priorität:** Hoch **Bereich:** Payment / Admin **Problem:** Zahlungen ohne Zuordnung → Punkte verschwinden, keine Provision. **Anforderung:** - Admin-Hinweis bei nicht zugeordneten Zahlungen - Manuelle Zuordnungsmöglichkeit **Technische Details:** | Parameter | Wert | |-----------|------| | Tabelle | `user_sales_volumes` | | Status-Feld | `status = 0` (not_assigned) | | Admin-View | Dashboard oder separate Sektion | **Query für nicht zugeordnete Einträge:** ```php $unassigned = UserSalesVolume::where('status', 0) ->with('user', 'order') ->orderBy('created_at', 'desc') ->get(); ``` **Umsetzung:** 1. **Dashboard-Alert:** Anzahl nicht zugeordneter Einträge anzeigen 2. **Admin-Seite:** Liste aller nicht zugeordneten Einträge 3. **Zuordnungs-Modal:** - User auswählen (Dropdown/Suche) - Status aktualisieren (1 = advisor_order, 2 = shoporder) - Punkte werden bei nächster Berechnung berücksichtigt --- ### [ ] 11. Monatsstatistik Erweiterungen **Priorität:** Mittel **Bereich:** Dashboard / Team **Probleme:** - Teamumsatz wird seit Januar nicht angezeigt - Neupartner/Abos nicht klickbar (keine Detailansicht) **Anforderungen:** | Feature | Beschreibung | |---------|--------------| | Teamumsatz | Bug fixen - wird nicht angezeigt | | Neupartner Details | Klick → Liste mit Name, E-Mail, Telefon, Generation, Mentor | | Team-Abos Details | Klick → Liste mit Abo-Details | | 1000-Punkte-Shops | Neue Kennzahl: Teampartner mit ≥1000 Punkte persönlichem Volumen | | Aktuelle Provision | In Monatsstatistik anzeigen | | Downline-Kontakte | Telefon, E-Mail, Adresse der eigenen Downline abrufbar (nicht nur VIPs) | **Technische Details:** | Parameter | Wert | |-----------|------| | Service | `App\Services\LevelReportService` | | Controller | `App\Http\Controllers\User\TeamController` | | View | `resources/views/user/team/marketingplan.blade.php` | | Daten | `user_business` Tabelle | **1000-Punkte Query:** ```php $count1000 = UserBusiness::where('month', $month) ->where('year', $year) ->where('sales_volume_points_sum', '>=', 1000) ->whereIn('user_id', $teamUserIds) ->count(); ``` **Klickbare Details (AJAX Modal):** ```php // Route Route::get('/team/new-partners/{month}/{year}', [TeamController::class, 'newPartnersDetail']); // Response return response()->json([ 'partners' => $partners->map(fn($p) => [ 'name' => $p->full_name, 'email' => $p->email, 'phone' => $p->phone, 'generation' => $p->generation, 'mentor' => $p->mentor->full_name ?? '-' ]) ]); ``` --- ### [ ] 12. Bezahllink Status-Unterscheidung **Priorität:** Mittel **Bereich:** Payment / Admin **Problem:** Unklar ob Payment-Link nur geklickt oder Zahlung wirklich durchgeführt. **Anforderung:** | Status | Bedeutung | Farbe | |--------|-----------|-------| | `link_sent` | Link wurde versendet | grau | | `link_clicked` | Link wurde geklickt, keine Zahlung | orange | | `payment_pending` | Zahlung in Bearbeitung | gelb | | `paid` | Zahlung erfolgreich | grün | | `failed` | Zahlung fehlgeschlagen | rot | **Technische Details:** | Parameter | Wert | |-----------|------| | Tabelle | `shopping_payments` | | Feld | `txaction` (VARCHAR 20) | | Service | `App\Services\Payment` | **Aktuelle Status (ungenau):** ```php 'paid' => 'paid' 'appointed' => 'open' // ← Zu ungenau 'failed' => 'failed' 'extern' => 'open' // ← Zu ungenau ``` **Umsetzung:** 1. Neues Feld oder erweiterte `txaction`-Werte 2. Bei Payment-Link-Aufruf: Status auf `link_clicked` setzen 3. Bei Payone-Callback: Status entsprechend aktualisieren 4. Admin-View: Farbkodierung nach neuem Schema --- ### [?] 13. Steuerberater-Modul **Priorität:** Niedrig **Status:** Noch zu definieren **Notiz:** Weitere Infos liegen vor - müssen noch spezifiziert werden. **TODO:** Anforderungen dokumentieren --- ### [X] 14. DHL Modul Erweiterungen **Status:** ✅ ERLEDIGT **Priorität:** Hoch **Bereich:** Versand / packages/acme-laravel-dhl **Implementierte Funktionen:** | Feature | Status | Beschreibung | |---------|--------|--------------| | Storno-Etiketten UI | ✅ | Admin-Button für Label-Stornierung | | Tracking-Abfrage | ✅ | Status automatisch abrufen | | Tracking-Mail | ✅ | Kunde über Versand informieren | **Technische Details:** | Parameter | Wert | |-----------|------| | Package | `packages/acme-laravel-dhl` | | Model | `DhlShipment` | | Tabelle | `dhl_package_shipments` | | Status-Feld | `status` (created/in_transit/delivered/canceled) | | Tracking-Tabelle | `dhl_tracking_events` | **Vorhandene Jobs (bereits implementiert):** - `CreateShipmentJob` ✓ - `CancelShipmentJob` ✓ (existiert, nutzt `canCancel()`) - `CreateReturnLabelJob` ✓ - `SyncTrackingJob` ✓ (Webhook-basiert) **Durchgeführte Implementierung:** 1. ✅ **Admin-UI für Storno:** - Button "Label stornieren" in Bestellansicht (`_detail_dhl_shipments.blade.php`) - Button im DHL Cockpit DataTable aktiviert (`DhlShipmentController.php`) - JavaScript Handler für Storno-Button in beiden Views - Dispatcht `CancelShipmentJob` - Nur wenn `$shipment->canCancel()` = true - Bestätigungsdialog mit Warnung vor ungültigem Label 2. ✅ **Tracking-Mail an Kunde:** - Mail-Klasse: `App\Mail\MailDhlTracking` (bereits vorhanden) - E-Mail Template: `resources/views/emails/dhl_tracking.blade.php` - Trigger: Nach Status-Update auf `in_transit` (automatisch via Cron) - Manueller Versand: Button in Admin-UI - Inhalt: Sendungsnummer + Tracking-Link + Bestellnummer - Übersetzungen: DE, EN, ES bereits vorhanden - Tracking Status wird in Datenbank gespeichert (tracking_email_sent_at, tracking_email_type) 3. ✅ **Cron für Tracking (Alternative zu Webhook):** - Command: `app/Console/Commands/DhlUpdateTracking.php` - Signature: `php artisan dhl:update-tracking` - Optionen: - `--days=14`: Sendungen der letzten X Tage aktualisieren - `--send-emails`: Automatisch E-Mails bei Transit-Status senden - `--dry-run`: Nur simulieren, keine Änderungen - Cron-Job eingetragen in `app/Console/Kernel.php`: - Täglich um 06:00 Uhr - Mit automatischem E-Mail-Versand - `withoutOverlapping()` und `runInBackground()` - Statistik-Ausgabe: updated, failed, emails_sent, skipped 4. ✅ **Retourenlabel-Button:** - Button "Retourenlabel erstellen" im DHL Cockpit aktiviert - JavaScript Handler hinzugefügt - Dispatcht `CreateReturnLabelJob` - Nur für ausgehende Sendungen ohne vorhandene Retoure **Routen (bereits vorhanden):** ```php Route::delete('/admin/dhl/shipment/{shipment}/cancel', ...) # Storno Route::post('/admin/dhl/shipment/{shipment}/return-label', ...) # Retourenlabel Route::post('/admin/dhl/shipment/{shipment}/update-tracking', ...) # Tracking Update Route::post('/admin/dhl/shipment/{shipment}/send-tracking-email', ...) # E-Mail senden ``` **DHL API Endpunkte:** ``` DELETE /parcel/de/shipping/v2/orders/{shipmentNumber} # Storno GET /parcel/de/tracking/v1/shipments/{shipmentNumber} # Tracking ``` **Betroffene Dateien:** 1. `resources/views/admin/sales/_detail_dhl_shipments.blade.php` - Storno-Button hinzugefügt 2. `resources/views/admin/dhl/cockpit.blade.php` - JavaScript Handler erweitert 3. `app/Http/Controllers/DhlShipmentController.php` - Nutzt jetzt `DhlShipmentService` 4. `app/Services/DhlShipmentService.php` - **Erweitert um `cancelShipment()` Methode** 5. `app/Jobs/CancelShipmentJob.php` - **Aktualisiert für neues Package-Model** 6. `app/Console/Commands/DhlUpdateTracking.php` - Tracking Command (bereits vorhanden) 7. `app/Console/Kernel.php` - Cron-Job (bereits eingetragen) 8. `app/Mail/MailDhlTracking.php` - E-Mail Klasse (bereits vorhanden) 9. `resources/views/emails/dhl_tracking.blade.php` - E-Mail Template (bereits vorhanden) 10. `resources/lang/{de,en,es}/email.php` - Übersetzungen (bereits vorhanden) **Fix für Model-Typ-Konflikt & Queue-Config:** - `CancelShipmentJob` wurde von `App\Models\DhlShipment` auf `Acme\Dhl\Models\DhlShipment` migriert - Nutzt jetzt `Acme\Dhl\Services\ShippingService::cancelLabel()` aus dem neuen Package - Verwendet `dhl_shipment_no` statt `shipment_number` (korrektes Feld-Mapping) - **`DhlShipmentService::cancelShipment()` hinzugefügt:** - Prüft `DHL_USE_QUEUE` Config-Einstellung - Verwendet Queue (`CancelShipmentJob`) wenn aktiviert - Führt synchron aus (`ShippingService::cancelLabel()`) wenn deaktiviert - Konsistentes Verhalten wie bei `createShipment()` - **Controller nutzt jetzt Service statt direkt Job zu dispatchen:** - `DhlShipmentController::cancel()` ruft `DhlShipmentService::cancelShipment()` auf - Automatische Entscheidung zwischen Queue/Sync basierend auf Config **Verwendung:** **Manuell:** ```bash # Tracking aktualisieren (Simulation) php artisan dhl:update-tracking --dry-run # Tracking aktualisieren mit E-Mail-Versand php artisan dhl:update-tracking --days=7 --send-emails # Nur letzte 3 Tage aktualisieren php artisan dhl:update-tracking --days=3 # NEU: Test-E-Mail an eigene Adresse php artisan dhl:update-tracking --send-emails --test-email=admin@firma.de # NEU: Nur für bestimmte Bestellung php artisan dhl:update-tracking --send-emails --order=45078 ``` **Automatisch via Cron:** - Läuft täglich um 06:00 Uhr - Aktualisiert Sendungen der letzten 14 Tage - Sendet automatisch E-Mails bei Status-Änderung zu "in_transit" - Verhindert Überlappungen mit `withoutOverlapping()` **NEU: Mehrere Sendungen in einer E-Mail:** - Wenn eine Bestellung mehrere Labels hat, werden alle in einer E-Mail zusammengefasst - Automatisch beim manuellen Versand über Admin-Button - Automatisch beim Cronjob-Versand - Zeigt "Paket 1, Paket 2, Paket 3" mit jeweiliger Tracking-Nummer - Markiert alle Sendungen als versendet **NEU: Versand-Status in Bestelldetails:** - Zeigt wann E-Mail versendet wurde - Zeigt ob automatisch (Cronjob) oder manuell (Admin) - Icons: 🤖 Automatisch / 👤 Manuell **NEU: E-Mail-Feld für bestehende Sendungen nachfüllen:** ```bash # Dry-Run (nur simulieren) php artisan dhl:backfill-emails --dry-run # Tatsächlich ausführen php artisan dhl:backfill-emails ``` **NEU: E-Mail + Postnummer bei Label-Erstellung:** - ✅ Migration hinzugefügt: `2026_01_23_140000_add_email_and_postnumber_to_dhl_shipments.php` - ✅ Neue Felder in `dhl_package_shipments`: `email`, `postnumber` - ✅ Model `DhlShipment` erweitert um beide Felder - ✅ Formular-Feld für E-Mail hinzugefügt in `modal_in_order_shipment.blade.php` - ✅ E-Mail-Feld ist Pflichtfeld mit Validierung (type="email", required) - ✅ Vorbefüllung mit Billing-E-Mail aus `order->shopping_user->email` - ✅ Postnummer-Feld bereits vorhanden (optional für Packstation) - ✅ Controller-Validierung erweitert: `shipping_email` (required), `shipping_postnumber` (nullable) - ✅ `DhlDataHelper` übergibt E-Mail + Postnummer an ShippingService - ✅ `ShippingService::createShipmentRecord()` speichert beide Felder in DB - ✅ Daten werden sowohl direkt als auch im JSON `recipient` gespeichert **Zweck der Felder:** - `email`: Wird für DHL Benachrichtigungen und Tracking-E-Mails verwendet - `postnumber`: DHL Postnummer (6-10 Stellen) für Packstation/Paketbox-Lieferungen **E-Mail-Button-Logik:** - ✅ Button wird angezeigt, wenn Sendung eine `dhl_shipment_no` hat UND eine E-Mail verfügbar ist - ✅ Priorisierung: Shipment-Email > Shopping-User-Email - ✅ `canSendTrackingEmail()` prüft zuerst das neue `email` Feld - ✅ Fallback auf `shopping_user->email` wenn Shipment-Email leer - ✅ Button funktioniert in beiden Views: Bestelldetails + DHL Cockpit **E-Mail-Versand-Priorisierung:** 1. **Test-E-Mail** (falls angegeben im Cronjob mit `--test-email`) 2. **Shipment-Email** (aus `dhl_package_shipments.email`) 3. **Shopping-User-Email** (Fallback aus `shopping_users.email`) **Betroffene Dateien (E-Mail + Postnummer):** 1. `database/migrations/2026_01_23_140000_add_email_and_postnumber_to_dhl_shipments.php` - NEU 2. `packages/acme-laravel-dhl/src/Models/DhlShipment.php` - fillable + canSendTrackingEmail() erweitert 3. `resources/views/admin/dhl/modal_in_order_shipment.blade.php` - E-Mail-Feld hinzugefügt 4. `app/Http/Controllers/DhlShipmentController.php` - Validierung + E-Mail-Priorisierung 5. `packages/acme-laravel-dhl/src/Services/ShippingService.php` - Speicherung erweitert 6. `app/Console/Commands/DhlUpdateTracking.php` - E-Mail-Priorisierung im Cronjob 7. `app/Services/DhlDataHelper.php` - Übergibt E-Mail + Postnummer (bereits vorhanden) 8. `app/Services/DhlModalService.php` - Liest Formularfelder (bereits vorhanden) 9. `resources/views/admin/dhl/show.blade.php` - E-Mail-Button + JavaScript Handler 10. `resources/views/admin/sales/_detail_dhl_shipments.blade.php` - E-Mail-Button + Handler (bereits vorhanden) 11. `resources/views/admin/dhl/modal_in_shipment_info.blade.php` - E-Mail-Button im Modal 12. `app/Console/Commands/DhlBackfillEmails.php` - Command zum Nachfüllen (NEU) **E-Mail-Button Standorte:** ✅ Bestelldetails (_detail_dhl_shipments.blade.php) ✅ DHL Cockpit (cockpit.blade.php) ✅ DHL Detail-Seite (show.blade.php) ✅ Modal nach Label-Erstellung (modal_in_shipment_info.blade.php) **NEU: Return Label (Retourenlabel) Funktionalität:** ✅ Button in allen DHL Views hinzugefügt ✅ Controller mit Sync/Async Unterstützung (DHL_USE_QUEUE) ✅ Job aktualisiert für neues DHL Package ✅ Automatisches Adress-Tausch (Kunde → Absender, Lager → Empfänger) ✅ Prüfung ob bereits Retoure existiert ✅ Nur für ausgehende Sendungen verfügbar ✅ **API-Fix (23.01.2026):** ReturnsService statt ShippingService verwenden ✅ Korrekter DHL Returns API Endpunkt: `/parcel/de/returns/v1/labels` ✅ Korrekte Payload-Struktur für Returns API ✅ Verbesserte Validierung und Fehlerbehandlung ✅ Erweitertes Logging für Debugging ✅ **Country-Code Fix:** Automatische Konvertierung 2-stellig → 3-stellig (DE → DEU) ✅ **Fallback-Implementierung:** Automatischer Fallback zu regulärer Shipping-API (V07PAK) bei fehlenden Returns-API Berechtigungen ✅ Intelligente Fehlerbehandlung für Auth-Fehler (401/403) ✅ Transparentes Logging welche Methode verwendet wird ✅ **Fallback-Fixes (23.01.2026):** Country-Code Konvertierung (3→2 Buchstaben), Dimensions hinzugefügt, print_format gesetzt ✅ Automatische Adress-Konvertierung für ShippingService-Kompatibilität ✅ **V01PAK statt V07PAK:** V07PAK nicht verfügbar, verwende V01PAK (Standard DHL Paket) mit vertauschten Adressen ✅ **Return-Label Fixes (23.01.2026 - 17:30):** Type-Update korrigiert ($result['shipment'] statt $result['shipmentId']) ✅ Doppelklick-Schutz für Return-Button implementiert ✅ Existierende Return-Labels (ID 18, 19) manuell korrigiert zu type='return' **Return Label Button Standorte:** ✅ Bestelldetails (_detail_dhl_shipments.blade.php) - NEU ✅ DHL Cockpit (cockpit.blade.php) - funktioniert ✅ DHL Detail-Seite (show.blade.php) - aktiviert ✅ Nur sichtbar wenn: type='outbound' UND keine Retoure existiert **NEU: Return Label Visuelle Hervorhebung (23.01.2026):** ✅ Return-Etiketten deutlich erkennbar mit oranger Farbgebung ✅ Orange "RETOURE" Badge (statt blau) in allen Listen ✅ Orange ID-Links mit Undo-Icon in allen Tabellen ✅ Zeilen-Highlighting in DataTable (orangener Hintergrund + linker Border) ✅ Zeilen-Highlighting in Order-Details (orangener Hintergrund) ✅ Größeres, fetteres Badge in Detail-Ansicht (show.blade.php) ✅ CSS-Klasse `return-shipment` für DataTable-Zeilen ✅ Konsistente orange Farbgebung (`badge-warning`, `text-warning`, `#ffc107`) ✅ Return-Etiketten bekommen KEINEN "Retourenlabel erstellen" Button **Betroffene Dateien (Styling):** 1. `app/Http/Controllers/DhlShipmentController.php` - DataTable Spalten (ID, Typ) 2. `resources/views/admin/dhl/cockpit.blade.php` - CSS + JS für Zeilen-Highlighting 3. `resources/views/admin/dhl/show.blade.php` - Header Badge + Icon 4. `resources/views/admin/sales/_detail_dhl_shipments.blade.php` - Zeilen-Style + Badge **Dokumentation:** `dev/23-01-2026/dhl-return-label-styling.md` --- ## ZUSAMMENFASSUNG | # | Aufgabe | Priorität | Komplexität | Bereich | |---|---------|-----------|-------------|---------| | 1 | News Links + Datei-Auswahl | Hoch | Niedrig | Frontend | | 2 | Points DECIMAL | Hoch | Hoch | DB/Backend | | 3 | Vorkasse TXID Hinweis | Hoch | Niedrig | Frontend | | 4 | Packstation/Postnummer | Mittel | Mittel | DB/Frontend | | 5 | Set-Produkte (wie Inhaltsstoffe) | Mittel | Hoch | DB/Backend | | 6 | Mehrsprachigkeit PDFs | Hoch | Mittel | Backend | | 7 | Stornorechnungen + Punktekorrektur | Hoch | Hoch | Backend | | 8 | Französisch | Mittel | Niedrig | i18n | | 9 | Gutschriften Punkte Bug | Hoch | Mittel | Backend | | 10 | Nicht zugeordnete Zahlungen | Hoch | Mittel | Backend | | 11 | Monatsstatistik Erweiterungen | Mittel | Mittel | Backend | | 12 | Bezahllink Status | Mittel | Niedrig | Backend | | 13 | Steuerberater | Niedrig | ? | TBD | | 14 | DHL UI + Tracking-Mail | Hoch | Mittel | Package | --- ## EMPFOHLENE REIHENFOLGE ### Phase 1: Quick Wins (Frontend, niedrige Komplexität) - [ ] #1 News Links - [ ] #3 Vorkasse TXID Hinweis - [ ] #12 Bezahllink Status ### Phase 2: Kritische Bugs (Provisionen betroffen) - [ ] #9 Gutschriften Punkte Bug - [ ] #10 Nicht zugeordnete Zahlungen ### Phase 3: Infrastruktur (DB-Änderungen) - [ ] #2 Points DECIMAL (benötigt Migration + Testing) - [ ] #7 Stornorechnungen mit Punktekorrektur ### Phase 4: Features - [ ] #6 Mehrsprachigkeit PDFs - [ ] #14 DHL UI + Tracking-Mail - [ ] #11 Monatsstatistik Erweiterungen ### Phase 5: Langfristig - [ ] #4 Packstation/Postnummer - [ ] #5 Set-Produkte Bundles - [ ] #8 Französisch - [ ] #13 Steuerberater-Modul