From 65431337131cc080e8ce7d1cdd2ebdc2ddc838f1 Mon Sep 17 00:00:00 2001 From: Thorsten Bus Date: Mon, 2 Mar 2026 21:22:30 +0100 Subject: [PATCH] feat(songs): auto-select default arrangement on song match --- app/Services/SongMatchingService.php | 29 ++++++- tests/Feature/SongMatchingTest.php | 120 +++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/app/Services/SongMatchingService.php b/app/Services/SongMatchingService.php index 8316084..9075897 100644 --- a/app/Services/SongMatchingService.php +++ b/app/Services/SongMatchingService.php @@ -31,8 +31,17 @@ public function autoMatch(ServiceSong $serviceSong): bool return false; } + // Find default arrangement: is_default → name='normal' → first + $defaultArrangement = $song->arrangements() + ->where('is_default', true) + ->first() ?? $song->arrangements() + ->where('name', 'normal') + ->first() ?? $song->arrangements() + ->first(); + $serviceSong->update([ 'song_id' => $song->id, + 'song_arrangement_id' => $defaultArrangement?->id, 'matched_at' => Carbon::now(), 'use_translation' => $song->has_translation, ]); @@ -43,14 +52,30 @@ public function autoMatch(ServiceSong $serviceSong): bool /** * Manually assign a song to a service song. * Overwrites any existing assignment. + * Only sets arrangement if currently null (preserves existing selection). */ public function manualAssign(ServiceSong $serviceSong, Song $song): void { - $serviceSong->update([ + $updateData = [ 'song_id' => $song->id, 'matched_at' => Carbon::now(), 'use_translation' => $song->has_translation, - ]); + ]; + + // Only set arrangement if currently null + if ($serviceSong->song_arrangement_id === null) { + // Find default arrangement: is_default → name='normal' → first + $defaultArrangement = $song->arrangements() + ->where('is_default', true) + ->first() ?? $song->arrangements() + ->where('name', 'normal') + ->first() ?? $song->arrangements() + ->first(); + + $updateData['song_arrangement_id'] = $defaultArrangement?->id; + } + + $serviceSong->update($updateData); } /** diff --git a/tests/Feature/SongMatchingTest.php b/tests/Feature/SongMatchingTest.php index b7d5389..901d7a7 100644 --- a/tests/Feature/SongMatchingTest.php +++ b/tests/Feature/SongMatchingTest.php @@ -4,6 +4,7 @@ use App\Models\Service; use App\Models\ServiceSong; use App\Models\Song; +use App\Models\SongArrangement; use App\Models\User; use App\Services\SongMatchingService; use Illuminate\Support\Facades\Mail; @@ -104,6 +105,80 @@ expect($serviceSong->song_id)->toBe($existingSong->id); }); +test('autoMatch setzt song_arrangement_id auf Standard-Arrangement', function () { + $song = Song::factory()->create(['ccli_id' => '7115744']); + $defaultArrangement = SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'normal', + 'is_default' => false, + ]); + + $serviceSong = ServiceSong::factory()->create([ + 'cts_ccli_id' => '7115744', + 'song_id' => null, + 'song_arrangement_id' => null, + ]); + + $service = app(SongMatchingService::class); + $result = $service->autoMatch($serviceSong); + + expect($result)->toBeTrue(); + $serviceSong->refresh(); + expect($serviceSong->song_arrangement_id)->toBe($defaultArrangement->id); +}); + +test('autoMatch bevorzugt is_default=true Arrangement', function () { + $song = Song::factory()->create(['ccli_id' => '7115744']); + $normalArrangement = SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'normal', + 'is_default' => false, + ]); + $defaultArrangement = SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'Standard', + 'is_default' => true, + ]); + + $serviceSong = ServiceSong::factory()->create([ + 'cts_ccli_id' => '7115744', + 'song_id' => null, + 'song_arrangement_id' => null, + ]); + + $service = app(SongMatchingService::class); + $service->autoMatch($serviceSong); + + $serviceSong->refresh(); + expect($serviceSong->song_arrangement_id)->toBe($defaultArrangement->id); +}); + +test('autoMatch nutzt erstes Arrangement wenn kein Standard vorhanden', function () { + $song = Song::factory()->create(['ccli_id' => '7115744']); + $firstArrangement = SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'Erste', + 'is_default' => false, + ]); + SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'Zweite', + 'is_default' => false, + ]); + + $serviceSong = ServiceSong::factory()->create([ + 'cts_ccli_id' => '7115744', + 'song_id' => null, + 'song_arrangement_id' => null, + ]); + + $service = app(SongMatchingService::class); + $service->autoMatch($serviceSong); + + $serviceSong->refresh(); + expect($serviceSong->song_arrangement_id)->toBe($firstArrangement->id); +}); + test('manualAssign ordnet Song manuell zu', function () { $song = Song::factory()->create(['has_translation' => true]); $serviceSong = ServiceSong::factory()->create([ @@ -138,6 +213,51 @@ expect($serviceSong->matched_at)->not->toBeNull(); }); +test('manualAssign setzt song_arrangement_id wenn null', function () { + $song = Song::factory()->create(); + $arrangement = SongArrangement::factory()->create([ + 'song_id' => $song->id, + 'name' => 'normal', + ]); + + $serviceSong = ServiceSong::factory()->create([ + 'song_id' => null, + 'song_arrangement_id' => null, + ]); + + $service = app(SongMatchingService::class); + $service->manualAssign($serviceSong, $song); + + $serviceSong->refresh(); + expect($serviceSong->song_arrangement_id)->toBe($arrangement->id); +}); + +test('manualAssign behält bestehende song_arrangement_id bei', function () { + $oldSong = Song::factory()->create(); + $oldArrangement = SongArrangement::factory()->create([ + 'song_id' => $oldSong->id, + 'name' => 'old', + ]); + + $newSong = Song::factory()->create(); + $newArrangement = SongArrangement::factory()->create([ + 'song_id' => $newSong->id, + 'name' => 'new', + ]); + + $serviceSong = ServiceSong::factory()->create([ + 'song_id' => $oldSong->id, + 'song_arrangement_id' => $oldArrangement->id, + ]); + + $service = app(SongMatchingService::class); + $service->manualAssign($serviceSong, $newSong); + + $serviceSong->refresh(); + expect($serviceSong->song_id)->toBe($newSong->id); + expect($serviceSong->song_arrangement_id)->toBe($oldArrangement->id); +}); + test('requestCreation sendet E-Mail und setzt request_sent_at', function () { Mail::fake();