20-02-2026
This commit is contained in:
parent
854ce02bf6
commit
4d6b4930b2
128 changed files with 18247 additions and 2093 deletions
983
dev/12-01-2026/entwicklungsplan.md
Normal file
983
dev/12-01-2026/entwicklungsplan.md
Normal file
|
|
@ -0,0 +1,983 @@
|
|||
# Entwicklungsplan: B2In / Local for Local Marktplatz-Ökosystem
|
||||
|
||||
**Erstellt:** 12.02.2026
|
||||
**Letzte Aktualisierung:** 12.02.2026
|
||||
**Basis:** konzeption.md (Version 1.1)
|
||||
**Status:** Phase 1 ✅, Phase 2 ✅, Phase 2.5 Produkt-Bearbeitung ✅, Phase 2.6 Refactoring & UX ✅, Phase 2.7 Admin-Produktverwaltung & Freigabe ✅, Phase 3 Kern ✅
|
||||
**Docker** Projekt läuft in Docker, root /var/www/html nutze php artisan ... nicht vendor/bin/sail artisan ...
|
||||
---
|
||||
|
||||
## 0. Umsetzungsprotokoll
|
||||
|
||||
### Phase 2.7: Admin-Produktverwaltung, Freigabe-Workflow & Statusaktionen – ABGESCHLOSSEN (13.02.2026)
|
||||
|
||||
Vollstaendige Admin-Produktverwaltung mit tabellarischer Uebersicht, Filtern, Freigabe-Workflow mit Ablehnungsgrund, sowie Archivieren/Verkauft-Aktionen fuer Haendler und Admin.
|
||||
|
||||
**Produkt archivieren / als Verkauft markieren**
|
||||
- Neue `archiveProduct()` und `markAsSold()` Methoden in `form-standard.blade.php`, `form-teaser.blade.php` und `products/index.blade.php`
|
||||
- Edit-Formulare: Buttons "Als verkauft markieren" und "Archivieren" links neben Speichern-Button (nur im Edit-Modus)
|
||||
- Produktliste: Dropdown-Menue (3-Punkte-Menue) je Produkt mit "Als verkauft" und "Archivieren" Optionen
|
||||
- `wire:confirm` Dialoge fuer Sicherheitsabfrage
|
||||
- Activity-Log-Eintraege (action: `archived` / `sold`) werden automatisch erstellt
|
||||
- Produkte im Status Archived/Sold zeigen kein Dropdown mehr
|
||||
|
||||
**Erstellungsdatum in Produktliste**
|
||||
- Neue Spalte "Erstellt" in `products/index.blade.php` (Format: dd.mm.YYYY)
|
||||
- Sowohl fuer Haendler als auch Admin sichtbar
|
||||
|
||||
**Admin-Produktuebersicht (komplett neu)**
|
||||
- `admin/products/index.blade.php` komplett umgebaut: von Card-/Tab-Layout zu tabellarischer Uebersicht
|
||||
- Statistik-Karten: Gesamt, Zur Freigabe, In Korrektur, Freigegeben (klickbar als Schnellfilter)
|
||||
- Filter: Suche (Name, Artikelnummer), Status (alle ProductStatus-Werte), Produkttyp, Haendler, Kategorie
|
||||
- Tabelle: Produkt (mit Bild), Haendler, Kategorie, Status, Kuration (Freigabe-Buttons), Erstellt, Aktionen
|
||||
- Admin kann alle Produkte bearbeiten – Edit-Link fuehrt zum gleichen Formular wie fuer Haendler
|
||||
- Suche durchsucht auch `b2in_article_number` und `partner_product_number`
|
||||
|
||||
**Freigabe-Workflow mit Ablehnungsgrund**
|
||||
- **Freigeben:** Direkt-Button in Kuration-Spalte (Pending → Active + is_curated)
|
||||
- **Korrektur:** Inline-Formular (orange) mit Pflicht-Textfeld → Status `Correction`, `curation_notes` gespeichert
|
||||
- **Ablehnung (NEU):** Inline-Formular (rot) mit Pflicht-Textfeld → Status `Archived`, Ablehnungsgrund in `curation_notes` gespeichert
|
||||
- Activity-Log-Eintrag bei allen drei Aktionen (mit `note` bei Korrektur/Ablehnung)
|
||||
- `Flux::toast()` Benachrichtigungen statt Flash-Messages
|
||||
|
||||
**Kuration-Hinweis beim Haendler**
|
||||
- Standard- und Teaser-Edit-Formulare zeigen prominenten Callout wenn `curation_notes` vorhanden:
|
||||
- Korrektur (Status `correction`): Gelbes Warning-Callout "Korrektur erforderlich"
|
||||
- Ablehnung (Status `archived`): Rotes Danger-Callout "Produkt abgelehnt"
|
||||
- Angezeigt oberhalb des Formulars, immer sichtbar
|
||||
|
||||
**Admin Archiv/Verkauft in Admin-Uebersicht**
|
||||
- Dropdown-Menue analog zur Haendler-Produktliste
|
||||
- Admin kann alle Produkte archivieren oder als verkauft markieren
|
||||
|
||||
**Erstellte/geaenderte Dateien (5):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Volt** (3) | `form-standard.blade.php`, `form-teaser.blade.php`, `products/index.blade.php` | Archive/Sold-Methoden, Kuration-Callout, Erstellungsdatum, Dropdown-Aktionen |
|
||||
| **Admin-Volt** (1) | `admin/products/index.blade.php` | Komplett umgebaut: Tabelle, Filter, Statistiken, Rejection mit Textfeld |
|
||||
| **Tests** (1) | `ProductCurationTest.php`, `ProductEditTest.php` | Aktualisiert + erweitert |
|
||||
|
||||
**Neue/aktualisierte Tests (25):**
|
||||
- ProductCurationTest: 23 Tests (komplett ueberarbeitet fuer neues Tabellen-Layout, Rejection mit Pflicht-Textfeld, Archive/Sold, Filter, Kuration-Notes-Anzeige)
|
||||
- ProductEditTest: +2 Tests (archiveProduct, markAsSold aus Edit)
|
||||
|
||||
**Tests gesamt: 194 Produkt-Tests, alle bestanden ✅ (533 Assertions)**
|
||||
|
||||
---
|
||||
|
||||
### Phase 2.6: Refactoring, UX-Verbesserungen & Teaser-Erweiterung – ABGESCHLOSSEN (13.02.2026)
|
||||
|
||||
Umfassende Qualitaetsverbesserung der Produkt-Formulare: Code-Redundanzen eliminiert, UX-Workflow optimiert, Bildsortierung implementiert, Teaser-Produkte mit Produktnummern und korrektem Freigabe-Workflow ausgestattet.
|
||||
|
||||
**Refactoring: Create + Edit zu einem Formular zusammengefuehrt**
|
||||
- `create.blade.php` + `edit.blade.php` → `form-standard.blade.php` (Standard-Produkte)
|
||||
- `create-teaser.blade.php` + `edit-teaser.blade.php` → `form-teaser.blade.php` (Teaser-Produkte)
|
||||
- Steuerung ueber `$isEditing`-Flag in `mount()`: `?Product $product = null`
|
||||
- Separate `saveNew()` und `saveExisting()` Methoden
|
||||
- Alte Einzeldateien geloescht
|
||||
|
||||
**UX: Save-Verhalten bei Bearbeitung**
|
||||
- Bei Edit: Seite bleibt offen statt Redirect zur Produktliste
|
||||
- Flux Toast-Notification ("Produkt wurde gespeichert" / "zur Freigabe eingereicht")
|
||||
- Bei Standard-Produkten: aktiver Tab bleibt erhalten (kein Page-Reload)
|
||||
- `<flux:toast />` global im Sidebar-Layout ergaenzt
|
||||
- Bei Create: weiterhin Redirect zur Produktliste mit Flash-Message
|
||||
|
||||
**Bildsortierung per Drag & Drop**
|
||||
- Vorhandene Bilder per HTML5 Drag & Drop umsortierbar (Alpine.js, keine externe Dependency)
|
||||
- Erstes Bild = Standardbild, visuell markiert (blaues "Standard"-Badge + blauer Ring)
|
||||
- `updateMediaOrder(array $orderedIds)` Methode speichert Reihenfolge in `order_column`
|
||||
- `existingMedia` wird immer nach `order_column` sortiert geladen
|
||||
- Drag-Feedback: halbtransparent, blauer Ring am Ziel, Grab-Cursor, Sortier-Icon bei Hover
|
||||
|
||||
**Vorschaubild in Produktliste**
|
||||
- `products/index.blade.php`: Erstes Bild (nach `order_column`) als 40x40px Thumbnail vor dem Produktnamen
|
||||
- Platzhalter-Icon (Foto-Symbol) wenn kein Bild vorhanden
|
||||
|
||||
**Teaser-Produkte: Produktnummern & Status-Fix**
|
||||
- `partnerProductNumber` hinzugefuegt: wird bei Create automatisch generiert (z.B. P003-0001), bei Edit pre-filled
|
||||
- `b2inArticleNumber` wird beim Speichern automatisch erzeugt (z.B. B2IN-000001)
|
||||
- Eigene "Produktnummern"-Karte im Formular (B2in-Badge + Partner-Nummer-Feld)
|
||||
- **Bug-Fix:** Status "aktiv" im UI setzte Status direkt auf Active statt Pending. Jetzt korrekt: UI "aktiv" → DB `pending` (zur Freigabe)
|
||||
- UI-Text vereinheitlicht: immer "Zur Freigabe einreichen" (nicht mehr "Aktiv – direkt veröffentlichen")
|
||||
- Freigabe-Workflow-Hinweis erscheint bei Create und Edit
|
||||
|
||||
**Routing-Aenderung:**
|
||||
```
|
||||
products/create/standard → products.form-standard (products.create.standard)
|
||||
products/create/teaser → products.form-teaser (products.create.teaser)
|
||||
products/{product}/edit-standard → products.form-standard (products.edit.standard)
|
||||
products/{product}/edit-teaser → products.form-teaser (products.edit.teaser)
|
||||
```
|
||||
|
||||
**Erstellte/geaenderte Dateien (8):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Volt** (2) | `form-standard.blade.php`, `form-teaser.blade.php` | Merged Create+Edit, Toast, Bildsortierung, Produktnummern |
|
||||
| **Views** (2) | `index.blade.php`, `sidebar.blade.php` | Vorschaubild, `<flux:toast />` |
|
||||
| **Routes** (1) | `routes/admin.php` | Neue Routennamen (.standard/.teaser) |
|
||||
| **Tests** (4) | `StandardProductCreateTest`, `ProductEditTest`, `TeaserProductCreateTest`, `TeaserProductEditTest` | Komponentennamen aktualisiert, 13 neue Tests |
|
||||
|
||||
**Geloeschte Dateien:** `create.blade.php`, `edit.blade.php`, `create-teaser.blade.php`, `edit-teaser.blade.php`
|
||||
|
||||
**Neue Tests (13):** Bildsortierung (5), Teaser-Status pending/draft (2), Teaser B2in-Artikelnummer (1), Teaser Partnernummer create+edit (3), Teaser Partnernummer pre-fill (2)
|
||||
|
||||
**Tests gesamt: 109 Produkt-Tests, alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 2.5: Produkt-Bearbeitung (Standard + Teaser) – ABGESCHLOSSEN (13.02.2026)
|
||||
|
||||
Beide Produkttypen koennen jetzt vollstaendig bearbeitet werden. Standard-Produkte (SmartOrder) nutzen das 8-Tab-Formular, Teaser-Produkte (LocalStock) ein vereinfachtes Einseiten-Formular. Die Produkt-Liste leitet automatisch zur richtigen Edit-Seite weiter.
|
||||
|
||||
Erstellte/geaenderte Dateien (4):
|
||||
- Volt: resources/views/livewire/products/edit-teaser.blade.php (neu) - Teaser-Edit mit Pre-Fill, Status-Handling, Media-Verwaltung, Activity-Log
|
||||
- Routes: routes/admin.php - products.edit.teaser Route hinzugefuegt
|
||||
- Views: resources/views/livewire/products/index.blade.php - Edit-Link basierend auf product_type
|
||||
- Tests: tests/Feature/TeaserProductEditTest.php (neu) - 24 Tests
|
||||
|
||||
Infrastruktur-Fix: Route-Cache und Test-DB-Migration bereinigt.
|
||||
|
||||
Tests gesamt: 24 Tests (TeaserProductEditTest) + 22 Tests (ProductEditTest), alle bestanden.
|
||||
Produkt-bezogene Tests gesamt: 133 Tests, alle bestanden.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2.3b: CSV-Felder Erweiterung (Moebeldatenliste) – ✅ ABGESCHLOSSEN (13.02.2026)
|
||||
|
||||
**Änderung:** Alle fehlenden Felder aus `Moebeldatenliste Stand 4.11.2025.csv` (70 Felder, 13 Sektionen) wurden als DB-Spalten und Formularfelder ergänzt. Von ~30 existierenden Feldern auf ~70 erweitert. Neue Tabelle `product_wood_origins` für EUDR-Compliance. Formular von 5 auf 8 Tabs umgebaut.
|
||||
|
||||
**Erstellte/geänderte Dateien (13):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Migrationen** (4) | `add_csv_fields_to_products_table`, `add_csv_fields_to_product_logistics_table`, `add_currency_to_product_variants_table`, `create_product_wood_origins_table` | +21 Spalten products, +4 Spalten product_logistics, +1 Spalte product_variants, neue Tabelle product_wood_origins |
|
||||
| **Models** (4) | `Product.php`, `ProductLogistics.php`, `ProductVariant.php`, `ProductWoodOrigin.php` (neu) | Fillable, Casts, woodOrigins() Relationship |
|
||||
| **Factories** (1) | `ProductWoodOriginFactory.php` (neu) | Factory mit Holzarten, Ländern, EUDR-Daten |
|
||||
| **Volt-Komponenten** (1) | `products/create.blade.php` | 8-Tab-Layout, ~25 neue Properties, Wood-Origins-Repeater, erweiterte Validierung + Save-Logik |
|
||||
| **Tests** (2) | `StandardProductCreateTest.php` (+12 Tests), `Models/ProductWoodOriginTest.php` (4 Tests, neu) | Material, Logistik, Services, Nachhaltigkeit, EUDR, Scoring, Währung, Validierung |
|
||||
|
||||
**Neue DB-Spalten (26):** products: country_of_origin, main_material, surface_material, cover_material, color_finish, certificates(JSON), assembly_time_min, load_capacity_kg, delivery_type, assembly_service, service_radius_km, warranty_months, production_time_days, visible_from, visible_until, co2_footprint_kg, recycling_percentage, is_regional_production, storage_volume_liters, assembly_effort_score, design_score. product_logistics: packaging_type, packaging_recyclable_percent, is_palletizable, hs_code. product_variants: currency.
|
||||
|
||||
**Neue Tabelle:** `product_wood_origins` (1:n von products) – EUDR-Compliance mit Holzart, Herkunftsland, Region, Erntejahr, Forstbetrieb, Zertifikat, EUDR-ID
|
||||
|
||||
**Pint-Formatierung:** ✅ (keine Korrekturen nötig)
|
||||
|
||||
**Tests gesamt: 33 Tests (StandardProductCreateTest) + 4 Tests (ProductWoodOriginTest), alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 2.3: Standard-Produkt Erstellung (Maske 2) – ✅ ABGESCHLOSSEN (13.02.2026)
|
||||
|
||||
**Änderung:** Das Standard-Produkt-Formular (`create.blade.php`) wurde komplett neu geschrieben – von einer nicht-funktionalen Dummy-Vorlage zu einem voll funktionsfähigen class-based Volt Component mit 5 Tabs.
|
||||
|
||||
**Erstellte/geänderte Dateien (3):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Volt-Komponenten** (1) | `resources/views/livewire/products/create.blade.php` | Komplett neugeschrieben: 5-Tab-Layout (Basis, Bilder, Physisch, Kommerziell, Zuordnung), erstellt Product + ProductVariant + ProductLogistics + Media, inline Validierung, Preise EUR→Cents |
|
||||
| **Migrationen** (1) | `database/migrations/2026_02_13_..._make_tax_rate_id_nullable_on_product_variants_table.php` | `tax_rate_id` nullable gemacht (war NOT NULL ohne Default, tax_rates Tabelle leer) |
|
||||
| **Tests** (1) | `tests/Feature/StandardProductCreateTest.php` | 17 neue Tests: Zugriff (3), Happy Path (2), Physisch+Logistik (1), Kommerziell (1), SEO (1), Validierung (8), Preistypen (1) |
|
||||
|
||||
**Pint-Formatierung:** ✅ (1 Fix: unused import in StandardProductCreateTest)
|
||||
|
||||
**Tests gesamt: 17 Tests (StandardProductCreateTest), alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 2 Ergänzung: Beide Rollen → Beide Produkttypen (13.02.2026)
|
||||
|
||||
**Änderung:** Sowohl Händler (Retailer) als auch Hersteller (Manufacturer) können nun BEIDE Produkttypen anlegen (Teaser + Standard). Vorher war die Erstellmaske rollenbasiert auf einen Typ beschränkt.
|
||||
|
||||
**Erstellte/geänderte Dateien (3):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Volt-Komponenten** (1) | `resources/views/livewire/products/index.blade.php` | Zwei Buttons ("Neues Teaser-Produkt" + "Neues Standard-Produkt"), neuer `productTypeFilter` State + Query-Filter, Produkttyp-Dropdown im Filterbereich |
|
||||
| **Tests** (2) | `tests/Feature/LocalFeedTest.php`, `tests/Feature/TeaserProductCreateTest.php` | +6 neue Tests: Produkttyp-Filter (3), Zwei-Button-Anzeige (3), Manufacturer Teaser-Zugriff (1), Manufacturer Teaser-Erstellung (1). Fix: `mainImage` → `mainImages` in bestehenden Tests |
|
||||
|
||||
**Pint-Formatierung:** ✅ (keine Korrekturen nötig)
|
||||
|
||||
**Tests gesamt: 21 Tests (LocalFeedTest + TeaserProductCreateTest), alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Kunden-Frontend & Local Feed – ✅ KERN ABGESCHLOSSEN (12.02.2026)
|
||||
|
||||
**Erstellte/geänderte Dateien (8):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Actions** (1) | `app/Actions/Fortify/CreateNewUser.php` | `origin` aus `config('app.theme')` via `UserOrigin::tryFrom()`, `hub_id` aus Input |
|
||||
| **Models** (1) | `app/Models/Partner.php` | `PartnerType` Cast hinzugefügt (`type` Feld) |
|
||||
| **Volt-Komponenten** (2) | `resources/views/livewire/products/index.blade.php`, `resources/views/livewire/partner/profile.blade.php` | products/index: echte DB-Queries, Rollen-basierte Filterung (Admin/Customer/Partner). partner/profile: neue öffentliche Profilseite |
|
||||
| **Routes** (1) | `routes/admin.php` | `partner.profile` Route hinzugefügt |
|
||||
| **Tests** (3) | `CreateNewUserOriginTest`, `LocalFeedTest`, `PartnerProfilePageTest` | **17 Tests – alle bestanden** |
|
||||
|
||||
**Wichtige Korrekturen (12.02.2026):**
|
||||
- `products` Tabelle hat keine `sku` Spalte – aus Suche und Tabellenansicht entfernt
|
||||
- Partner `type` war nicht auf `PartnerType` Enum gecastet – `PartnerType::class` Cast zu `Partner.php` hinzugefügt
|
||||
- `Volt::test()` wirft `ModelNotFoundException` direkt (kein HTTP 404) – 404-Tests nutzen `toThrow()`
|
||||
|
||||
**Pint-Formatierung:** ✅ (5 Dateien: Partner.php, CreateNewUser.php, 3 Test-Dateien)
|
||||
|
||||
**Tests Phase 3 gesamt: 17 Tests, alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Händler-Profil & Produkt-Management – ✅ KERN ABGESCHLOSSEN (12.02.2026)
|
||||
|
||||
**Erstellte/geänderte Dateien (11):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Policies** (2) | `app/Policies/PartnerPolicy.php`, `ProductPolicy.php` | PartnerPolicy: viewAny/view/create/update/delete/curateProducts. ProductPolicy: viewAny/view/create/update/delete/curate |
|
||||
| **Volt-Komponenten** (4) | `admin/partners/index.blade.php`, `admin/partners/edit.blade.php`, `products/create-teaser.blade.php`, `admin/products/index.blade.php` | Partner-Übersicht, Profil-Edit (Story+Öffnungszeiten), Teaser-Erstellen (Typ A, Preislogik via Enum), Kuration-Queue |
|
||||
| **Routes** (1) | `routes/admin.php` | 4 neue Routen: `admin.partners.index`, `admin.partners.edit`, `products.create.teaser`, `admin.products.index` |
|
||||
| **Seeders** (1) | `database/seeders/RoleSeeder.php` | `curate products` Permission hinzugefügt |
|
||||
| **Tests** (5) | `PartnerPolicyTest`, `ProductPolicyTest`, `PartnerProfileUpdateTest`, `TeaserProductCreateTest`, `ProductCurationTest` | **48 Tests – alle bestanden** |
|
||||
|
||||
**Enums erweitert (12.02.2026):**
|
||||
- `app/Enums/ProductType.php`: `requiresTicket(): bool`, `allowedPriceTypes(): array`
|
||||
- `tests/Unit/Enums/ProductTypeTest.php`: 4 neue Tests für Typ A/B Geschäftsregeln
|
||||
|
||||
**Pint-Formatierung:** ✅ (keine Style-Korrekturen nötig)
|
||||
|
||||
**Wichtige Erkenntnisse:**
|
||||
- Volt `actingAs()` ist `void` – für Tests immer `$this->actingAs($user)` VOR `Volt::test()` aufrufen
|
||||
- Für HTTP-Tests mit `partner.setup` Middleware: `Partner::factory()->setupCompleted()->create()` verwenden
|
||||
- `Storage::fake('public')` + `UploadedFile::fake()->image()` für File-Upload-Tests
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: DB & Core-Vervollständigung – ✅ ABGESCHLOSSEN (12.02.2026)
|
||||
|
||||
**Erstellte Dateien (29 Dateien):**
|
||||
|
||||
| Kategorie | Dateien | Details |
|
||||
|-----------|---------|---------|
|
||||
| **Enums** (6) | `app/Enums/ProductType.php`, `ProductStatus.php`, `PriceType.php`, `PartnerType.php`, `UserOrigin.php`, `CurationStatus.php` | Alle mit `label()`, Status-Enums zusätzlich mit `color()`, UserOrigin mit `tonality()` |
|
||||
| **Neue Models** (4) | `app/Models/Product.php`, `Media.php`, `Setting.php`, `ProductLogistics.php` | Product: 6 Relationships, 6 Scopes. Media: Polymorphe Beziehung. Setting: Key-Value mit getValue/setValue Helpers |
|
||||
| **Aktualisierte Models** (11) | `Attribute`, `AttributeValue`, `Category`, `Collection`, `Tag`, `ProductVariant`, `ShippingClass`, `TaxRate`, `Hub`, `Partner`, `User` | Alle Sparse Models mit Fillable, Casts, Relationships ergänzt. Partner: +products(), +media(). User: +hub(), +origin Cast |
|
||||
| **Migrationen** (4) | `2026_02_12_000001` bis `000004` | Users (+origin, +hub_id), Products (+product_type, +is_curated, +hub_id, +price_type, +is_available, +curated_at, +curated_by), Partners (+story_text, +opening_hours, +specialties, +founded_year), Settings-Tabelle |
|
||||
| **Factories** (5) | `ProductFactory`, `MediaFactory`, `PartnerFactory`, `HubFactory`, `BrandFactory` | Alle mit sinnvollen States (localStock, smartOrder, active, retailer, manufacturer, etc.) |
|
||||
| **Seeders** (1) | `SettingsSeeder` | 6 Default-Settings: Ticket-Gültigkeit, Beleg-Deadline, Ticket-Limits, Provisions-Defaults |
|
||||
| **Config** (1) | `config/domains.php` | +local4local Domain (local4local.test / local4local.online) |
|
||||
| **Tests** (7 Dateien) | 3× Unit (Enums), 4× Feature (Product, Setting, User, Partner) | **47 Tests, 84 Assertions – alle bestanden** |
|
||||
|
||||
**Migrationen auf Produktions-DB ausgeführt:** ✅
|
||||
**Settings-Seeder ausgeführt:** ✅ (6 Settings angelegt)
|
||||
**Pint-Formatierung:** ✅ (7 Style-Issues automatisch korrigiert)
|
||||
|
||||
**Vorbestehende Test-Failures – BEHOBEN (12.02.2026):**
|
||||
Alle 16 vorbestehenden Fehler plus 15 weitere Fehler (31 insgesamt) wurden systematisch behoben. Die gesamte Test-Suite besteht jetzt mit **139 Tests, 269 Assertions – alle bestanden** ✅
|
||||
|
||||
Behobene Probleme:
|
||||
- `phpunit.xml`: `BASIC_AUTH_ENABLED=false` hinzugefügt (BasicAuth-Middleware blockierte alle HTTP-Tests)
|
||||
- `admin/partners/index.blade.php`: Flux UI v2 Shorthand `<flux:columns>` → `<flux:table.columns>` korrigiert
|
||||
- `config/livewire.php`: `component_layout` Key hinzugefügt (Livewire 4 Layout-Resolution)
|
||||
- Auth-Tests: Portal-Domain (`portal.b2in.test`) für alle HTTP-Requests
|
||||
- `RegistrationTest`: Komplett neu geschrieben für code-basierte Registrierung über `/reg/{role}`
|
||||
- `PasswordResetTest`: `CustomResetPasswordNotification` statt Standard-`ResetPassword`
|
||||
- `EmailVerificationTest`: Redirect zu `partner.setup.wizard` statt `dashboard`
|
||||
- `ProfileUpdateTest`: SoftDeletes-Assertion (`->trashed()`) und Layout-Redirect
|
||||
- `DashboardTest`: `RefreshDatabase` Trait hinzugefügt
|
||||
- `ProductCurationTest`, `PartnerProfileUpdateTest`, `TeaserProductCreateTest`: `actingAs()` Chaining korrigiert (Volt `actingAs()` ist `void`)
|
||||
- `TeaserProductCreateTest`: `mainImage` Upload, `setupCompleted()` Factory-State, `CategoryFactory` erstellt
|
||||
- `ProductCurationTest`: Authorization-Test angepasst (Component authorize in `with()`)
|
||||
- `Category` Model: `HasFactory` Trait hinzugefügt
|
||||
|
||||
---
|
||||
|
||||
## 1. Validierung: IST-Zustand vs. Konzeption
|
||||
|
||||
### ✅ Bereits implementiert und konzeptkonform
|
||||
|
||||
| Bereich | Status | Details |
|
||||
|---------|--------|---------|
|
||||
| **Hub-System** | ✅ Fertig | `hubs` + `hub_locations` Tabellen, Hub Model mit Relationships |
|
||||
| **Partner-System** | ✅ Basis fertig | `partners` Tabelle mit `hub_id`, `type`, Provisionsfelder, Parent/Child-Beziehungen |
|
||||
| **User-System** | ✅ Basis fertig | Users mit `partner_id`, SoftDeletes, Spatie Roles |
|
||||
| **Rollen & Permissions** | ✅ Fertig | Customer, Estate-Agent, Retailer, Manufacturer, Admin, Super-Admin (via Spatie) |
|
||||
| **Registrierungs-Codes** | ✅ Fertig | `registration_codes` mit `broker_partner_id` für Makler→Kunden Attribution |
|
||||
| **Partner-Invitations** | ✅ Fertig | `partner_invitations` mit Token, Expiry, Status |
|
||||
| **Multi-Domain-Routing** | ✅ Fertig | ThemeMiddleware, ThemeServiceProvider, `config/domains.php` (5 Domains) |
|
||||
| **Auth-System** | ✅ Fertig | Fortify + Sanctum, Login, Register, Passwort-Reset, Email-Verifizierung, 2FA |
|
||||
| **Admin-Portal** | ✅ Basis fertig | Dashboard, User-Management, Partner-Management, Hub-Management, CMS |
|
||||
| **Partner-Setup-Wizard** | ✅ Fertig | Setup-Workflow für neue Partner nach Registrierung |
|
||||
| **Produkt-DB-Struktur** | ✅ Tabellen vorhanden | `products`, `product_variants`, `categories`, `tags`, `brands`, `collections`, `attributes`, `media` |
|
||||
|
||||
### ⚠️ Teilweise implementiert – Erweiterung nötig
|
||||
|
||||
| Bereich | Was fehlt | Konzept-Referenz |
|
||||
|---------|-----------|-----------------|
|
||||
| **Product Model** | Migration existiert, aber **kein `App\Models\Product`** – Model muss erstellt werden | Abschnitt 2: Produkt-Modul |
|
||||
| **Media Model** | Migration existiert, aber **kein `App\Models\Media`** – Model muss erstellt werden | Für Produkt-Bilder, Partner-Galerie |
|
||||
| **Partner-Profil** | Basis-Felder vorhanden, aber es fehlen: Team-Fotos, Showroom-Galerie, Story-Text, Öffnungszeiten | Abschnitt 3: "Faces" Profile |
|
||||
| **Produkt-Upload UI** | `livewire/products/create` existiert als Blade, aber unvollständig | Abschnitt 2: Händler-Upload |
|
||||
| **Produkt-Feed** | `livewire/products/index` existiert, aber keine Hub-basierte Filterung | Abschnitt 5: Local Feed |
|
||||
| **Sparse Models** | `Attribute`, `AttributeValue`, `Category`, `Collection`, `Tag`, `ProductVariant`, `ShippingClass`, `TaxRate` haben keine Relationships definiert | Allgemein |
|
||||
|
||||
### ❌ Nicht implementiert – Muss gebaut werden
|
||||
|
||||
| Bereich | Beschreibung | Konzept-Referenz |
|
||||
|---------|-------------|-----------------|
|
||||
| **User `origin` Feld** | Herkunft des Kunden (`style2own` / `stileigentum`) für Theme-Steuerung | Abschnitt 1: Core-Modul |
|
||||
| **User `hub_id` Feld** | Direkte Hub-Zuordnung für Kunden (nicht nur indirekt über Partner) | Abschnitt 1: Hub-Logik |
|
||||
| **`product_type` Feld** | Unterscheidung `local_stock` (Säule A) vs. `smart_order` (Säule B) | Abschnitt 2: Produkt-Modul |
|
||||
| **`is_curated` Feld** | Admin-Freigabe-Flag für Produkt-Sichtbarkeit | Abschnitt 2: Produkt-Modul |
|
||||
| **Ticket-System** | Komplett fehlend: Tickets, QR-Codes, Voucher-Generierung | Abschnitt 3A: Ticket-System |
|
||||
| **Transaction/Receipt** | Beleg-Upload, State Machine (pending→confirmed→paid→distributed) | Abschnitt 3B: Clearing-System |
|
||||
| **Wallet/Ledger** | Cashback-Wallets, Provisions-Split, Kassenbuch | Abschnitt 3C: Wallet-Logik |
|
||||
| **Kunden-Dashboard** | "Mein Zuhause" – emotionaler Feed mit Produkten aus dem eigenen Hub | Abschnitt 4: User Journey |
|
||||
| **Setup-Buchung** | Service-Store für Händler (z.B. Setup-Paket 399€) | Abschnitt 4: Partner-Modul |
|
||||
| **Enums** | Keine PHP Enums vorhanden (ProductType, TransactionStatus, TicketStatus, etc.) | Best Practice |
|
||||
| **Form Requests** | Keine Form Request Klassen vorhanden | Best Practice |
|
||||
| **Policies** | Keine Authorization Policies vorhanden | Best Practice |
|
||||
|
||||
---
|
||||
|
||||
## 2. Entwicklungsphasen
|
||||
|
||||
### Phase 1: Datenbank & Core-Vervollständigung ✅ ABGESCHLOSSEN
|
||||
**Geschätzter Aufwand:** 2-3 Tage | **Tatsächlich:** 1 Tag (12.02.2026)
|
||||
**Priorität:** HÖCHSTE – Basis für alle weiteren Phasen
|
||||
|
||||
#### 1.1 Fehlende Models erstellen
|
||||
- [ ] `App\Models\Product` erstellen mit allen Relationships:
|
||||
- `belongsTo(Partner)`, `belongsTo(Brand)`, `belongsTo(Collection)`
|
||||
- `belongsToMany(Category)`, `belongsToMany(Tag)`
|
||||
- `hasMany(ProductVariant)`, `morphMany(Media)`
|
||||
- `hasMany(RelatedProduct)`
|
||||
- Scopes: `active()`, `curated()`, `localStock()`, `smartOrder()`, `inHub($hubId)`
|
||||
- [ ] `App\Models\Media` erstellen (polymorphe Beziehung)
|
||||
- [ ] Fehlende Relationships in Sparse Models ergänzen:
|
||||
- `Attribute` → `hasMany(AttributeValue)`
|
||||
- `AttributeValue` → `belongsTo(Attribute)`
|
||||
- `Category` → self-referencing parent/children, `belongsToMany(Product)`
|
||||
- `Tag` → `belongsToMany(Product)`
|
||||
- `ProductVariant` → `belongsTo(Product)`, `belongsToMany(AttributeValue)`
|
||||
- etc.
|
||||
- [ ] `Partner::products()` Relationship aktivieren (aktuell auskommentiert)
|
||||
|
||||
#### 1.2 Migrationen: User-Erweiterung
|
||||
```
|
||||
Migration: add_origin_and_hub_id_to_users_table
|
||||
```
|
||||
- [ ] `origin` (nullable string) – `style2own` | `stileigentum` | null
|
||||
- [ ] `hub_id` (nullable FK → hubs) – direkte Hub-Zuordnung für Kunden
|
||||
- [ ] `broker_partner_id` (nullable FK → partners) – direkte Makler-Attribution (Alternative zu indirekter Verknüpfung über Partner-Hierarchie; **Entscheidung nötig**: reicht `partner.parent_partner_id` oder braucht User direkt ein Feld?)
|
||||
|
||||
> **Offene Frage 1:** Soll der Kunde einen eigenen Partner-Eintrag bekommen (so wie jetzt über `partner_id` → Partner mit `parent_partner_id`) oder reicht eine direkte `hub_id` + `broker_partner_id` auf dem User? Die aktuelle Architektur erstellt für jeden Kunden einen Partner-Eintrag. Das könnte für das Wallet/Ledger System nützlich sein (jeder Partner hat eigene Provision-Settings). **Empfehlung:** Beibehalten, aber `hub_id` und `origin` direkt auf User setzen für schnelle Queries.
|
||||
|
||||
#### 1.3 Migrationen: Produkt-Erweiterung
|
||||
```
|
||||
Migration: add_marketplace_fields_to_products_table
|
||||
```
|
||||
- [ ] `product_type` (string, default: `local_stock`) – `local_stock` | `smart_order`
|
||||
- [ ] `is_curated` (boolean, default: false) – Admin-Freigabe
|
||||
- [ ] `curated_at` (nullable datetime) – Zeitpunkt der Freigabe
|
||||
- [ ] `curated_by` (nullable FK → users) – Wer hat freigegeben
|
||||
- [ ] `hub_id` (nullable FK → hubs) – Direkte Hub-Zuordnung (ergänzend zu Partner→Hub)
|
||||
- [ ] `price_type` (string) – `fixed` | `from_price` | `on_request` (Preisanzeige-Logik)
|
||||
- [ ] `price_display_text` (nullable string) – z.B. "Ab 2.500 €" Freitext
|
||||
- [ ] `is_available` (boolean, default: true) – Verfügbar/Verkauft Toggle für Händler
|
||||
|
||||
#### 1.4 Migrationen: Partner-Profil Erweiterung
|
||||
```
|
||||
Migration: add_profile_fields_to_partners_table
|
||||
```
|
||||
- [ ] `story_text` (nullable text) – "Seit 1950 in Herford..."
|
||||
- [ ] `opening_hours` (nullable JSON) – Öffnungszeiten strukturiert
|
||||
- [ ] `specialties` (nullable JSON) – Fachgebiete/Spezialisierungen
|
||||
- [ ] `founded_year` (nullable integer) – Gründungsjahr
|
||||
|
||||
> **Hinweis:** Team-Fotos und Showroom-Galerie werden über die polymorphe `media` Tabelle abgebildet (type: `team_photo`, `showroom`, `gallery`).
|
||||
|
||||
#### 1.5 Migration: Settings-Tabelle
|
||||
```
|
||||
Migration: create_settings_table
|
||||
```
|
||||
- [ ] `id`
|
||||
- [ ] `group` (string) – z.B. `tickets`, `marketplace`, `commissions`
|
||||
- [ ] `key` (string, unique innerhalb group)
|
||||
- [ ] `value` (text, nullable)
|
||||
- [ ] `type` (string) – `string` | `integer` | `boolean` | `json`
|
||||
- [ ] `description` (nullable text) – Beschreibung für Admin-UI
|
||||
- [ ] `timestamps`
|
||||
|
||||
Default-Werte per Seeder:
|
||||
- `tickets.validity_days` = 30
|
||||
- `tickets.receipt_upload_deadline_days` = 30
|
||||
- `tickets.max_per_merchant_per_customer` = 3
|
||||
- `tickets.max_merchants_per_customer` = 4
|
||||
- `commissions.default_broker_rate` = 0 (individuell)
|
||||
- `commissions.default_cashback_rate` = 0 (individuell)
|
||||
|
||||
#### 1.6 Domain-Ergänzung: local4local
|
||||
- [ ] `config/domains.php` um `local4local` Domain erweitern:
|
||||
- Produktion: `local4local.online`
|
||||
- Entwicklung: `local4local.test`
|
||||
- Theme, Farben, Fonts definieren
|
||||
- [ ] `.env` Variablen: `DOMAIN_LOCAL4LOCAL=local4local.test`
|
||||
- [ ] Routing in `routes/domains.php` einbinden
|
||||
|
||||
#### 1.7 Enums erstellen
|
||||
- [ ] `App\Enums\ProductType` – `LocalStock`, `SmartOrder`
|
||||
- [ ] `App\Enums\ProductStatus` – `Draft`, `Active`, `Archived`, `Sold`
|
||||
- [ ] `App\Enums\PriceType` – `Fixed`, `FromPrice`, `OnRequest`
|
||||
- [ ] `App\Enums\PartnerType` – `Retailer`, `Manufacturer`, `EstateAgent`
|
||||
- [ ] `App\Enums\UserOrigin` – `Style2Own`, `StilEigentum`
|
||||
- [ ] `App\Enums\CurationStatus` – `Pending`, `Approved`, `Rejected`
|
||||
|
||||
#### 1.8 Factories & Seeders
|
||||
- [ ] `ProductFactory` erstellen
|
||||
- [ ] `MediaFactory` erstellen
|
||||
- [ ] `SettingFactory` erstellen
|
||||
- [ ] `PartnerFactory` erweitern (fehlende Felder)
|
||||
- [ ] `ProductSeeder` für Testdaten
|
||||
- [ ] `SettingsSeeder` für Default-Werte
|
||||
- [ ] `HubSeeder` erweitern (wenn nötig)
|
||||
|
||||
#### 1.9 Tests Phase 1
|
||||
- [ ] Unit-Tests für alle neuen Models und Relationships
|
||||
- [ ] Unit-Tests für Enums
|
||||
- [ ] Feature-Tests für Migrations (Datenintegrität)
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Händler-Profil & Produkt-Management – ✅ KERN ABGESCHLOSSEN (12.02.2026)
|
||||
**Geschätzter Aufwand:** 5-6 Tage | **Tatsächlich:** 1 Tag (12.02.2026)
|
||||
**Priorität:** HOCH – Content-Erstellung ermöglichen
|
||||
|
||||
#### 2.0 Architektur-Entscheidung: EINE Tabelle, ZWEI Masken
|
||||
|
||||
> **Entscheidung:** Beide Produkttypen nutzen dieselbe `products` Tabelle. Das Feld `product_type` (`local_stock` | `smart_order`) bestimmt, welche Eingabemaske und Validierung angewendet wird.
|
||||
|
||||
**Begründung:**
|
||||
- Beides sind Produkte mit identischem Kern (Name, Beschreibung, Preis, Bilder, Partner, Hub, Kategorie)
|
||||
- Der Local Feed zeigt beide Typen gemischt an – eine Tabelle ermöglicht einfache Queries ohne UNION
|
||||
- Das Teaser-Produkt ist ein "leichtes" Produkt: die Felder, die es nicht braucht, bleiben `null`
|
||||
- Ein Model, ein Satz Relationships, ein Satz Scopes – kein doppelter Code
|
||||
- Filterung per Scope: `Product::localStock()`, `Product::smartOrder()`
|
||||
|
||||
---
|
||||
|
||||
#### Kundenfeedback: Typ A vs. Typ B (12.02.2026)
|
||||
|
||||
> **Implementiert in `app/Enums/ProductType.php`:**
|
||||
> - `requiresTicket(): bool` → `LocalStock = true`, `SmartOrder = false`
|
||||
> - `allowedPriceTypes(): array` → `LocalStock = [FromPrice, OnRequest]`, `SmartOrder = [Fixed, FromPrice, OnRequest]`
|
||||
|
||||
**Typ A – Teaser-Produkte** (`product_type = 'local_stock'`)
|
||||
|
||||
Komplex → Beratung → Laden → **Ticket zwingend erforderlich**
|
||||
|
||||
- Aufwendige Konfiguration (Maße, Module, Materialien) – Abschluss nur im Laden
|
||||
- Online nur Beispiele, Referenzen, grobe Preisindikationen
|
||||
- Typisch: Küchenstudios, maßgefertigte Möbel (Cabinet), Einbausysteme
|
||||
- **Preistyp:** Nur `from_price` oder `on_request` – Festpreis online nicht erlaubt
|
||||
- **Ticket:** zwingend (`ProductType::LocalStock->requiresTicket() === true`)
|
||||
|
||||
**Typ B – Standard-Produkte** (`product_type = 'smart_order'`)
|
||||
|
||||
Einfach → verständlich → skalierbar → **Ticket optional / Direktkauf möglich**
|
||||
|
||||
- Klare Varianten, vollständig online konfigurierbar, ca. 99 % des Sortiments
|
||||
- **Preistyp:** Alle erlaubt (`fixed`, `from_price`, `on_request`)
|
||||
- **Ticket:** optional – Direktkauf online möglich oder Ticket + Ladenbesuch
|
||||
|
||||
---
|
||||
|
||||
**Maske 1: Typ A – Teaser-Produkt / Local Stock (Händler)**
|
||||
Ziel: Extrem vereinfacht, handy-optimiert. Kein komplexes Warenwirtschafts-Monster.
|
||||
|
||||
| Feld | DB-Spalte | Pflicht | Beschreibung |
|
||||
|------|-----------|---------|-------------|
|
||||
| Fotos | → `media` (morph) | Ja (min. 1) | 1 Hauptbild + optional 2 Galerie-Bilder |
|
||||
| Titel | `name` | Ja | Produktname |
|
||||
| Kurzbeschreibung | `description_short` | Ja | Max. 180 Zeichen |
|
||||
| Preistyp | `price_type` | Ja | Nur `from_price` oder `on_request` (via `allowedPriceTypes()`) |
|
||||
| Preisangabe | `price_display_text` | Cond. | Pflicht wenn `from_price` (z.B. "Ab 2.500 €") |
|
||||
| Kategorie | → `category_product` (Pivot) | Ja | Dropdown-Auswahl |
|
||||
| Status | `status` | Ja | Verfügbar / Verkauft |
|
||||
|
||||
Automatisch gesetzt: `product_type = 'local_stock'`, `hub_id` = Hub des Händlers, `partner_id` = Partner des Users.
|
||||
`PriceType::Fixed` für Maske 1 **nicht erlaubt** – Validierung via `ProductType::LocalStock->allowedPriceTypes()`.
|
||||
|
||||
**Maske 2: Typ B – Konfigurations-Produkt / Smart Order (Hersteller)**
|
||||
Das bestehende 6-Tab-Formular (`livewire/products/create.blade.php`) mit 13 Sektionen:
|
||||
Basis → Bilder → Physisch → Material & Herkunft → Kommerziell → Zuordnung & Verwaltung
|
||||
|
||||
Automatisch gesetzt: `product_type = 'smart_order'`, `partner_id` = Partner des Users.
|
||||
Hub-Zuordnung: Manuell wählbar (Hersteller können in mehreren Hubs aktiv sein).
|
||||
Alle Preistypen erlaubt; Standard-Varianten mit Festpreis möglich.
|
||||
|
||||
**Routing-Logik:** ✅ AKTUALISIERT (13.02.2026)
|
||||
```
|
||||
// Beide Rollen können BEIDE Produkttypen anlegen.
|
||||
// Die Produktliste zeigt zwei Buttons:
|
||||
// "Neues Teaser-Produkt" → Maske 1 (Typ A: Teaser, vereinfacht, Ticket zwingend)
|
||||
// "Neues Standard-Produkt" → Maske 2 (Typ B: Konfiguration, komplex, Ticket optional)
|
||||
// Sichtbar für: Retailer, Manufacturer, Admin, Super-Admin
|
||||
// Zusätzlich: Produkttyp-Filter in der Produktliste (Teaser / Standard / Alle)
|
||||
```
|
||||
|
||||
#### 2.1 Partner-Profil Erweiterung (Admin-Portal) – ✅ Kern implementiert
|
||||
- [x] Partner-Profil Formular erweitern:
|
||||
- [x] Story-Text Editor (`<flux:textarea>`)
|
||||
- [x] Öffnungszeiten-Eingabe (strukturiertes JSON-Formular, 7 Wochentage)
|
||||
- [ ] Team-Fotos Upload (mehrere Bilder via `<flux:file-upload>`) – ausstehend
|
||||
- [ ] Showroom-Galerie Upload – ausstehend
|
||||
- [ ] Form Request: `UpdatePartnerProfileRequest` – inline Validierung verwendet
|
||||
- [x] Policy: `PartnerPolicy` (viewAny, view, update, delete, curateProducts)
|
||||
- [x] Admin Partner-Übersicht: `admin/partners/index.blade.php` (Suche, Filter, Tabelle)
|
||||
|
||||
#### 2.2 Produkt-CRUD: Maske 1 – Teaser / Local Stock (Händler) – ✅ Implementiert
|
||||
- [x] Volt-Komponente: `livewire/products/form-teaser.blade.php` (merged Create + Edit)
|
||||
- [x] Einzelseiten-Formular (KEIN Tab-Layout)
|
||||
- [x] Bild-Upload via `WithFileUploads` + Drag & Drop Bildsortierung
|
||||
- [x] Felder: Foto, Titel, Kurzbeschreibung, Preistyp (nur FromPrice/OnRequest), Preistext, Kategorie, Status, Produktnummer
|
||||
- [x] Produktnummern: Partner-Produktnummer (auto-generiert) + B2in-Artikelnummer (auto bei Save)
|
||||
- [x] Automatisch: `product_type = 'local_stock'`, `hub_id` vom Händler-Hub, `partner_id`
|
||||
- [x] Preistyp-Validierung via `ProductType::LocalStock->allowedPriceTypes()` (kein Festpreis)
|
||||
- [x] Freigabe-Workflow: UI "aktiv" → DB `pending` (korrekt zur Freigabe, nicht direkt Active)
|
||||
- [x] Produkt-Liste: Zwei Buttons ("Neues Teaser-Produkt" + "Neues Standard-Produkt") für alle Partner-Rollen
|
||||
- [x] Produkt-Liste: Produkttyp-Filter (Teaser / Standard / Alle)
|
||||
- [x] Produkt-Liste: Vorschaubild (erstes Bild nach order_column) vor Produktname
|
||||
- [x] Produkt bearbeiten: Pre-Fill, Status-Handling, Media-Verwaltung, Activity-Log, Toast-Notification
|
||||
- [x] Bildsortierung: Drag & Drop, erstes Bild = Standardbild, order_column in DB
|
||||
- [x] Produkt archivieren / als "Verkauft" markieren – in Edit-Formular und Produktliste (Dropdown-Menue)
|
||||
- [ ] Form Request: `StoreTeaserProductRequest` – inline Validierung verwendet
|
||||
- [x] Policy: `ProductPolicy` (viewAny, view, create, update, delete, curate)
|
||||
|
||||
#### 2.3 Produkt-CRUD: Maske 2 – Standard / Smart Order – ✅ Implementiert (13.02.2026)
|
||||
- [x] Volt-Komponente: `livewire/products/form-standard.blade.php` (merged Create + Edit)
|
||||
- [x] 8-Tab-Layout: Basis, Bilder, Maße & Material, Verpackung & Versand, Kommerziell, Service & Garantie, Nachhaltigkeit, Zuordnung
|
||||
- [x] Erstellt Product + Master-Variante (ProductVariant) + ProductLogistics + Media
|
||||
- [x] Preis-Typ Logik: Festpreis / Ab-Preis / Preis auf Anfrage (alle 3 für SmartOrder erlaubt)
|
||||
- [x] Automatisch: `product_type = 'smart_order'`, Hub-Fallback auf Partner-Hub
|
||||
- [x] File-Uploads: Mehrere Bilder (max 10, JPG/PNG, max 10 MB) + Drag & Drop Bildsortierung
|
||||
- [x] Preise in EUR eingeben → automatische Umrechnung in Cents bei Speicherung
|
||||
- [x] Alle optionalen Felder: MPN, EAN/GTIN, UVP, EK, Lagerstatus, Lieferzeit, SEO-Metadaten, Maße, Verpackung
|
||||
- [x] Validierung inline (kein separater Form Request – konsistent mit Teaser-Formular)
|
||||
- [x] Authorization: ProductPolicy + Rollenprüfung (Retailer, Manufacturer, Admin, Super-Admin)
|
||||
- [x] Bei Edit: Toast-Notification, aktiver Tab bleibt erhalten, kein Redirect
|
||||
- [x] Migration: `tax_rate_id` auf `product_variants` nullable gemacht (war NOT NULL ohne Default)
|
||||
|
||||
#### 2.4 Admin: Produkt-Kuration (Approval Queue) – ✅ Vollstaendig implementiert
|
||||
- [x] Admin-View: `admin/products/index.blade.php` – Tabelle mit Filtern (Status, Typ, Haendler, Kategorie, Suche)
|
||||
- [x] Statistik-Karten: Gesamt, Zur Freigabe, In Korrektur, Freigegeben (klickbar als Schnellfilter)
|
||||
- [x] Approve Action: `is_curated=true`, `curated_at`, `curated_by` + Flux::toast()
|
||||
- [x] Correction Action: Inline-Formular mit Pflicht-Textfeld → `curation_notes`
|
||||
- [x] Reject Action: Inline-Formular mit Pflicht-Ablehnungsgrund → `curation_notes` + `status=Archived`
|
||||
- [x] Autorisierung via `ProductPolicy::curate` (`curate products` Permission)
|
||||
- [x] Admin kann alle Produkte bearbeiten (gleiche Edit-Formulare wie Haendler)
|
||||
- [x] Admin kann Produkte archivieren / als verkauft markieren (Dropdown-Menue)
|
||||
- [x] Erstellungsdatum in Tabelle sichtbar
|
||||
- [x] Kuration-Hinweis beim Haendler: Callout im Edit-Formular (Korrektur gelb, Ablehnung rot)
|
||||
- [ ] Notification an Händler/Hersteller bei Freigabe/Ablehnung – ausstehend
|
||||
- [ ] Admin kann `product_type` bei Bedarf nachträglich ändern – ausstehend
|
||||
|
||||
#### 2.5 Tests Phase 2 – ✅ Kern abgeschlossen
|
||||
- [x] Feature-Tests: `PartnerPolicyTest` (10 Tests) – viewAny, view, update, curateProducts
|
||||
- [x] Feature-Tests: `ProductPolicyTest` (14 Tests) – alle Policy-Methoden
|
||||
- [x] Feature-Tests: `PartnerProfileUpdateTest` (8 Tests) – Profil-Update, Öffnungszeiten, Validierung
|
||||
- [x] Feature-Tests: `TeaserProductCreateTest` (18 Tests) – Happy Path, Preistyp-Validierung, Autorisierung, Status-Logik, Produktnummern
|
||||
- [x] Feature-Tests: `ProductCurationTest` (23 Tests) – Approve, Reject mit Pflicht-Textfeld, Correction, Archive/Sold, Filter, Kuration-Notes-Anzeige im Edit
|
||||
- [x] Feature-Tests: `StandardProductCreateTest` (39 Tests) – Zugriff, Happy Path, alle Tabs, Validierung, Preistypen, Produktnummern, Marken
|
||||
- [x] Feature-Tests: `ProductEditTest` (27 Tests) – Pre-Fill, Save, Status, Validierung, Media-Sortierung, Wood Origins, Activity, Archive/Sold
|
||||
- [x] Feature-Tests: `TeaserProductEditTest` (30 Tests) – Pre-Fill, Save, Status, Media-Sortierung, Produktnummern
|
||||
- [x] Tests: Beide Rollen (Händler + Hersteller) können BEIDE Produkttypen anlegen (Teaser + Standard)
|
||||
|
||||
**Tests Phase 2 gesamt: 109 Produkt-Tests + 48 Policy/Profil/Kuration-Tests, alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Kunden-Frontend & Local Feed – ✅ KERN ABGESCHLOSSEN (12.02.2026)
|
||||
**Geschätzter Aufwand:** 4-5 Tage | **Tatsächlich:** 1 Tag (12.02.2026)
|
||||
**Priorität:** HOCH – Kunden-Facing Funktionalität
|
||||
|
||||
#### 3.1 User-Origin Tracking – ✅ Implementiert
|
||||
- [x] Bei Registrierung: `origin` automatisch aus Domain setzen
|
||||
- Request von `style2own.test` → `origin = 'style2own'`
|
||||
- Request von `stileigentum.test` → `origin = 'stileigentum'`
|
||||
- [x] `CreateNewUser` Action (Fortify) erweitern um `origin` + `hub_id`
|
||||
- [ ] Hub-Auswahl bei Registrierung (oder automatisch via Invite-Code des Maklers) – ausstehend
|
||||
|
||||
#### 3.2 Kunden-Dashboard ("Mein Zuhause") – ✅ Kern implementiert
|
||||
- [x] `topOffers` im Dashboard: echte Produkte aus dem Hub des Kunden laden
|
||||
- `Product::query()->where('status', Active)->where('is_curated', true)->...->take(3)`
|
||||
- Fallback auf Dummy-Daten wenn noch keine Produkte vorhanden
|
||||
- [ ] Eigene Dashboard-Seite für Customer-Rolle – ausstehend
|
||||
- [ ] Theme-Steuerung basierend auf `origin` (`style2own` / `stileigentum`) – ausstehend
|
||||
|
||||
#### 3.3 Local Feed (Marktplatz-View) – ✅ Implementiert
|
||||
- [x] `products/index` mit echter DB-Abfrage und Rollen-Filterung:
|
||||
- **Admin**: sieht alle Produkte
|
||||
- **Customer**: nur `active + is_curated + is_available` aus eigenem Hub
|
||||
- **Retailer/Partner**: nur eigene Produkte
|
||||
- [x] Suchfilter nach Name, Kategorie-Filter, Paginierung (20 Einträge)
|
||||
- [ ] Produkt-Detailseite – ausstehend
|
||||
- [ ] Filteroptionen: Preisbereich, Verfügbarkeit – ausstehend
|
||||
|
||||
#### 3.4 Partner-Profilseite (öffentlich) – ✅ Implementiert
|
||||
- [x] `partner/profile.blade.php`: Story, Öffnungszeiten, Produkte (max. 6), Spezialisierungen, Adresse
|
||||
- [x] Route: `partner/{partnerId}/profile` → `partner.profile`
|
||||
- [ ] Team-Fotos / Showroom-Galerie – ausstehend (abhängig von Media-Upload)
|
||||
|
||||
#### 3.5 Tests Phase 3 – ✅ Abgeschlossen
|
||||
- [x] `CreateNewUserOriginTest` (6 Tests) – Origin-Tracking, hub_id
|
||||
- [x] `LocalFeedTest` (5 Tests) – Hub-Filterung, Kuration, Rollen, Suche, Kategorie
|
||||
- [x] `PartnerProfilePageTest` (6 Tests) – Profilseite, Story, Produkte, Auth
|
||||
|
||||
**Tests Phase 3 gesamt: 17 Tests, alle bestanden ✅**
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Ticket-System & QR-Generierung
|
||||
**Geschätzter Aufwand:** 3-4 Tage
|
||||
**Priorität:** HOCH – Kern der Monetarisierung
|
||||
|
||||
#### 4.1 Datenbank: Tickets
|
||||
```
|
||||
Migration: create_tickets_table
|
||||
```
|
||||
- [ ] `id`, `uuid` (public identifier)
|
||||
- [ ] `user_id` (FK → users, Kunde)
|
||||
- [ ] `merchant_partner_id` (FK → partners, Händler)
|
||||
- [ ] `product_id` (nullable FK → products)
|
||||
- [ ] `hub_id` (FK → hubs)
|
||||
- [ ] `code` (unique string, z.B. "TK-2026-XXXXX")
|
||||
- [ ] `qr_code_path` (nullable string, Pfad zur generierten QR-Datei)
|
||||
- [ ] `status` (string: `active` | `redeemed` | `expired`)
|
||||
- [ ] `discount_type` (string: `percentage` | `fixed`)
|
||||
- [ ] `discount_value` (decimal)
|
||||
- [ ] `valid_until` (datetime)
|
||||
- [ ] `redeemed_at` (nullable datetime)
|
||||
- [ ] `timestamps`
|
||||
|
||||
#### 4.2 Ticket Model & Enum
|
||||
- [ ] `App\Models\Ticket` mit Relationships
|
||||
- [ ] `App\Enums\TicketStatus` – `Active`, `Redeemed`, `Expired`
|
||||
- [ ] `TicketFactory` erstellen
|
||||
- [ ] `TicketPolicy` erstellen
|
||||
|
||||
#### 4.3 Ticket-Generierung
|
||||
- [ ] Composer-Paket für QR-Code Generierung (`simplesoftwareio/simple-qrcode` oder ähnlich) – **Genehmigung einholen**
|
||||
- [ ] Service: `App\Services\TicketService`
|
||||
- `generateTicket(User $user, Partner $merchant, ?Product $product): Ticket`
|
||||
- `generateQrCode(Ticket $ticket): string` (gibt Pfad zurück)
|
||||
- `validateTicket(string $code): ?Ticket`
|
||||
- `redeemTicket(Ticket $ticket): void`
|
||||
- [ ] Livewire-Komponente: "Ticket ziehen" Button auf Produktdetailseite
|
||||
- [ ] Mail/PDF-Generierung mit QR-Code + Händler-Info
|
||||
|
||||
#### 4.4 Händler: Ticket-Einlösung
|
||||
- [ ] Händler-Dashboard: "Eingelöste Tickets" Übersicht
|
||||
- [ ] QR-Code Scanner (Optional, Phase 5)
|
||||
- [ ] Manuelle Code-Eingabe zum Einlösen
|
||||
|
||||
#### 4.5 Tests Phase 4
|
||||
- [ ] Unit-Tests für TicketService
|
||||
- [ ] Feature-Tests für Ticket-Generierung
|
||||
- [ ] Feature-Tests für Ticket-Einlösung
|
||||
- [ ] Tests für QR-Code Generierung
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Transaction-Engine & Clearing
|
||||
**Geschätzter Aufwand:** 5-6 Tage
|
||||
**Priorität:** MITTEL – Aufbauend auf Ticket-System
|
||||
|
||||
#### 5.1 Datenbank: Transaktionen
|
||||
```
|
||||
Migration: create_transactions_table
|
||||
```
|
||||
- [ ] `id`, `uuid`
|
||||
- [ ] `ticket_id` (FK → tickets)
|
||||
- [ ] `user_id` (FK → users, Kunde)
|
||||
- [ ] `merchant_partner_id` (FK → partners, Händler)
|
||||
- [ ] `broker_partner_id` (nullable FK → partners, Makler)
|
||||
- [ ] `amount` (integer, in Cents)
|
||||
- [ ] `receipt_image_path` (nullable string, hochgeladener Beleg)
|
||||
- [ ] `receipt_amount` (nullable integer, vom Kunden angegebener Betrag in Cents)
|
||||
- [ ] `status` (string: State Machine)
|
||||
- [ ] `merchant_confirmed_at` (nullable datetime)
|
||||
- [ ] `merchant_confirmed_amount` (nullable integer, in Cents)
|
||||
- [ ] `invoice_generated_at` (nullable datetime)
|
||||
- [ ] `paid_at` (nullable datetime)
|
||||
- [ ] `distributed_at` (nullable datetime)
|
||||
- [ ] `notes` (nullable text)
|
||||
- [ ] `timestamps`
|
||||
|
||||
#### 5.2 Transaction State Machine
|
||||
```
|
||||
Enum: App\Enums\TransactionStatus
|
||||
```
|
||||
- [ ] `PendingReceipt` – Ticket eingelöst, Kunde muss Beleg hochladen
|
||||
- [ ] `PendingMerchant` – Beleg hochgeladen, Händler muss bestätigen
|
||||
- [ ] `Confirmed` – Händler hat bestätigt → Rechnung an Händler generieren
|
||||
- [ ] `Invoiced` – Rechnung erstellt → Zahlung ausstehend
|
||||
- [ ] `Paid` – Händler hat Provision an B2In überwiesen
|
||||
- [ ] `Distributed` – Provisionen an Makler/Kunde ausgeschüttet
|
||||
- [ ] `Rejected` – Händler hat abgelehnt
|
||||
- [ ] `Disputed` – Streitfall
|
||||
|
||||
#### 5.3 Beleg-Upload (Kunden-View)
|
||||
- [ ] Livewire-Komponente: Beleg-Upload Formular
|
||||
- Foto-Upload des Kaufbelegs
|
||||
- Betrag-Eingabe
|
||||
- Verknüpfung mit Ticket
|
||||
- [ ] Validierung: Ticket muss `redeemed` sein
|
||||
- [ ] Mail-Notification an Händler: "Neuer Beleg zur Bestätigung"
|
||||
|
||||
#### 5.4 Händler-Bestätigung
|
||||
- [ ] Händler-Dashboard: "Offene Umsatz-Bestätigungen"
|
||||
- [ ] Beleg anzeigen mit Bestätigungs-/Ablehnungs-Buttons
|
||||
- [ ] Händler kann Betrag korrigieren (falls Kunde falschen Betrag eingegeben hat)
|
||||
- [ ] Bei Bestätigung: Event `TransactionConfirmed` feuern
|
||||
|
||||
#### 5.5 Tests Phase 5
|
||||
- [ ] Unit-Tests für State Machine Übergänge
|
||||
- [ ] Feature-Tests für Beleg-Upload
|
||||
- [ ] Feature-Tests für Händler-Bestätigung
|
||||
- [ ] Tests für Events und Notifications
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Wallet, Cashback & Provisions-System
|
||||
**Geschätzter Aufwand:** 4-5 Tage
|
||||
**Priorität:** MITTEL – Monetarisierung
|
||||
|
||||
#### 6.1 Datenbank: Wallets & Ledger
|
||||
```
|
||||
Migration: create_wallets_table
|
||||
```
|
||||
- [ ] `id`
|
||||
- [ ] `partner_id` (nullable FK → partners)
|
||||
- [ ] `user_id` (nullable FK → users)
|
||||
- [ ] `balance` (integer, in Cents)
|
||||
- [ ] `type` (string: `cashback` | `commission` | `platform`)
|
||||
- [ ] `timestamps`
|
||||
|
||||
```
|
||||
Migration: create_ledger_entries_table
|
||||
```
|
||||
- [ ] `id`, `uuid`
|
||||
- [ ] `wallet_id` (FK → wallets)
|
||||
- [ ] `transaction_id` (nullable FK → transactions)
|
||||
- [ ] `type` (string: `credit` | `debit`)
|
||||
- [ ] `amount` (integer, in Cents, immer positiv)
|
||||
- [ ] `balance_after` (integer, in Cents)
|
||||
- [ ] `description` (string)
|
||||
- [ ] `timestamps`
|
||||
|
||||
#### 6.2 Provisions-Berechnung
|
||||
- [ ] Service: `App\Services\CommissionService`
|
||||
- `calculateSplit(Transaction $transaction): CommissionSplit`
|
||||
- Berechnet: Makler-Anteil, Kunden-Cashback, B2In-Marge
|
||||
- Nutzt `partner.provision_rate_percentage` und `partner.provision_fixed_amount`
|
||||
- [ ] Event Listener für `TransactionPaid`:
|
||||
- Erstellt Ledger-Einträge
|
||||
- Aktualisiert Wallet-Balances
|
||||
- Setzt Transaction-Status auf `distributed`
|
||||
|
||||
#### 6.3 Wallet-Views
|
||||
- [ ] Kunden-Dashboard: "Mein Cashback" (Guthaben, Historie)
|
||||
- [ ] Makler-Dashboard: "Meine Provisionen" (Guthaben, Historie)
|
||||
- [ ] Admin: Wallet-Übersicht aller Partner und Kunden
|
||||
|
||||
#### 6.4 Tests Phase 6
|
||||
- [ ] Unit-Tests für CommissionService (Splits korrekt berechnet)
|
||||
- [ ] Feature-Tests für Wallet-Operationen
|
||||
- [ ] Feature-Tests für Event-basierte Distribution
|
||||
- [ ] Edge Cases: Rundung, Null-Beträge, fehlende Provisionsregeln
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: Frontend-Polish & Domains
|
||||
**Geschätzter Aufwand:** 3-4 Tage
|
||||
**Priorität:** MITTEL
|
||||
|
||||
#### 7.1 Style2Own vs. StilEigentum Feinschliff
|
||||
- [ ] CSS-Variablen-Sets für beide Brands finalisieren
|
||||
- [ ] Wording-Datenbank: "Du" vs. "Sie" Ansprache in allen Texten
|
||||
- [ ] Landingpage-Anpassungen je Brand
|
||||
|
||||
#### 7.2 "Local for Local" Branding
|
||||
- [ ] Hub-spezifisches Branding (z.B. "Local for Local OWL")
|
||||
- [ ] Hub-Landingpages mit regionalen Inhalten
|
||||
- [ ] Domain-Routing für `localforlocal.de` (wenn verfügbar)
|
||||
|
||||
#### 7.3 Responsive & Mobile
|
||||
- [ ] Händler-Upload Formular mobil-optimiert ("Handy-optimiert" laut Konzept)
|
||||
- [ ] Ticket-Anzeige mobil-optimiert (QR-Code gut scanbar)
|
||||
- [ ] Kunden-Dashboard responsive
|
||||
|
||||
#### 7.4 Tests Phase 7
|
||||
- [ ] Browser-Tests (Laravel Dusk) für Multi-Domain
|
||||
- [ ] Responsive Tests
|
||||
|
||||
---
|
||||
|
||||
## 3. Beantwortete Fragen & Entscheidungen
|
||||
|
||||
### Architektur-Entscheidungen
|
||||
|
||||
**Frage 1: Kunden-Partner-Beziehung** ✅
|
||||
> **Entscheidung:** Beibehalten. Jeder Kunde behält seinen Partner-Eintrag. Zusätzlich `hub_id` und `origin` direkt auf User für schnelle Queries.
|
||||
|
||||
**Frage 2: Hub-Zuordnung bei Produkten** ✅
|
||||
> **Entscheidung:** Direktes `hub_id` Feld auf Products. Hersteller können Produkte in mehreren Hubs anbieten.
|
||||
|
||||
**Frage 3: QR-Code Paket** ✅
|
||||
> **Entscheidung:** `chillerlan/php-qrcode`
|
||||
|
||||
**Frage 4: PDF-Generierung für Tickets** ✅
|
||||
> **Entscheidung:** `spatie/laravel-pdf`
|
||||
|
||||
**Frage 5: Makler-Invite und Hub-Zuweisung** ✅
|
||||
> **Entscheidung:** Automatisch vom Makler übernehmen. Admin kann manuell ändern.
|
||||
|
||||
**Frage 6: Domain Local for Local** ✅
|
||||
> **Entscheidung:** `local4local.online` (Produktion) / `local4local.test` (Entwicklung). Muss in `config/domains.php` ergänzt werden.
|
||||
|
||||
**Frage 11: Produkttypen – Eine oder zwei Tabellen?** ✅
|
||||
> **Entscheidung:** EINE `products` Tabelle, ZWEI Eingabemasken. Das Feld `product_type` (`local_stock` | `smart_order`) steuert welche Maske und Validierung greift. Siehe Phase 2.0 für Details.
|
||||
> - Maske 1 (Teaser/Local Stock): Vereinfacht, handy-optimiert, für Händler
|
||||
> - Maske 2 (Konfiguration/Smart Order): Komplex mit 6 Tabs, für Hersteller
|
||||
|
||||
### Business-Logic Entscheidungen
|
||||
|
||||
**Frage 7: Provisions-Split** ✅
|
||||
> **Entscheidung:** Individuell pro Partner. Admin-Settings-Seite mit Feldern für:
|
||||
> - Makler-Provision (%)
|
||||
> - Kunden-Cashback (%)
|
||||
> - B2In-Marge (Rest)
|
||||
> Felder werden pro Partner im Admin-Backend konfiguriert.
|
||||
|
||||
**Frage 8: Ticket-Gültigkeit** ✅
|
||||
> **Entscheidung:** Default 30 Tage. Konfigurierbar über Admin-Settings-Seite.
|
||||
|
||||
**Frage 9: Beleg-Upload Deadline** ✅
|
||||
> **Entscheidung:** Default 30 Tage. Konfigurierbar über Admin-Settings-Seite.
|
||||
|
||||
**Frage 10: Ticket-Begrenzung pro Kunde** ✅
|
||||
> **Entscheidung:** Produkte sind immer an einen Händler gebunden. Ein Kunde kann Tickets bei verschiedenen Händlern für ähnliche Produkte ziehen. Begrenzung über Admin-Settings:
|
||||
> - Max. Tickets pro Händler (z.B. 3)
|
||||
> - Max. Händler pro Zeitraum (z.B. 4)
|
||||
> - Konfigurierbar durch Admin
|
||||
|
||||
### Benötigte Admin-Settings-Seite (Neue Anforderung)
|
||||
Aus den Antworten ergibt sich die Notwendigkeit einer zentralen **Admin-Settings-Seite** für:
|
||||
- Ticket-Gültigkeit (Tage)
|
||||
- Beleg-Upload Deadline (Tage)
|
||||
- Max. Tickets pro Händler pro Kunde
|
||||
- Max. Händler pro Kunde pro Zeitraum
|
||||
- Standard-Provisions-Split (Default-Werte für neue Partner)
|
||||
|
||||
> **Umsetzung:** `settings` Tabelle (key-value) + Admin-Settings-View in Phase 4/5. Alternativ: `config/marketplace.php` mit `.env`-Variablen für nicht-dynamische Werte.
|
||||
---
|
||||
|
||||
## 4. Technische Richtlinien für die Entwicklung
|
||||
|
||||
### Architektur-Prinzipien
|
||||
- **Eloquent-First:** Keine `DB::` Facades, immer `Model::query()`
|
||||
- **Form Requests:** Jede Validierung in eigene Request-Klassen
|
||||
- **Policies:** Jede Autorisierung über Laravel Policies
|
||||
- **Events/Listeners:** Für alle Seiteneffekte (Mail, Wallet-Buchung, Notifications)
|
||||
- **Services:** Business-Logik in Service-Klassen (`TicketService`, `CommissionService`)
|
||||
- **Enums:** Alle Status-Werte als PHP 8.1+ Enums
|
||||
|
||||
### Namenskonventionen
|
||||
- Models: Singular, PascalCase (`Ticket`, `LedgerEntry`)
|
||||
- Tabellen: Plural, snake_case (`tickets`, `ledger_entries`)
|
||||
- Enums: PascalCase Keys (`LocalStock`, `SmartOrder`)
|
||||
- Services: `{Domain}Service` (`TicketService`, `CommissionService`)
|
||||
- Policies: `{Model}Policy` (`TicketPolicy`, `TransactionPolicy`)
|
||||
- Form Requests: `{Action}{Model}Request` (`StoreProductRequest`, `UpdatePartnerProfileRequest`)
|
||||
- Events: Past Tense (`TransactionConfirmed`, `TicketRedeemed`)
|
||||
|
||||
### Testing-Strategie
|
||||
- Jede Phase muss vollständig getestet sein bevor die nächste beginnt
|
||||
- Feature-Tests für alle Endpoints und Livewire-Komponenten
|
||||
- Unit-Tests für Services und komplexe Business-Logik
|
||||
- Pest als Test-Framework, Factories für Testdaten
|
||||
- Mindestens: Happy Path + Validation + Authorization pro Feature
|
||||
|
||||
### Reihenfolge der Entwicklung (innerhalb jeder Phase)
|
||||
1. Migration(s) erstellen und ausführen
|
||||
2. Model(s) mit Relationships, Casts, Scopes
|
||||
3. Enum(s) falls nötig
|
||||
4. Factory und Seeder
|
||||
5. Form Request(s)
|
||||
6. Policy
|
||||
7. Service-Klasse (falls komplexe Logik)
|
||||
8. Livewire/Volt Komponente
|
||||
9. Tests schreiben und ausführen
|
||||
10. `pint --dirty` für Code-Formatierung
|
||||
|
||||
---
|
||||
|
||||
## 5. Abhängigkeiten zwischen Phasen
|
||||
|
||||
```
|
||||
Phase 1 (Core) ──────────┬──────────> Phase 2 (Produkte)
|
||||
│ │
|
||||
│ ▼
|
||||
└──────────> Phase 3 (Frontend) ──> Phase 7 (Polish)
|
||||
│
|
||||
▼
|
||||
Phase 4 (Tickets)
|
||||
│
|
||||
▼
|
||||
Phase 5 (Transactions)
|
||||
│
|
||||
▼
|
||||
Phase 6 (Wallet/Cashback)
|
||||
```
|
||||
|
||||
- **Phase 1** ist Voraussetzung für alles
|
||||
- **Phase 2 und 3** können teilweise parallel laufen
|
||||
- **Phase 4** setzt Phase 3 voraus (Produktdetailseite muss existieren)
|
||||
- **Phase 5** setzt Phase 4 voraus (Tickets müssen existieren)
|
||||
- **Phase 6** setzt Phase 5 voraus (Transaktionen müssen existieren)
|
||||
- **Phase 7** kann teilweise parallel zu Phase 4-6 laufen
|
||||
|
||||
---
|
||||
|
||||
## 6. Neue Permissions (zu ergänzen)
|
||||
|
||||
Folgende Permissions müssen im RoleSeeder ergänzt werden:
|
||||
|
||||
| Permission | Beschreibung | Rollen |
|
||||
|-----------|-------------|--------|
|
||||
| `curate products` | Produkte freigeben/ablehnen | Admin, Super-Admin |
|
||||
| `view tickets` | Eigene Tickets sehen | Customer |
|
||||
| `create tickets` | Ticket generieren | Customer |
|
||||
| `redeem tickets` | Ticket einlösen | Retailer |
|
||||
| `view transactions` | Transaktionen sehen | Customer, Retailer, Estate-Agent, Admin |
|
||||
| `confirm transactions` | Umsatz bestätigen | Retailer |
|
||||
| `upload receipts` | Beleg hochladen | Customer |
|
||||
| `view wallet` | Wallet-Guthaben sehen | Customer, Estate-Agent, Retailer |
|
||||
| `manage wallets` | Wallets verwalten | Admin |
|
||||
| `view commissions` | Provisionen einsehen | Estate-Agent, Admin |
|
||||
| `manage setup packages` | Setup-Pakete verwalten | Admin |
|
||||
| `book setup package` | Setup-Paket buchen | Retailer |
|
||||
|
||||
---
|
||||
|
||||
*Dieses Dokument dient als lebende Referenz für die Entwicklung. Bei Änderungen am Konzept muss dieser Plan entsprechend aktualisiert werden.*
|
||||
ochladen | Customer |
|
||||
| `view wallet` | Wallet-Guthaben sehen | Customer, Estate-Agent, Retailer |
|
||||
| `manage wallets` | Wallets verwalten | Admin |
|
||||
| `view commissions` | Provisionen einsehen | Estate-Agent, Admin |
|
||||
| `manage setup packages` | Setup-Pakete verwalten | Admin |
|
||||
| `book setup package` | Setup-Paket buchen | Retailer |
|
||||
|
||||
---
|
||||
|
||||
*Dieses Dokument dient als lebende Referenz für die Entwicklung. Bei Änderungen am Konzept muss dieser Plan entsprechend aktualisiert werden.*
|
||||
Loading…
Add table
Add a link
Reference in a new issue