gruene-seele/tests/Feature/WhitelabelAssignmentTest.php
Kevin Adametz d687a6c569 White Label: Sofortpaket WL-00 und Bestandsprüfung WL-01
- Etikettgröße im Lieferschein korrekt parsen ("40#3,5" ergab 3 statt 3,5 cm)
- Produktkopie optional inkl. Kundenzuordnungen und Logo-Dateien (zweiter Kopier-Link)
- whitelabel:transfer-assignments: gezielte, wiederholbare Übertragung ohne Dubletten
- whitelabel:migrate-colors: Zuordnungen anhand der Bildfarbe auf die Farbprodukte übertragen (Dry-Run ohne --execute)
- whitelabel:report: rein lesende Bestandsprüfung (fehlende Zuordnungen, Farbwidersprüche, Dateien, Dubletten)
- Migration für attributes.value (Schema-Drift, auf Live No-op)
- Analyse/Entwicklungsplan um Gegenprüfung, systemischen Befund und neue Paketreihenfolge ergänzt

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:05:42 +00:00

183 lines
7.3 KiB
PHP

<?php
use App\Models\Attribute;
use App\Models\AttributeType;
use App\Models\Product;
use App\Models\ProductImage;
use App\Models\UserWhitelabelProduct;
use App\Repositories\InvoiceRepository;
use App\Repositories\ProductRepository;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
function wlMakeUser(): User
{
return User::query()->create([
'email' => uniqid('wl_', true).'@test.example',
'password' => bcrypt('password'),
]);
}
function wlMakeProduct(string $name): Product
{
return Product::query()->create([
'name' => $name,
'title' => $name,
'active' => true,
'is_set' => false,
'whitelabel' => true,
]);
}
function wlMakeVariant(string $name, string $value): Attribute
{
$type = AttributeType::query()->firstOrCreate(['id' => 1], ['name' => 'Variante', 'slug' => 'variante']);
return Attribute::query()->create([
'attribute_type_id' => $type->id,
'name' => $name,
'value' => $value,
'slug' => uniqid('v_'),
'active' => true,
'pos' => 0,
]);
}
/** Kundenzuordnung mit einem Logo-Bild und Datei auf der Public-Disk anlegen. */
function wlMakeAssignment(User $user, Product $product, array $attributes): UserWhitelabelProduct
{
$assignment = UserWhitelabelProduct::create([
'user_id' => $user->id,
'product_id' => $product->id,
'active' => true,
]);
Storage::disk('public')->put('images/user_product/'.$assignment->id.'/logo.png', 'png-bytes');
ProductImage::create([
'user_wl_product_id' => $assignment->id,
'type' => 'uwllogo',
'filename' => 'logo.png',
'original_name' => 'logo.png',
'ext' => 'png',
'mine' => 'image/png',
'size' => 9,
'pos' => 0,
'active' => true,
'attributes' => $attributes,
]);
return $assignment->fresh();
}
test('kundenzuordnungen werden samt logo-datei auf das zielprodukt uebertragen und nicht doppelt angelegt', function () {
Storage::fake('public');
$user = wlMakeUser();
$source = wlMakeProduct('Creme 40 ml (Tiegel GOLD)');
$target = wlMakeProduct('Creme 40 ml (Tiegel SCHWARZ)');
wlMakeAssignment($user, $source, ['6']);
$repo = new ProductRepository($target);
$first = $repo->copyWhitelabelAssignments($source, $target);
$second = $repo->copyWhitelabelAssignments($source, $target);
expect($first['copied'])->toBe(1)->and($first['skipped'])->toBe(0)
->and($second['copied'])->toBe(0)->and($second['skipped'])->toBe(1);
$copies = UserWhitelabelProduct::query()->where('user_id', $user->id)->where('product_id', $target->id)->get();
expect($copies)->toHaveCount(1);
$image = $copies->first()->whitelabel_images->first();
expect($image)->not->toBeNull()
->and((array) $image->attributes)->toBe(['6'])
->and(Storage::disk('public')->exists('images/user_product/'.$copies->first()->id.'/'.$image->filename))->toBeTrue();
});
test('uebertragung kann auf einen partner eingeschraenkt werden', function () {
Storage::fake('public');
$a = wlMakeUser();
$b = wlMakeUser();
$source = wlMakeProduct('Creme (Tiegel GOLD)');
$target = wlMakeProduct('Creme (Tiegel WEISS)');
wlMakeAssignment($a, $source, ['5']);
wlMakeAssignment($b, $source, ['5']);
$result = (new ProductRepository($target))->copyWhitelabelAssignments($source, $target, $a->id);
expect($result['copied'])->toBe(1)
->and(UserWhitelabelProduct::query()->where('product_id', $target->id)->pluck('user_id')->all())->toBe([$a->id]);
});
test('produktkopie uebernimmt kundenzuordnungen nur auf ausdrueckliche anforderung', function () {
Storage::fake('public');
$user = wlMakeUser();
$source = wlMakeProduct('Creme (Tiegel GOLD)');
wlMakeAssignment($user, $source, ['6']);
$plain = (new ProductRepository(new Product))->copy($source);
$withAssignments = (new ProductRepository(new Product))->copy($source, true);
expect(UserWhitelabelProduct::query()->where('product_id', $plain->id)->count())->toBe(0)
->and(UserWhitelabelProduct::query()->where('product_id', $withAssignments->id)->count())->toBe(1);
});
test('artisan-befehl uebertraegt zuordnungen und meldet dry-run ohne aenderung', function () {
Storage::fake('public');
$user = wlMakeUser();
$source = wlMakeProduct('Creme (Tiegel GOLD)');
$target = wlMakeProduct('Creme (Tiegel SCHWARZ)');
wlMakeAssignment($user, $source, ['6']);
$this->artisan('whitelabel:transfer-assignments', ['source' => $source->id, 'target' => $target->id, '--dry-run' => true])
->expectsOutputToContain('Dry-run')
->assertSuccessful();
expect(UserWhitelabelProduct::query()->where('product_id', $target->id)->count())->toBe(0);
$this->artisan('whitelabel:transfer-assignments', ['source' => $source->id, 'target' => $target->id])
->expectsOutputToContain('Übernommen: 1')
->assertSuccessful();
expect(UserWhitelabelProduct::query()->where('product_id', $target->id)->count())->toBe(1);
});
test('farbmigration ordnet anhand der bildfarbe dem gleichfarbigen produkt zu', function () {
Storage::fake('public');
$schwarz = wlMakeVariant('Schwarz 40 ml', '40#3,5');
wlMakeVariant('Gold 40 ml', '40#3,5');
$user = wlMakeUser();
$gold = wlMakeProduct('Bio Creme 40 ml (Tiegel GOLD)');
$black = wlMakeProduct('Bio Creme 40 ml (Tiegel SCHWARZ)');
wlMakeProduct('Bio Creme 40 ml (Tiegel WEISS)');
wlMakeAssignment($user, $gold, [(string) $schwarz->id]);
$this->artisan('whitelabel:migrate-colors')->expectsOutputToContain('Dry-run')->assertSuccessful();
expect(UserWhitelabelProduct::query()->where('product_id', $black->id)->count())->toBe(0);
$this->artisan('whitelabel:migrate-colors', ['--execute' => true])->expectsOutputToContain('Übernommen: 1')->assertSuccessful();
expect(UserWhitelabelProduct::query()->where('product_id', $black->id)->where('user_id', $user->id)->count())->toBe(1)
->and(UserWhitelabelProduct::query()->where('product_id', $gold->id)->count())->toBe(1);
// wiederholt: keine Dublette
$this->artisan('whitelabel:migrate-colors', ['--execute' => true])->expectsOutputToContain('Übernommen: 0')->assertSuccessful();
expect(UserWhitelabelProduct::query()->where('product_id', $black->id)->count())->toBe(1);
});
test('bestandspruefung laeuft rein lesend durch', function () {
Storage::fake('public');
$user = wlMakeUser();
$gold = wlMakeProduct('Bio Creme 40 ml (Tiegel GOLD)');
$schwarz = wlMakeVariant('Schwarz 40 ml', '40#3,5');
wlMakeAssignment($user, $gold, [(string) $schwarz->id]);
$before = UserWhitelabelProduct::query()->count() + ProductImage::query()->count();
$this->artisan('whitelabel:report')->expectsOutputToContain('Farbwiderspruch')->assertSuccessful();
expect(UserWhitelabelProduct::query()->count() + ProductImage::query()->count())->toBe($before);
});
test('etikettgroesse mit komma wird korrekt geparst', function () {
expect(InvoiceRepository::parseVariantValue('40#3,5'))->toBe([40.0, 3.5])
->and(InvoiceRepository::parseVariantValue('15#2'))->toBe([15.0, 2.0])
->and(InvoiceRepository::parseVariantValue('30#5'))->toBe([30.0, 5.0]);
});