forgetCachedPermissions(); Role::create(['name' => 'Retailer']); Role::create(['name' => 'Manufacturer']); Role::create(['name' => 'Customer']); }); function makeRetailerWithHub(): array { $hub = Hub::factory()->create(); $partner = Partner::factory()->setupCompleted()->create(['hub_id' => $hub->id]); $user = User::factory()->create(['partner_id' => $partner->id]); $user->assignRole('Retailer'); return [$user, $partner, $hub]; } test('retailer can access teaser product creation page', function () { [$user] = makeRetailerWithHub(); $this->actingAs($user) ->get(route('products.create.teaser')) ->assertSuccessful(); }); test('manufacturer can access teaser product creation page', function () { $hub = Hub::factory()->create(); $partner = Partner::factory()->setupCompleted()->create(['hub_id' => $hub->id]); $user = User::factory()->create(['partner_id' => $partner->id]); $user->assignRole('Manufacturer'); $this->actingAs($user) ->get(route('products.create.teaser')) ->assertSuccessful(); }); test('customer cannot access teaser product creation page', function () { $customer = User::factory()->create(); $customer->assignRole('Customer'); $this->actingAs($customer) ->get(route('products.create.teaser')) ->assertForbidden(); }); test('retailer can create teaser product with from_price', function () { Storage::fake('public'); [$user, $partner, $hub] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Sideboard Eiche') ->set('descriptionShort', 'Schönes Massivholz-Sideboard aus regionaler Fertigung.') ->set('priceType', 'from_price') ->set('priceDisplayText', 'Ab 1.800 €') ->set('categoryId', $category->id) ->set('status', 'active') ->set('mainImages', [UploadedFile::fake()->image('product.jpg', 800, 600)]) ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Sideboard Eiche')->first(); expect($product)->not->toBeNull(); expect($product->product_type)->toBe(ProductType::LocalStock); expect($product->price_type)->toBe(PriceType::FromPrice); expect($product->price_display_text)->toBe('Ab 1.800 €'); expect($product->partner_id)->toBe($partner->id); expect($product->hub_id)->toBe($hub->id); expect($product->is_curated)->toBeFalse(); }); test('retailer can create teaser product with on_request price', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Küche Massivholz') ->set('descriptionShort', 'Individuelle Küche auf Maß.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('status', 'draft') ->set('mainImages', [UploadedFile::fake()->image('kitchen.jpg', 800, 600)]) ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Küche Massivholz')->first(); expect($product->price_type)->toBe(PriceType::OnRequest); }); test('teaser product rejects fixed price type', function () { [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Produkt mit Festpreis') ->set('descriptionShort', 'Beschreibung.') ->set('priceType', 'fixed') ->set('categoryId', $category->id) ->set('status', 'active') ->call('save') ->assertHasErrors(['priceType']); }); test('teaser product requires price display text when from_price', function () { [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Produkt') ->set('descriptionShort', 'Beschreibung.') ->set('priceType', 'from_price') ->set('priceDisplayText', '') ->set('categoryId', $category->id) ->set('status', 'active') ->call('save') ->assertHasErrors(['priceDisplayText']); }); test('teaser product requires name', function () { [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', '') ->set('descriptionShort', 'Beschreibung.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->call('save') ->assertHasErrors(['name' => 'required']); }); test('teaser product description short max 180 characters', function () { [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Test') ->set('descriptionShort', str_repeat('x', 181)) ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->call('save') ->assertHasErrors(['descriptionShort' => 'max']); }); test('teaser product requires category', function () { [$user] = makeRetailerWithHub(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Test Produkt') ->set('descriptionShort', 'Beschreibung.') ->set('priceType', 'on_request') ->set('categoryId', null) ->call('save') ->assertHasErrors(['categoryId' => 'required']); }); test('manufacturer can create teaser product', function () { Storage::fake('public'); $hub = Hub::factory()->create(); $partner = Partner::factory()->setupCompleted()->create(['hub_id' => $hub->id]); $user = User::factory()->create(['partner_id' => $partner->id]); $user->assignRole('Manufacturer'); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Hersteller Teaser-Produkt') ->set('descriptionShort', 'Ein Teaser-Produkt vom Hersteller.') ->set('priceType', 'from_price') ->set('priceDisplayText', 'Ab 3.200 €') ->set('categoryId', $category->id) ->set('status', 'active') ->set('mainImages', [UploadedFile::fake()->image('product.jpg', 800, 600)]) ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Hersteller Teaser-Produkt')->first(); expect($product)->not->toBeNull(); expect($product->product_type)->toBe(ProductType::LocalStock); expect($product->partner_id)->toBe($partner->id); expect($product->hub_id)->toBe($hub->id); }); test('created teaser product is automatically not curated', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Nicht kuratiertes Produkt') ->set('descriptionShort', 'Test.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('status', 'active') ->set('mainImages', [UploadedFile::fake()->image('test.jpg', 800, 600)]) ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Nicht kuratiertes Produkt')->first(); expect($product->is_curated)->toBeFalse(); expect($product->product_type)->toBe(ProductType::LocalStock); }); test('submitting teaser product as active sets status to pending', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Pending Teaser') ->set('descriptionShort', 'Test zur Freigabe.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('status', 'active') ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Pending Teaser')->first(); expect($product->status)->toBe(ProductStatus::Pending); }); test('saving teaser product as draft keeps status as draft', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Draft Teaser') ->set('descriptionShort', 'Test Entwurf.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('status', 'draft') ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Draft Teaser')->first(); expect($product->status)->toBe(ProductStatus::Draft); }); test('teaser product generates b2in article number on create', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Teaser mit Nummer') ->set('descriptionShort', 'Test Artikelnummer.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('status', 'active') ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Teaser mit Nummer')->first(); expect($product->b2in_article_number)->toStartWith('B2IN-'); expect($product->b2in_article_number)->toBe('B2IN-000001'); }); test('teaser product saves partner product number', function () { Storage::fake('public'); [$user] = makeRetailerWithHub(); $category = Category::factory()->create(); $this->actingAs($user); Livewire\Volt\Volt::test('products.form-teaser') ->set('name', 'Teaser mit Partnernummer') ->set('descriptionShort', 'Test Partnernummer.') ->set('priceType', 'on_request') ->set('categoryId', $category->id) ->set('partnerProductNumber', 'MY-CUSTOM-001') ->set('status', 'active') ->call('save') ->assertHasNoErrors(); $product = Product::where('name', 'Teaser mit Partnernummer')->first(); expect($product->partner_product_number)->toBe('MY-CUSTOM-001'); }); test('teaser product auto-generates partner product number on mount', function () { [$user, $partner] = makeRetailerWithHub(); $this->actingAs($user); $expectedNumber = sprintf('P%03d-%04d', $partner->id, 1); Livewire\Volt\Volt::test('products.form-teaser') ->assertSet('partnerProductNumber', $expectedNumber); });