b2in/dev/12-01-2026/entwicklungsplan.md
2026-02-20 17:57:50 +01:00

54 KiB
Raw Blame History

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.phpform-standard.blade.php (Standard-Produkte)
  • create-teaser.blade.php + edit-teaser.blade.phpform-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: mainImagemainImages 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:
    • AttributehasMany(AttributeValue)
    • AttributeValuebelongsTo(Attribute)
    • Category → self-referencing parent/children, belongsToMany(Product)
    • TagbelongsToMany(Product)
    • ProductVariantbelongsTo(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(): boolLocalStock = true, SmartOrder = false
  • allowedPriceTypes(): arrayLocalStock = [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

  • Partner-Profil Formular erweitern:
    • Story-Text Editor (<flux:textarea>)
    • Ö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
  • Policy: PartnerPolicy (viewAny, view, update, delete, curateProducts)
  • Admin Partner-Übersicht: admin/partners/index.blade.php (Suche, Filter, Tabelle)

2.2 Produkt-CRUD: Maske 1 Teaser / Local Stock (Händler) Implementiert

  • Volt-Komponente: livewire/products/form-teaser.blade.php (merged Create + Edit)
    • Einzelseiten-Formular (KEIN Tab-Layout)
    • Bild-Upload via WithFileUploads + Drag & Drop Bildsortierung
    • Felder: Foto, Titel, Kurzbeschreibung, Preistyp (nur FromPrice/OnRequest), Preistext, Kategorie, Status, Produktnummer
    • Produktnummern: Partner-Produktnummer (auto-generiert) + B2in-Artikelnummer (auto bei Save)
    • Automatisch: product_type = 'local_stock', hub_id vom Händler-Hub, partner_id
    • Preistyp-Validierung via ProductType::LocalStock->allowedPriceTypes() (kein Festpreis)
    • Freigabe-Workflow: UI "aktiv" → DB pending (korrekt zur Freigabe, nicht direkt Active)
  • Produkt-Liste: Zwei Buttons ("Neues Teaser-Produkt" + "Neues Standard-Produkt") für alle Partner-Rollen
  • Produkt-Liste: Produkttyp-Filter (Teaser / Standard / Alle)
  • Produkt-Liste: Vorschaubild (erstes Bild nach order_column) vor Produktname
  • Produkt bearbeiten: Pre-Fill, Status-Handling, Media-Verwaltung, Activity-Log, Toast-Notification
  • Bildsortierung: Drag & Drop, erstes Bild = Standardbild, order_column in DB
  • Produkt archivieren / als "Verkauft" markieren in Edit-Formular und Produktliste (Dropdown-Menue)
  • Form Request: StoreTeaserProductRequest inline Validierung verwendet
  • Policy: ProductPolicy (viewAny, view, create, update, delete, curate)

2.3 Produkt-CRUD: Maske 2 Standard / Smart Order Implementiert (13.02.2026)

  • Volt-Komponente: livewire/products/form-standard.blade.php (merged Create + Edit)
    • 8-Tab-Layout: Basis, Bilder, Maße & Material, Verpackung & Versand, Kommerziell, Service & Garantie, Nachhaltigkeit, Zuordnung
    • Erstellt Product + Master-Variante (ProductVariant) + ProductLogistics + Media
    • Preis-Typ Logik: Festpreis / Ab-Preis / Preis auf Anfrage (alle 3 für SmartOrder erlaubt)
    • Automatisch: product_type = 'smart_order', Hub-Fallback auf Partner-Hub
    • File-Uploads: Mehrere Bilder (max 10, JPG/PNG, max 10 MB) + Drag & Drop Bildsortierung
    • Preise in EUR eingeben → automatische Umrechnung in Cents bei Speicherung
    • Alle optionalen Felder: MPN, EAN/GTIN, UVP, EK, Lagerstatus, Lieferzeit, SEO-Metadaten, Maße, Verpackung
    • Validierung inline (kein separater Form Request konsistent mit Teaser-Formular)
    • Authorization: ProductPolicy + Rollenprüfung (Retailer, Manufacturer, Admin, Super-Admin)
    • Bei Edit: Toast-Notification, aktiver Tab bleibt erhalten, kein Redirect
  • Migration: tax_rate_id auf product_variants nullable gemacht (war NOT NULL ohne Default)

2.4 Admin: Produkt-Kuration (Approval Queue) Vollstaendig implementiert

  • Admin-View: admin/products/index.blade.php Tabelle mit Filtern (Status, Typ, Haendler, Kategorie, Suche)
  • Statistik-Karten: Gesamt, Zur Freigabe, In Korrektur, Freigegeben (klickbar als Schnellfilter)
  • Approve Action: is_curated=true, curated_at, curated_by + Flux::toast()
  • Correction Action: Inline-Formular mit Pflicht-Textfeld → curation_notes
  • Reject Action: Inline-Formular mit Pflicht-Ablehnungsgrund → curation_notes + status=Archived
  • Autorisierung via ProductPolicy::curate (curate products Permission)
  • Admin kann alle Produkte bearbeiten (gleiche Edit-Formulare wie Haendler)
  • Admin kann Produkte archivieren / als verkauft markieren (Dropdown-Menue)
  • Erstellungsdatum in Tabelle sichtbar
  • 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

  • Feature-Tests: PartnerPolicyTest (10 Tests) viewAny, view, update, curateProducts
  • Feature-Tests: ProductPolicyTest (14 Tests) alle Policy-Methoden
  • Feature-Tests: PartnerProfileUpdateTest (8 Tests) Profil-Update, Öffnungszeiten, Validierung
  • Feature-Tests: TeaserProductCreateTest (18 Tests) Happy Path, Preistyp-Validierung, Autorisierung, Status-Logik, Produktnummern
  • Feature-Tests: ProductCurationTest (23 Tests) Approve, Reject mit Pflicht-Textfeld, Correction, Archive/Sold, Filter, Kuration-Notes-Anzeige im Edit
  • Feature-Tests: StandardProductCreateTest (39 Tests) Zugriff, Happy Path, alle Tabs, Validierung, Preistypen, Produktnummern, Marken
  • Feature-Tests: ProductEditTest (27 Tests) Pre-Fill, Save, Status, Validierung, Media-Sortierung, Wood Origins, Activity, Archive/Sold
  • Feature-Tests: TeaserProductEditTest (30 Tests) Pre-Fill, Save, Status, Media-Sortierung, Produktnummern
  • 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

  • Bei Registrierung: origin automatisch aus Domain setzen
    • Request von style2own.testorigin = 'style2own'
    • Request von stileigentum.testorigin = 'stileigentum'
  • 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

  • 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

  • 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
  • Suchfilter nach Name, Kategorie-Filter, Paginierung (20 Einträge)
  • Produkt-Detailseite ausstehend
  • Filteroptionen: Preisbereich, Verfügbarkeit ausstehend

3.4 Partner-Profilseite (öffentlich) Implementiert

  • partner/profile.blade.php: Story, Öffnungszeiten, Produkte (max. 6), Spezialisierungen, Adresse
  • Route: partner/{partnerId}/profilepartner.profile
  • Team-Fotos / Showroom-Galerie ausstehend (abhängig von Media-Upload)

3.5 Tests Phase 3 Abgeschlossen

  • CreateNewUserOriginTest (6 Tests) Origin-Tracking, hub_id
  • LocalFeedTest (5 Tests) Hub-Filterung, Kuration, Rollen, Suche, Kategorie
  • 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.