From 80974108b10402f18e6ee976aea5082e6e310eff Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 3 Aug 2026 20:59:45 +0200 Subject: [PATCH] Wanderung friert das Nachziehen selbst ein, statt die Modellmethode zu rufen up() rief bisher Seat::linkToInstanceAdmin() auf. Ein migrate:fresh auf frischer Datenbank haette damit die dann aktuelle Fassung dieser Methode mitgefahren, nicht die von heute. Die Nachbefuellung ist jetzt eine eigene, dokumentiert duplizierte Fassung ueber den Query Builder. nc_admin_ref ist am Modell verschluesselt gecastet - roher Spaltenzugriff haette Chiffretext statt Klartext geliefert. Crypt::decryptString() macht denselben Aufruf wie der Modell-Cast, ohne eine Modellklasse zu laden. Kleinere Nachbesserung: fill() statt forceFill() in linkToInstanceAdmin() (alle drei Felder stehen in $fillable), und die Gruppen-Zuordnungspruefung verifiziert jetzt die konkreten Namen 'mitarbeiter'/'nur-lesen', nicht nur "irgendein Wert". --- app/Models/Seat.php | 2 +- ..._180000_add_nextcloud_columns_to_seats.php | 60 ++++++++++++++++--- tests/Feature/Seats/SeatModelTest.php | 6 ++ 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/app/Models/Seat.php b/app/Models/Seat.php index f70208f..47925a9 100644 --- a/app/Models/Seat.php +++ b/app/Models/Seat.php @@ -73,7 +73,7 @@ class Seat extends Model return; } - $this->forceFill([ + $this->fill([ 'nc_username' => $ref, 'nc_state' => self::STATE_SYNCED, 'nc_synced_at' => now(), diff --git a/database/migrations/2026_08_03_180000_add_nextcloud_columns_to_seats.php b/database/migrations/2026_08_03_180000_add_nextcloud_columns_to_seats.php index 73ef568..3a92dfb 100644 --- a/database/migrations/2026_08_03_180000_add_nextcloud_columns_to_seats.php +++ b/database/migrations/2026_08_03_180000_add_nextcloud_columns_to_seats.php @@ -1,8 +1,10 @@ customer, und ohne Eager Load waere das eine Anfrage pro - // Sitz statt einer pro Bloecken von 100 — bei owner-Sitzen (einer - // je Kunde) ist der Bestand hier so gross wie der Kundenstamm. - Seat::query()->where('role', 'owner')->with('customer')->chunkById(100, function ($sitze) { + DB::table('seats')->where('role', 'owner')->chunkById(100, function ($sitze) { foreach ($sitze as $sitz) { - $sitz->linkToInstanceAdmin(); + $roh = DB::table('instances') + ->where('customer_id', $sitz->customer_id) + ->whereIn('status', ['active', 'cancellation_scheduled']) + ->orderByDesc('id') + ->value('nc_admin_ref'); + + if ($roh === null) { + continue; + } + + try { + $ref = Crypt::decryptString($roh); + } catch (DecryptException) { + // Nicht entschluesselbar (z. B. ein Schluesselwechsel ohne + // Migration der Altwerte) wird wie „kein Konto" behandelt — + // ein Wanderungslauf darf daran nicht scheitern. + continue; + } + + if (blank($ref)) { + continue; + } + + DB::table('seats')->where('id', $sitz->id)->update([ + 'nc_username' => $ref, + 'nc_state' => 'synced', + 'nc_synced_at' => now(), + ]); } }); } diff --git a/tests/Feature/Seats/SeatModelTest.php b/tests/Feature/Seats/SeatModelTest.php index 3b11074..1d6ba4b 100644 --- a/tests/Feature/Seats/SeatModelTest.php +++ b/tests/Feature/Seats/SeatModelTest.php @@ -15,6 +15,12 @@ it('bildet jede Rolle auf genau eine Nextcloud-Gruppe ab', function () { expect(Seat::GROUPS)->toHaveKey($rolle) ->and(Seat::GROUPS[$rolle])->not->toBe(''); } + + // Nicht nur "irgendein Wert" — die Namen selbst, denn sie wandern spaeter + // woertlich in `occ group:adduser`. Ein Vertipper darin faellt sonst + // erst auf einer echten Instanz auf, nicht hier. + expect(Seat::GROUPS['member'])->toBe('mitarbeiter') + ->and(Seat::GROUPS['readonly'])->toBe('nur-lesen'); }); it('fuehrt owner und admin in die Admin-Gruppe', function () {