seedAdmin(); $coach = $this->seedCoach(); $parentRep = $this->seedParentRep(); $team = $this->seedTeam($coach); $data = $this->playerData(); $parentUsers = $this->seedParentUsers($data); $players = $this->seedPlayers($data, $team); $this->seedParentPlayerRelations($data, $players, $parentUsers); $this->assignParentRepChild($parentRep, $players); $this->seedSeasons(); $locations = $this->seedLocations(); $events = $this->seedEvents($team, $admin, $locations); $this->seedParticipants($events, $players, $admin, $parentUsers); $this->seedCatering($events, $parentUsers); $this->seedTimekeepers($events, $parentUsers); $this->seedComments($events, $admin, $coach, $parentUsers); $this->seedPlayerStats($events, $players); $this->seedCarpools($events, $parentUsers, $players); $this->seedFaqs($admin); $this->seedFinances($team, $admin, $coach, $parentRep); $this->seedActivityLogs($admin, $coach, $team, $events); $this->seedSoftDeletedRecords($team); } // ─── Admin ───────────────────────────────────────────── private function seedAdmin(): User { // Wenn bereits ein Admin existiert (z.B. durch Installer erstellt), diesen nutzen $existing = User::where('role', UserRole::Admin)->first(); if ($existing) { return $existing; } return User::updateOrCreate( ['email' => env('ADMIN_EMAIL', 'admin@handball.local')], [ 'name' => 'Charles Xavier', 'password' => env('ADMIN_PASSWORD', 'admin1234'), 'role' => UserRole::Admin, 'is_active' => true, ] ); } // ─── Trainer ─────────────────────────────────────────── private function seedCoach(): User { return User::firstOrCreate( ['email' => 'trainer@handball.local'], [ 'name' => 'Nick Fury', 'password' => Hash::make('trainer1234'), 'role' => UserRole::Coach, 'is_active' => true, 'phone' => '0171-1234567', ] ); } // ─── Elternvertretung ────────────────────────────────── private function seedParentRep(): User { return User::firstOrCreate( ['email' => 'elternvertretung@handball.local'], [ 'name' => 'Peggy Carter', 'password' => Hash::make('eltern1234'), 'role' => UserRole::ParentRep, 'is_active' => true, 'phone' => '0172-9876543', ] ); } // ─── Team ────────────────────────────────────────────── private function seedTeam(User $coach): Team { $team = Team::firstOrCreate( ['name' => 'Mannschaft I'], ['year_group' => '2017/2018', 'is_active' => true] ); // Coach via team_user pivot zuweisen DB::table('team_user')->updateOrInsert( ['team_id' => $team->id, 'user_id' => $coach->id], ['created_at' => now()] ); return $team; } // ─── Datenstruktur ───────────────────────────────────── private function playerData(): array { // [Kind-Vorname, Nachname, [[Eltern-Vorname, Geschlecht], ...]] // Mix aus Disney-, Marvel- und DC-Universum return [ // ── Marvel ────────────────────────────── ['Peter', 'Parker', [['Mary', 'w']]], ['Morgan', 'Stark', [['Tony', 'm'], ['Pepper', 'w']]], ['Cooper', 'Barton', [['Clint', 'm'], ['Laura', 'w']]], ['Kamala', 'Khan', [['Muneeba', 'w']]], ['Cassie', 'Lang', [['Scott', 'm']]], ['Natasha', 'Romanoff', [['Alexei', 'm']]], ['Wanda', 'Maximoff', [['Natalya', 'w']]], ['Loki', 'Odinson', [['Frigga', 'w']]], ['Groot', 'Guardian', [['Rocket', 'm']]], // ── DC ────────────────────────────────── ['Damian', 'Wayne', [['Bruce', 'm'], ['Talia', 'w']]], ['Jon', 'Kent', [['Clark', 'm'], ['Lois', 'w']]], ['Barbara', 'Gordon', [['James', 'm']]], ['Wally', 'West', [['Iris', 'w']]], ['Arthur', 'Curry', [['Atlanna', 'w']]], ['Diana', 'Prince', [['Hippolyta', 'w']]], ['Oliver', 'Queen', [['Moira', 'w']]], ['Kara', 'Danvers', [['Eliza', 'w']]], ['Garfield', 'Logan', [['Marie', 'w']]], // ── Disney ────────────────────────────── ['Simba', 'Pride', [['Sarabi', 'w']]], ['Elsa', 'Arendelle', [['Iduna', 'w']]], ['Rapunzel', 'Corona', [['Arianna', 'w']]], ['Moana', 'Motunui', [['Sina', 'w']]], ['Nemo', 'Reef', [['Marlin', 'm']]], ['Dash', 'Parr', [['Helen', 'w']]], ['Raya', 'Kumandra', [['Benja', 'm']]], ['Mirabel', 'Madrigal', [['Julieta', 'w']]], ['Riley', 'Andersen', [['Jill', 'w']]], ]; } // ─── Eltern-Benutzer ─────────────────────────────────── private function seedParentUsers(array $data): array { $hashedPassword = Hash::make('eltern1234'); $parentUsers = []; $phones = [ 'Mary Parker' => '0151-1111111', 'Tony Stark' => '0152-2222222', 'Sarabi Pride' => '0153-3333333', 'Scott Lang' => '0154-4444444', ]; foreach ($data as [$childFirst, $lastName, $parents]) { foreach ($parents as [$parentFirst, $gender]) { $fullName = "{$parentFirst} {$lastName}"; if (isset($parentUsers[$fullName])) { continue; } $email = $this->makeEmail($parentFirst, $lastName); $user = User::firstOrCreate( ['email' => $email], [ 'name' => $fullName, 'password' => $hashedPassword, 'role' => UserRole::User, 'is_active' => true, 'phone' => $phones[$fullName] ?? null, ] ); $parentUsers[$fullName] = $user; } } return $parentUsers; } // ─── Spieler ─────────────────────────────────────────── private function seedPlayers(array $data, Team $team): array { // Positionsverteilung: Index → Position $positions = [ 0 => PlayerPosition::Torwart, 1 => PlayerPosition::LinksAussen, 2 => PlayerPosition::RueckraumMitte, 3 => PlayerPosition::RueckraumLinks, 4 => PlayerPosition::RechtsAussen, 5 => PlayerPosition::RueckraumRechts, 6 => PlayerPosition::Kreislaeufer, 7 => PlayerPosition::LinksAussen, 8 => PlayerPosition::RechtsAussen, 9 => PlayerPosition::RueckraumLinks, 10 => PlayerPosition::RueckraumMitte, 11 => PlayerPosition::RueckraumRechts, 12 => PlayerPosition::Kreislaeufer, 13 => PlayerPosition::Torwart, // Ersatz-TW ]; $players = []; $jerseyNr = 1; foreach ($data as $idx => [$childFirst, $lastName, $parents]) { $player = Player::firstOrCreate( ['first_name' => $childFirst, 'last_name' => $lastName, 'team_id' => $team->id], [ 'birth_year' => $jerseyNr % 2 === 0 ? 2017 : 2018, 'jersey_number' => $jerseyNr, 'is_active' => true, 'photo_permission' => true, 'position' => $positions[$idx] ?? null, ] ); // Position auch bei bestehenden Spielern setzen if ($player->position === null && isset($positions[$idx])) { $player->update(['position' => $positions[$idx]]); } $players["{$childFirst} {$lastName}"] = $player; $jerseyNr++; } return $players; } // ─── Eltern-Kind-Zuordnungen ─────────────────────────── // ACHTUNG: parent_player hat KEIN updated_at → DB::table()->updateOrInsert() private function seedParentPlayerRelations(array $data, array $players, array $parentUsers): void { foreach ($data as [$childFirst, $lastName, $parents]) { $player = $players["{$childFirst} {$lastName}"]; foreach ($parents as [$parentFirst, $gender]) { $user = $parentUsers["{$parentFirst} {$lastName}"]; DB::table('parent_player')->updateOrInsert( ['parent_id' => $user->id, 'player_id' => $player->id], ['relationship_label' => $gender === 'm' ? 'Vater' : 'Mutter', 'created_at' => now()] ); } } } // ─── Elternvertretung Kind zuweisen ──────────────────── private function assignParentRepChild(User $parentRep, array $players): void { $firstPlayer = reset($players); DB::table('parent_player')->updateOrInsert( ['parent_id' => $parentRep->id, 'player_id' => $firstPlayer->id], ['relationship_label' => 'Elternvertretung', 'created_at' => now()] ); } // ─── Orte ────────────────────────────────────────────── private function seedLocations(): array { return [ Location::firstOrCreate( ['name' => 'Sporthalle Musterstadt'], ['address_text' => 'Schulstraße 12, 12345 Musterstadt', 'location_lat' => 50.1109, 'location_lng' => 8.6821] ), Location::firstOrCreate( ['name' => 'Sporthalle Nachbarort'], ['address_text' => 'Am Sportpark 5, 12346 Nachbarort', 'location_lat' => 50.1205, 'location_lng' => 8.7100] ), Location::firstOrCreate( ['name' => 'Turnierzentrum Landeshauptstadt'], ['address_text' => 'Sportplatzweg 1, 60000 Landeshauptstadt', 'location_lat' => 50.1155, 'location_lng' => 8.6842] ), ]; } // ─── Events ──────────────────────────────────────────── private function seedEvents(Team $team, User $admin, array $locations): array { $events = []; // 0: Training (vergangen) $events[] = Event::updateOrCreate( ['title' => 'Training Dienstag', 'team_id' => $team->id, 'start_at' => now()->subDays(3)->setTime(17, 0)], [ 'type' => EventType::Training, 'end_at' => now()->subDays(3)->setTime(18, 30), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'description_html' => '

Reguläres Training. Bitte Hallenschuhe und ausreichend Trinken mitbringen.

', 'created_by' => $admin->id, ] ); // 1: Training (Zukunft) $events[] = Event::updateOrCreate( ['title' => 'Training nächste Woche', 'team_id' => $team->id, 'start_at' => now()->next('Tuesday')->setTime(17, 0)], [ 'type' => EventType::Training, 'end_at' => now()->next('Tuesday')->setTime(18, 30), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'min_players' => 12, 'min_catering' => 1, 'min_timekeepers' => 1, 'description_html' => '

Training mit Schwerpunkt Passspiel. Bitte pünktlich kommen!

', 'created_by' => $admin->id, ] ); // 2: Heimspiel (vergangen, mit Ergebnis) $events[] = Event::updateOrCreate( ['title' => 'Heimspiel vs. TSV Beispielburg', 'team_id' => $team->id, 'start_at' => now()->subWeek()->previous('Saturday')->setTime(10, 0)], [ 'type' => EventType::HomeGame, 'end_at' => now()->subWeek()->previous('Saturday')->setTime(12, 0), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'opponent' => 'TSV Beispielburg', 'score_home' => 15, 'score_away' => 12, 'min_players' => 10, 'min_catering' => 2, 'min_timekeepers' => 2, 'description_html' => '

Unser erstes Heimspiel der Rückrunde! Bitte 30 Minuten vor Anpfiff da sein.

', 'created_by' => $admin->id, ] ); // 3: Auswärtsspiel (Zukunft) $events[] = Event::updateOrCreate( ['title' => 'Auswärtsspiel beim SC Nachbarort', 'team_id' => $team->id, 'start_at' => now()->addWeeks(2)->next('Saturday')->setTime(11, 0)], [ 'type' => EventType::AwayGame, 'end_at' => now()->addWeeks(2)->next('Saturday')->setTime(13, 0), 'status' => EventStatus::Published, 'location_name' => $locations[1]->name, 'address_text' => $locations[1]->address_text, 'location_lat' => $locations[1]->location_lat, 'location_lng' => $locations[1]->location_lng, 'opponent' => 'SC Nachbarort', 'min_players' => 10, 'description_html' => '

Auswärtsspiel — Fahrgemeinschaften bitte abstimmen. Treffpunkt 10:00 am Vereinsheim.

', 'created_by' => $admin->id, ] ); // 4: Turnier (Zukunft) $events[] = Event::updateOrCreate( ['title' => 'Nikolaus-Turnier Sportverein', 'team_id' => $team->id, 'start_at' => now()->addWeeks(3)->next('Sunday')->setTime(9, 0)], [ 'type' => EventType::Tournament, 'end_at' => now()->addWeeks(3)->next('Sunday')->setTime(16, 0), 'status' => EventStatus::Published, 'location_name' => $locations[2]->name, 'address_text' => $locations[2]->address_text, 'location_lat' => $locations[2]->location_lat, 'location_lng' => $locations[2]->location_lng, 'min_players' => 12, 'min_catering' => 3, 'min_timekeepers' => 2, 'description_html' => '

Ganztages-Turnier mit 8 Mannschaften. Bitte Lunchpaket einpacken.

Zeitplan

', 'created_by' => $admin->id, ] ); // 5: Besprechung (Zukunft) $events[] = Event::updateOrCreate( ['title' => 'Elternabend Rückrunden-Planung', 'team_id' => $team->id, 'start_at' => now()->addWeeks(4)->next('Wednesday')->setTime(19, 30)], [ 'type' => EventType::Meeting, 'end_at' => now()->addWeeks(4)->next('Wednesday')->setTime(21, 0), 'status' => EventStatus::Published, 'location_name' => 'Vereinsheim TV Musterstadt', 'address_text' => 'Vereinsweg 1, 12345 Musterstadt', 'min_players' => 10, 'description_html' => '

Themen:

', 'created_by' => $admin->id, ] ); // 6: Abgesagtes Training $events[] = Event::updateOrCreate( ['title' => 'Training Donnerstag (entfällt!)', 'team_id' => $team->id, 'start_at' => now()->next('Thursday')->setTime(17, 0)], [ 'type' => EventType::Training, 'end_at' => now()->next('Thursday')->setTime(18, 30), 'status' => EventStatus::Cancelled, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'description_html' => '

Fällt wegen Hallensperrung aus. Nächstes Training wie gewohnt am Dienstag.

', 'created_by' => $admin->id, ] ); // 7: Sonstiges (EventType::Other) $events[] = Event::updateOrCreate( ['title' => 'Trikot-Ausgabe & Mannschaftsfoto', 'team_id' => $team->id, 'start_at' => now()->addWeeks(5)->next('Saturday')->setTime(10, 0)], [ 'type' => EventType::Other, 'end_at' => now()->addWeeks(5)->next('Saturday')->setTime(12, 0), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'min_players' => 20, 'description_html' => '

Neue Trikots werden ausgegeben und wir machen das offizielle Mannschaftsfoto!

', 'created_by' => $admin->id, ] ); // 8: Entwurf $events[] = Event::updateOrCreate( ['title' => 'Freundschaftsspiel (in Planung)', 'team_id' => $team->id, 'start_at' => now()->addWeeks(6)->next('Saturday')->setTime(10, 0)], [ 'type' => EventType::HomeGame, 'end_at' => now()->addWeeks(6)->next('Saturday')->setTime(12, 0), 'status' => EventStatus::Draft, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'opponent' => 'TBD', 'description_html' => '

Freundschaftsspiel — Gegner und Uhrzeit werden noch festgelegt.

', 'created_by' => $admin->id, ] ); // 9: Heimspiel (vergangen, 12:10 Sieg) $events[] = Event::updateOrCreate( ['title' => 'Heimspiel vs. SG Westend', 'team_id' => $team->id, 'start_at' => now()->subWeeks(5)->previous('Saturday')->setTime(10, 0)], [ 'type' => EventType::HomeGame, 'end_at' => now()->subWeeks(5)->previous('Saturday')->setTime(12, 0), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'opponent' => 'SG Westend', 'score_home' => 12, 'score_away' => 10, 'min_players' => 10, 'min_catering' => 2, 'min_timekeepers' => 2, 'description_html' => '

Zweites Heimspiel der Saison. Wichtig: Hallenschuhe!

', 'created_by' => $admin->id, ] ); // 10: Auswärtsspiel (vergangen, 8:14 Niederlage) $events[] = Event::updateOrCreate( ['title' => 'Auswärtsspiel bei HSG Bergheim', 'team_id' => $team->id, 'start_at' => now()->subWeeks(4)->previous('Saturday')->setTime(11, 0)], [ 'type' => EventType::AwayGame, 'end_at' => now()->subWeeks(4)->previous('Saturday')->setTime(13, 0), 'status' => EventStatus::Published, 'location_name' => $locations[1]->name, 'address_text' => $locations[1]->address_text, 'location_lat' => $locations[1]->location_lat, 'location_lng' => $locations[1]->location_lng, 'opponent' => 'HSG Bergheim', 'score_home' => 8, 'score_away' => 14, 'min_players' => 10, 'description_html' => '

Auswärtsspiel. Treffpunkt 10:00 Uhr am Parkplatz.

', 'created_by' => $admin->id, ] ); // 11: Heimspiel (vergangen, 11:11 Unentschieden) $events[] = Event::updateOrCreate( ['title' => 'Heimspiel vs. TV Grüntal', 'team_id' => $team->id, 'start_at' => now()->subWeeks(3)->previous('Saturday')->setTime(10, 0)], [ 'type' => EventType::HomeGame, 'end_at' => now()->subWeeks(3)->previous('Saturday')->setTime(12, 0), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'opponent' => 'TV Grüntal', 'score_home' => 11, 'score_away' => 11, 'min_players' => 10, 'min_catering' => 2, 'min_timekeepers' => 2, 'description_html' => '

Spannendes Spiel erwartet! TV Grüntal steht direkt hinter uns in der Tabelle.

', 'created_by' => $admin->id, ] ); // 12: Auswärtsspiel (vergangen, 13:9 Sieg) $events[] = Event::updateOrCreate( ['title' => 'Auswärtsspiel bei JSG Adlerhorst', 'team_id' => $team->id, 'start_at' => now()->subDays(18)->setTime(11, 0)], [ 'type' => EventType::AwayGame, 'end_at' => now()->subDays(18)->setTime(13, 0), 'status' => EventStatus::Published, 'location_name' => $locations[2]->name, 'address_text' => $locations[2]->address_text, 'location_lat' => $locations[2]->location_lat, 'location_lng' => $locations[2]->location_lng, 'opponent' => 'JSG Adlerhorst', 'score_home' => 13, 'score_away' => 9, 'min_players' => 10, 'description_html' => '

Abfahrt 9:30 Uhr. Bitte Fahrgemeinschaften bilden.

', 'created_by' => $admin->id, ] ); // 13: Heimspiel (vergangen, 16:14 Sieg) $events[] = Event::updateOrCreate( ['title' => 'Heimspiel vs. SC Nordpark', 'team_id' => $team->id, 'start_at' => now()->subDays(11)->setTime(10, 0)], [ 'type' => EventType::HomeGame, 'end_at' => now()->subDays(11)->setTime(12, 0), 'status' => EventStatus::Published, 'location_name' => $locations[0]->name, 'address_text' => $locations[0]->address_text, 'location_lat' => $locations[0]->location_lat, 'location_lng' => $locations[0]->location_lng, 'opponent' => 'SC Nordpark', 'score_home' => 16, 'score_away' => 14, 'min_players' => 10, 'min_catering' => 2, 'min_timekeepers' => 2, 'description_html' => '

Tabellenführer zu Gast! Bitte alle da sein. Eltern: Kuchen mitbringen.

', 'created_by' => $admin->id, ] ); return $events; } // ─── Teilnehmer ──────────────────────────────────────── // ACHTUNG: set_by_user_id ist NOT NULL → immer explizit setzen private function seedParticipants(array $events, array $players, User $admin, array $parentUsers): void { $parentList = array_values($parentUsers); $playerList = array_values($players); // Statusverteilungen pro Event-Index: [Ja, Nein, Rest=Offen] $distributions = [ 0 => [22, 3], // Training vergangen 1 => [15, 5], // Training Zukunft 2 => [18, 4], // Heimspiel vs TSV Beispielburg 3 => [12, 3], // Auswärtsspiel 4 => [20, 2], // Turnier 6 => [5, 2], // Abgesagt 9 => [20, 3], // Heimspiel vs SG Westend 10 => [14, 6], // Auswärtsspiel bei HSG Bergheim 11 => [18, 4], // Heimspiel vs TV Grüntal 12 => [16, 5], // Auswärtsspiel bei JSG Adlerhorst 13 => [22, 2], // Heimspiel vs SC Nordpark ]; $sampleNotes = [ 'Komme direkt aus der Schule', 'Wird eventuell 10 Min. später', 'Bringt eigene Trinkflasche mit', ]; foreach ($distributions as $eventIdx => $dist) { $event = $events[$eventIdx]; [$yesCount, $noCount] = $dist; foreach (array_values($playerList) as $i => $player) { if ($i < $yesCount) { $status = ParticipantStatus::Yes; } elseif ($i < $yesCount + $noCount) { $status = ParticipantStatus::No; } else { $status = ParticipantStatus::Unknown; } $setByUserId = isset($parentList[$i]) ? $parentList[$i]->id : $admin->id; $respondedAt = $status !== ParticipantStatus::Unknown ? now()->subHours(rand(1, 72)) : null; $note = ($status === ParticipantStatus::Yes && $i < count($sampleNotes)) ? $sampleNotes[$i] : null; $participant = EventParticipant::firstOrNew( ['event_id' => $event->id, 'player_id' => $player->id] ); $participant->status = $status; $participant->set_by_user_id = $setByUserId; $participant->responded_at = $respondedAt; $participant->note = $note; $participant->save(); } } // Besprechung (Index 5): User-basierte Teilnehmer $meeting = $events[5]; $meeting->syncMeetingParticipants($admin->id); $meetingParticipants = EventParticipant::where('event_id', $meeting->id)->get(); foreach ($meetingParticipants->take(10) as $idx => $p) { $p->status = $idx < 7 ? ParticipantStatus::Yes : ParticipantStatus::No; $p->set_by_user_id = $p->user_id ?? $admin->id; $p->responded_at = now()->subHours(rand(1, 24)); $p->save(); } } // ─── Catering ────────────────────────────────────────── // Kein Catering bei AwayGame und Meeting private function seedCatering(array $events, array $parentUsers): void { $p = array_values($parentUsers); $entries = [ [$events[2]->id, $p[0]->id, CateringStatus::Yes, 'Bringe Marmorkuchen mit'], [$events[2]->id, $p[1]->id, CateringStatus::Yes, 'Bringe Obst und Wasser mit'], [$events[2]->id, $p[2]->id, CateringStatus::No, 'Kann leider nicht'], [$events[1]->id, $p[3]->id, CateringStatus::Yes, 'Belegte Brötchen'], [$events[1]->id, $p[4]->id, CateringStatus::Unknown, null], [$events[4]->id, $p[5]->id, CateringStatus::Yes, 'Kuchen und Muffins'], [$events[4]->id, $p[6]->id, CateringStatus::Yes, 'Getränke'], [$events[4]->id, $p[7]->id, CateringStatus::No, null], // Heimspiel vs SG Westend (9) [$events[9]->id, $p[2]->id, CateringStatus::Yes, 'Apfelkuchen'], [$events[9]->id, $p[4]->id, CateringStatus::Yes, 'Wasser und Saft'], // Heimspiel vs TV Grüntal (11) [$events[11]->id, $p[6]->id, CateringStatus::Yes, 'Obstteller'], [$events[11]->id, $p[8]->id, CateringStatus::Yes, 'Brezeln und Laugenbrötchen'], // Heimspiel vs SC Nordpark (13) [$events[13]->id, $p[1]->id, CateringStatus::Yes, 'Pizzaschnecken'], [$events[13]->id, $p[3]->id, CateringStatus::Yes, 'Gemüse-Sticks und Dip'], [$events[13]->id, $p[9]->id, CateringStatus::Yes, 'Getränke'], ]; foreach ($entries as [$eventId, $userId, $status, $note]) { $c = EventCatering::where('event_id', $eventId)->where('user_id', $userId)->first(); if (!$c) { $c = new EventCatering(['event_id' => $eventId]); $c->user_id = $userId; } $c->status = $status; $c->note = $note; $c->save(); } } // ─── Zeitnehmer ──────────────────────────────────────── // EventTimekeeper nutzt CateringStatus-Enum private function seedTimekeepers(array $events, array $parentUsers): void { $p = array_values($parentUsers); $entries = [ [$events[2]->id, $p[10]->id, CateringStatus::Yes], [$events[2]->id, $p[11]->id, CateringStatus::Yes], [$events[1]->id, $p[12]->id, CateringStatus::Yes], [$events[1]->id, $p[13]->id, CateringStatus::No], [$events[4]->id, $p[14]->id, CateringStatus::Yes], [$events[4]->id, $p[15]->id, CateringStatus::Unknown], // Heimspiel vs SG Westend (9) [$events[9]->id, $p[8]->id, CateringStatus::Yes], [$events[9]->id, $p[9]->id, CateringStatus::Yes], // Heimspiel vs TV Grüntal (11) [$events[11]->id, $p[10]->id, CateringStatus::Yes], [$events[11]->id, $p[12]->id, CateringStatus::Yes], // Heimspiel vs SC Nordpark (13) [$events[13]->id, $p[14]->id, CateringStatus::Yes], [$events[13]->id, $p[11]->id, CateringStatus::Yes], ]; foreach ($entries as [$eventId, $userId, $status]) { $t = EventTimekeeper::where('event_id', $eventId)->where('user_id', $userId)->first(); if (!$t) { $t = new EventTimekeeper(['event_id' => $eventId]); $t->user_id = $userId; } $t->status = $status; $t->save(); } } // ─── Kommentare ──────────────────────────────────────── private function seedComments(array $events, User $admin, User $coach, array $parentUsers): void { $p = array_values($parentUsers); // Heimspiel (Index 2): 4 Kommentare Comment::updateOrCreate( ['event_id' => $events[2]->id, 'user_id' => $p[0]->id, 'body' => 'Können wir Fahrgemeinschaften bilden?'], ['created_at' => now()->subDays(5)->subHours(5)] ); Comment::updateOrCreate( ['event_id' => $events[2]->id, 'user_id' => $admin->id, 'body' => 'Gute Idee! Bitte untereinander absprechen.'], ['created_at' => now()->subDays(5)->subHours(3)] ); Comment::updateOrCreate( ['event_id' => $events[2]->id, 'user_id' => $p[3]->id, 'body' => 'Wir können 3 Kinder mitnehmen.'], ['created_at' => now()->subDays(5)->subHours(2)] ); Comment::updateOrCreate( ['event_id' => $events[2]->id, 'user_id' => $coach->id, 'body' => 'Denkt bitte an die Trikots!'], ['created_at' => now()->subDays(5)->subHour()] ); // Training Zukunft (Index 1): 2 Kommentare Comment::updateOrCreate( ['event_id' => $events[1]->id, 'user_id' => $p[5]->id, 'body' => 'Kann jemand mein Kind abholen? Bin auf Dienstreise.'], ['created_at' => now()->subHours(12)] ); Comment::updateOrCreate( ['event_id' => $events[1]->id, 'user_id' => $p[8]->id, 'body' => 'Klar, kein Problem! Bringe sie mit.'], ['created_at' => now()->subHours(10)] ); // Turnier (Index 4): 2 Kommentare Comment::updateOrCreate( ['event_id' => $events[4]->id, 'user_id' => $admin->id, 'body' => 'Zeitplan folgt nächste Woche.'], ['created_at' => now()->subDays(1)] ); Comment::updateOrCreate( ['event_id' => $events[4]->id, 'user_id' => $p[2]->id, 'body' => 'Gibt es vor Ort Essen zu kaufen oder sollen wir selber was mitbringen?'], ['created_at' => now()->subHours(6)] ); // Besprechung (Index 5): 1 Kommentar Comment::updateOrCreate( ['event_id' => $events[5]->id, 'user_id' => $p[1]->id, 'body' => 'Wird es eine Tagesordnung geben?'], ['created_at' => now()->subHours(2)] ); // 1 gelöschter Kommentar (manuelles Soft-Delete via deleted_at + deleted_by) $deleted = Comment::updateOrCreate( ['event_id' => $events[2]->id, 'user_id' => $p[15]->id, 'body' => 'Dieser Kommentar wurde von einem Admin entfernt.'], ['created_at' => now()->subDays(6)] ); if (! $deleted->isDeleted()) { $deleted->update([ 'deleted_at' => now()->subDays(4), 'deleted_by' => $admin->id, ]); } } // ─── Spielerstatistiken ───────────────────────────────── // Stats für 6 vergangene Spiele (Index 2, 9, 10, 11, 12, 13) private function seedPlayerStats(array $events, array $players): void { $playerList = array_values($players); // Format: [index, is_gk, gk_saves, gk_shots, goals, shots, pen_goals, pen_shots, yellow, two_min, minutes, note, position] $allStats = [ // Spiel 2: Heimspiel vs TSV Beispielburg (15:12 Sieg) 2 => [ [0, true, 8, 20, 0, 0, null, null, 0, 0, 60, 'Starke Leistung im Tor', 'torwart'], [1, false, null, null, 3, 6, 1, 2, 0, 0, 55, 'Drei Tempogegenstöße', 'links_aussen'], [2, false, null, null, 2, 5, 0, 1, 1, 0, 50, 'Guter Aufbau', 'rueckraum_mitte'], [3, false, null, null, 2, 4, 1, 1, 0, 0, 50, null, 'rueckraum_links'], [4, false, null, null, 1, 3, 0, 0, 0, 0, 45, 'Schnelle Beine', 'rechts_aussen'], [5, false, null, null, 2, 5, 0, 0, 0, 1, 50, null, 'rueckraum_rechts'], [6, false, null, null, 1, 2, 0, 0, 0, 0, 40, 'Erste Tore der Saison!', 'kreislaeufer'], [7, false, null, null, 1, 4, 0, 0, 1, 0, 35, null, 'links_aussen'], [8, false, null, null, 1, 3, 0, 0, 0, 0, 40, null, 'rechts_aussen'], [9, false, null, null, 1, 2, 0, 0, 0, 0, 30, null, 'rueckraum_links'], [10, false, null, null, 1, 3, 0, 0, 0, 0, 45, 'Stark in der Abwehr', 'rueckraum_mitte'], [11, false, null, null, 0, 2, 0, 0, 0, 0, 25, null, 'rueckraum_rechts'], [12, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'kreislaeufer'], [13, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, 'torwart'], [14, false, null, null, 0, 2, 0, 0, 0, 0, 20, 'Erste Spielminuten', null], [15, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [16, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [17, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], ], // Spiel 9: Heimspiel vs SG Westend (12:10 Sieg) 9 => [ [0, true, 10, 22, 0, 0, null, null, 0, 0, 60, 'Überragend gehalten', 'torwart'], [1, false, null, null, 2, 5, 0, 1, 0, 0, 55, null, 'links_aussen'], [2, false, null, null, 3, 7, 2, 3, 0, 0, 55, 'Bester Werfer', 'rueckraum_mitte'], [3, false, null, null, 1, 3, 0, 0, 1, 0, 45, null, 'rueckraum_links'], [4, false, null, null, 1, 4, 0, 0, 0, 0, 50, null, 'rechts_aussen'], [5, false, null, null, 2, 4, 0, 0, 0, 0, 50, null, 'rueckraum_rechts'], [6, false, null, null, 1, 2, 0, 0, 0, 1, 40, null, 'kreislaeufer'], [7, false, null, null, 0, 3, 0, 0, 0, 0, 30, null, 'links_aussen'], [8, false, null, null, 1, 2, 0, 0, 0, 0, 35, null, 'rechts_aussen'], [9, false, null, null, 0, 1, 0, 0, 0, 0, 25, null, 'rueckraum_links'], [10, false, null, null, 1, 3, 0, 0, 0, 0, 40, null, 'rueckraum_mitte'], [11, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'rueckraum_rechts'], [12, false, null, null, 0, 2, 0, 0, 0, 0, 25, null, 'kreislaeufer'], [13, false, null, null, 0, 0, 0, 0, 0, 0, 15, null, null], [14, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [15, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [16, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [17, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [18, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [19, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], ], // Spiel 10: Auswärtsspiel bei HSG Bergheim (8:14 Niederlage) 10 => [ [0, true, 5, 22, 0, 0, null, null, 0, 0, 60, 'Schwieriger Tag', 'torwart'], [1, false, null, null, 2, 6, 1, 2, 0, 1, 50, null, 'links_aussen'], [2, false, null, null, 1, 5, 0, 0, 1, 0, 50, null, 'rueckraum_mitte'], [3, false, null, null, 1, 4, 0, 1, 0, 0, 45, null, 'rueckraum_links'], [4, false, null, null, 1, 3, 0, 0, 0, 0, 45, null, 'rechts_aussen'], [5, false, null, null, 1, 4, 0, 0, 1, 1, 40, '2-Min wegen Foul', 'rueckraum_rechts'], [6, false, null, null, 1, 3, 0, 0, 0, 0, 35, null, 'kreislaeufer'], [7, false, null, null, 0, 2, 0, 0, 0, 0, 30, null, 'links_aussen'], [8, false, null, null, 1, 3, 0, 0, 0, 0, 35, null, 'rechts_aussen'], [9, false, null, null, 0, 2, 0, 0, 0, 0, 25, null, 'rueckraum_links'], [10, false, null, null, 0, 2, 0, 0, 0, 0, 35, null, 'rueckraum_mitte'], [13, true, 3, 8, 0, 0, null, null, 0, 0, 20, 'Ersatz-TW 2. Halbzeit', 'torwart'], [11, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'kreislaeufer'], [14, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], ], // Spiel 11: Heimspiel vs TV Grüntal (11:11 Unentschieden) 11 => [ [0, true, 9, 22, 0, 0, null, null, 0, 0, 60, 'Klasse gehalten', 'torwart'], [1, false, null, null, 2, 5, 1, 1, 0, 0, 55, null, 'links_aussen'], [2, false, null, null, 2, 6, 0, 1, 0, 0, 55, null, 'rueckraum_mitte'], [3, false, null, null, 2, 4, 1, 2, 0, 0, 50, 'Starke 7-Meter', 'rueckraum_links'], [4, false, null, null, 1, 3, 0, 0, 0, 0, 45, null, 'rechts_aussen'], [5, false, null, null, 1, 4, 0, 0, 0, 0, 50, null, 'rueckraum_rechts'], [6, false, null, null, 1, 2, 0, 0, 1, 0, 40, null, 'kreislaeufer'], [7, false, null, null, 1, 3, 0, 0, 0, 0, 35, null, 'links_aussen'], [8, false, null, null, 0, 2, 0, 0, 0, 0, 30, null, 'rechts_aussen'], [9, false, null, null, 0, 1, 0, 0, 0, 0, 25, null, 'rueckraum_links'], [10, false, null, null, 1, 2, 0, 0, 0, 0, 40, null, 'rueckraum_mitte'], [11, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'rueckraum_rechts'], [12, false, null, null, 0, 2, 0, 0, 0, 0, 25, null, 'kreislaeufer'], [13, false, null, null, 0, 0, 0, 0, 0, 0, 15, null, null], [14, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, null], [15, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [16, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [17, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], ], // Spiel 12: Auswärtsspiel bei JSG Adlerhorst (13:9 Sieg) 12 => [ [0, true, 11, 20, 0, 0, null, null, 0, 0, 60, 'Bestes Spiel der Saison!', 'torwart'], [1, false, null, null, 3, 5, 2, 2, 0, 0, 55, 'Perfekte 7-Meter-Quote', 'links_aussen'], [2, false, null, null, 2, 4, 0, 0, 0, 0, 50, null, 'rueckraum_mitte'], [3, false, null, null, 2, 4, 0, 0, 0, 0, 50, null, 'rueckraum_links'], [4, false, null, null, 2, 4, 0, 0, 0, 0, 50, 'Doppelpack!', 'rechts_aussen'], [5, false, null, null, 1, 3, 0, 0, 0, 0, 45, null, 'rueckraum_rechts'], [6, false, null, null, 1, 2, 0, 0, 0, 0, 40, null, 'kreislaeufer'], [7, false, null, null, 1, 3, 0, 0, 1, 0, 35, null, 'links_aussen'], [8, false, null, null, 0, 2, 0, 0, 0, 0, 30, null, 'rechts_aussen'], [9, false, null, null, 1, 2, 0, 0, 0, 0, 30, null, 'rueckraum_links'], [10, false, null, null, 0, 2, 0, 0, 0, 0, 35, null, 'rueckraum_mitte'], [11, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'rueckraum_rechts'], [12, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, 'kreislaeufer'], [13, false, null, null, 0, 0, 0, 0, 0, 0, 15, null, null], [14, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [15, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], ], // Spiel 13: Heimspiel vs SC Nordpark (16:14 Sieg) 13 => [ [0, true, 7, 24, 0, 0, null, null, 0, 0, 50, null, 'torwart'], [13, true, 4, 10, 0, 0, null, null, 0, 0, 20, 'Guter Einsatz als Ersatz-TW', 'torwart'], [1, false, null, null, 4, 7, 2, 3, 0, 0, 55, 'Überragend!', 'links_aussen'], [2, false, null, null, 3, 6, 1, 1, 0, 0, 55, null, 'rueckraum_mitte'], [3, false, null, null, 2, 5, 0, 0, 0, 1, 50, null, 'rueckraum_links'], [4, false, null, null, 2, 4, 0, 0, 0, 0, 50, null, 'rechts_aussen'], [5, false, null, null, 1, 4, 0, 0, 1, 0, 50, null, 'rueckraum_rechts'], [6, false, null, null, 1, 3, 0, 0, 0, 0, 45, null, 'kreislaeufer'], [7, false, null, null, 1, 2, 0, 0, 0, 0, 35, null, 'links_aussen'], [8, false, null, null, 1, 3, 0, 0, 0, 0, 40, null, 'rechts_aussen'], [9, false, null, null, 0, 2, 0, 0, 0, 0, 30, null, 'rueckraum_links'], [10, false, null, null, 1, 3, 0, 0, 0, 0, 45, null, 'rueckraum_mitte'], [11, false, null, null, 0, 1, 0, 0, 0, 0, 25, null, 'rueckraum_rechts'], [12, false, null, null, 0, 2, 0, 0, 0, 0, 25, null, 'kreislaeufer'], [14, false, null, null, 0, 1, 0, 0, 0, 0, 20, null, null], [15, false, null, null, 0, 0, 0, 0, 0, 0, 15, null, null], [16, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [17, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [18, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [19, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], [20, false, null, null, 0, 0, 0, 0, 0, 0, 10, null, null], [21, false, null, null, 0, 1, 0, 0, 0, 0, 15, null, null], ], ]; foreach ($allStats as $eventIdx => $statsData) { $event = $events[$eventIdx]; foreach ($statsData as [$idx, $isGk, $gkSaves, $gkShots, $goals, $shots, $penGoals, $penShots, $yellow, $twoMin, $minutes, $note, $position]) { if (!isset($playerList[$idx])) { continue; } EventPlayerStat::updateOrCreate( ['event_id' => $event->id, 'player_id' => $playerList[$idx]->id], [ 'is_goalkeeper' => $isGk, 'goalkeeper_saves' => $gkSaves, 'goalkeeper_shots' => $gkShots, 'goals' => $goals, 'shots' => $shots, 'penalty_goals' => $penGoals, 'penalty_shots' => $penShots, 'yellow_cards' => $yellow, 'two_minute_suspensions' => $twoMin, 'playing_time_minutes' => $minutes, 'note' => $note, 'position' => $position, ] ); } } } // ─── Fahrgemeinschaften ────────────────────────────────── // Auswärtsspiel (Index 3) + Turnier (Index 4) private function seedCarpools(array $events, array $parentUsers, array $players): void { $p = array_values($parentUsers); $pl = array_values($players); // Auswärtsspiel (Index 3): 2 Fahrgemeinschaften $carpool1 = EventCarpool::where('event_id', $events[3]->id)->where('user_id', $p[0]->id)->first(); if (!$carpool1) { $carpool1 = new EventCarpool(['event_id' => $events[3]->id, 'seats' => 3, 'note' => 'Treffpunkt Parkplatz, 9:30 Uhr']); $carpool1->user_id = $p[0]->id; $carpool1->save(); } // 2 Kinder zuordnen foreach ([0, 1] as $childIdx) { if (isset($pl[$childIdx])) { $pass = EventCarpoolPassenger::where('carpool_id', $carpool1->id)->where('player_id', $pl[$childIdx]->id)->first(); if (!$pass) { $pass = new EventCarpoolPassenger(['carpool_id' => $carpool1->id, 'player_id' => $pl[$childIdx]->id]); $pass->added_by = $p[0]->id; $pass->save(); } } } $carpool2 = EventCarpool::where('event_id', $events[3]->id)->where('user_id', $p[2]->id)->first(); if (!$carpool2) { $carpool2 = new EventCarpool(['event_id' => $events[3]->id, 'seats' => 2, 'note' => 'Abfahrt 9:15 Uhr ab Vereinsheim']); $carpool2->user_id = $p[2]->id; $carpool2->save(); } // Voll belegt — 2 Kinder foreach ([2, 3] as $childIdx) { if (isset($pl[$childIdx])) { $pass = EventCarpoolPassenger::where('carpool_id', $carpool2->id)->where('player_id', $pl[$childIdx]->id)->first(); if (!$pass) { $pass = new EventCarpoolPassenger(['carpool_id' => $carpool2->id, 'player_id' => $pl[$childIdx]->id]); $pass->added_by = $p[2]->id; $pass->save(); } } } // Turnier (Index 4): 1 Fahrgemeinschaft $carpool3 = EventCarpool::where('event_id', $events[4]->id)->where('user_id', $p[5]->id)->first(); if (!$carpool3) { $carpool3 = new EventCarpool(['event_id' => $events[4]->id, 'seats' => 4, 'note' => 'Großer Van, Abfahrt 8:00 Uhr']); $carpool3->user_id = $p[5]->id; $carpool3->save(); } // 1 Kind zugeordnet if (isset($pl[5])) { $pass = EventCarpoolPassenger::where('carpool_id', $carpool3->id)->where('player_id', $pl[5]->id)->first(); if (!$pass) { $pass = new EventCarpoolPassenger(['carpool_id' => $carpool3->id, 'player_id' => $pl[5]->id]); $pass->added_by = $p[5]->id; $pass->save(); } } } // ─── FAQs ────────────────────────────────────────────── private function seedFaqs(User $admin): void { $faqs = [ ['title' => 'Wie melde ich mein Kind für ein Event an?', 'content_html' => '

Gehe auf die Event-Detailseite und klicke bei deinem Kind auf "Zusagen". Du kannst den Status jederzeit ändern.

Bitte gib so früh wie möglich Bescheid, damit der Trainer planen kann.

', 'sort_order' => 1], ['title' => 'Wie funktioniert das Catering?', 'content_html' => '

Bei Heimspielen und Turnieren wird Catering organisiert. Du kannst auf der Event-Seite angeben, ob du etwas mitbringst und was genau.

Typische Beiträge sind Kuchen, Obst, Getränke oder belegte Brötchen.

', 'sort_order' => 2], ['title' => 'Was ist ein Zeitnehmer?', 'content_html' => '

Bei Heimspielen werden Zeitnehmer benötigt, die die Spieluhr bedienen. Pro Spiel brauchen wir mindestens 2 Zeitnehmer aus den Reihen der Eltern.

Keine Sorge — es wird vorher eine kurze Einweisung gegeben!

', 'sort_order' => 3], ['title' => 'Wie funktionieren Fahrgemeinschaften?', 'content_html' => '

Bei Auswärtsspielen und Turnieren können Eltern Fahrgemeinschaften anbieten. Gehe auf die Event-Detailseite und trage ein, wie viele Plätze du anbieten kannst.

Andere Eltern können dann ihre Kinder für die Mitfahrt eintragen.

', 'sort_order' => 4], ['title' => 'Wo finde ich die Spielerstatistiken?', 'content_html' => '

Im Admin-Bereich unter "Statistik" findest du eine Übersicht aller Spieler mit Toren, Würfen und Torwart-Statistiken.

Die Statistiken können nach Saison, Zeitraum und Team gefiltert werden.

', 'sort_order' => 5], ['title' => 'Kann ich mein Profil bearbeiten?', 'content_html' => '

Ja! Klicke oben rechts auf deinen Namen und dann auf "Profil". Dort kannst du:

', 'sort_order' => 6], ['title' => 'Was passiert wenn ich absage?', 'content_html' => '

Kein Problem! Absagen sind wichtig, damit der Trainer rechtzeitig planen kann. Bitte sage so früh wie möglich ab.

Du kannst auch eine Notiz hinterlassen (z.B. "krank" oder "Familienfeier").

', 'sort_order' => 7], ]; foreach ($faqs as $data) { $existing = Faq::where('title', $data['title'])->first(); if (! $existing) { $faq = new Faq([ 'title' => $data['title'], 'content_html' => $data['content_html'], 'sort_order' => $data['sort_order'], ]); $faq->created_by = $admin->id; $faq->save(); } } } // ─── Aktivitätslog ───────────────────────────────────── // ActivityLog: $timestamps = false, created_at muss explizit gesetzt werden private function seedActivityLogs(User $admin, User $coach, Team $team, array $events): void { $base = now()->subDays(7); $logs = [ ['user_id' => $admin->id, 'action' => 'install', 'description' => 'App wurde installiert', 'ip_address' => '127.0.0.1', 'created_at' => $base], ['user_id' => $admin->id, 'action' => 'login', 'description' => 'Admin hat sich eingeloggt', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addHour()], ['user_id' => $admin->id, 'action' => 'created', 'model_type' => 'Team', 'model_id' => $team->id, 'description' => "Team '{$team->name}' erstellt", 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addHours(2)], ['user_id' => $admin->id, 'action' => 'created', 'model_type' => 'Event', 'model_id' => $events[0]->id, 'description' => "Event '{$events[0]->title}' erstellt", 'properties' => ['new' => ['title' => $events[0]->title, 'type' => 'training']], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addHours(3)], ['user_id' => $admin->id, 'action' => 'updated', 'model_type' => 'Event', 'model_id' => $events[2]->id, 'description' => "Event '{$events[2]->title}' bearbeitet", 'properties' => ['old' => ['min_players' => null], 'new' => ['min_players' => 10]], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addHours(4)], ['user_id' => $admin->id, 'action' => 'updated', 'model_type' => 'Setting', 'description' => 'Einstellungen aktualisiert', 'properties' => ['old' => ['app_name' => 'Handball App'], 'new' => ['app_name' => 'Demo Handball']], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDay()], ['user_id' => $coach->id, 'action' => 'login', 'description' => 'Trainer hat sich eingeloggt', 'ip_address' => '192.168.1.50', 'created_at' => $base->copy()->addDays(2)], ['user_id' => $admin->id, 'action' => 'created', 'model_type' => 'Player', 'model_id' => 1, 'description' => 'Spieler erstellt', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(2)->addHour()], ['user_id' => $admin->id, 'action' => 'updated', 'model_type' => 'User', 'model_id' => $coach->id, 'description' => "Rolle von '{$coach->name}' geändert", 'properties' => ['old' => ['role' => 'user'], 'new' => ['role' => 'coach']], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(3)], ['user_id' => $admin->id, 'action' => 'updated', 'model_type' => 'Event', 'model_id' => $events[6]->id, 'description' => 'Event abgesagt', 'properties' => ['old' => ['status' => 'published'], 'new' => ['status' => 'cancelled']], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(4)], ['user_id' => $admin->id, 'action' => 'deleted', 'model_type' => 'User', 'description' => 'Benutzer gelöscht (Soft-Delete)', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(5)], ['user_id' => $admin->id, 'action' => 'created', 'model_type' => 'File', 'description' => 'Datei "Regelwerk_Handball.pdf" hochgeladen', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(6)], ['user_id' => $admin->id, 'action' => 'season_created', 'model_type' => 'Season', 'description' => 'Saison "2025/2026" erstellt', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(6)->addHours(2)], ['user_id' => $admin->id, 'action' => 'finance_created', 'model_type' => 'Finance', 'description' => 'income: Hauptsponsor Saison 2025/2026', 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(6)->addHours(3)], ['user_id' => $coach->id, 'action' => 'finance_created', 'model_type' => 'Finance', 'description' => 'expense: Neue Handbälle (10 Stück)', 'ip_address' => '192.168.1.50', 'created_at' => $base->copy()->addDays(6)->addHours(4)], ['user_id' => $admin->id, 'action' => 'finance_updated', 'model_type' => 'Finance', 'description' => 'income: Hauptsponsor Saison 2025/2026', 'properties' => ['before' => ['amount' => 80000], 'after' => ['amount' => 100000]], 'ip_address' => '127.0.0.1', 'created_at' => $base->copy()->addDays(6)->addHours(5)], ]; foreach ($logs as $log) { $exists = ActivityLog::where('description', $log['description']) ->where('created_at', $log['created_at']) ->exists(); if (! $exists) { ActivityLog::create($log); } } } // ─── Soft-Deleted Records ────────────────────────────── private function seedSoftDeletedRecords(Team $team): void { // Gelöschter User (3 Tage, im restaurierbaren 7-Tage-Fenster) $deletedUser = User::withTrashed()->firstOrCreate( ['email' => 'geloescht@handball.local'], [ 'name' => 'Thaddeus Ross', 'password' => Hash::make('geloescht1234'), 'role' => UserRole::User, 'is_active' => true, ] ); if (! $deletedUser->trashed()) { $deletedUser->delete(); User::withTrashed()->where('id', $deletedUser->id) ->update(['deleted_at' => now()->subDays(3)]); } // Gelöschter Spieler (2 Tage, im restaurierbaren 7-Tage-Fenster) $deletedPlayer = Player::withTrashed()->firstOrCreate( ['first_name' => 'Bucky', 'last_name' => 'Barnes', 'team_id' => $team->id], [ 'birth_year' => 2017, 'is_active' => true, 'photo_permission' => false, ] ); if (! $deletedPlayer->trashed()) { $deletedPlayer->delete(); Player::withTrashed()->where('id', $deletedPlayer->id) ->update(['deleted_at' => now()->subDays(2)]); } } // ─── Helper ──────────────────────────────────────────── private function makeEmail(string $first, string $last): string { $map = ['ß' => 'ss', 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'ae', 'Ö' => 'oe', 'Ü' => 'ue']; $f = mb_strtolower(strtr($first, $map)); $l = mb_strtolower(strtr($last, $map)); return "{$f}.{$l}@handball.local"; } // ─── Saisons ────────────────────────────────────────── private function seedSeasons(): void { Season::firstOrCreate( ['name' => '2024/2025'], ['start_date' => '2024-09-01', 'end_date' => '2025-06-30', 'is_current' => false] ); Season::firstOrCreate( ['name' => '2025/2026'], ['start_date' => '2025-09-01', 'end_date' => '2026-06-30', 'is_current' => true] ); } // ─── Finanzen ───────────────────────────────────────── private function seedFinances(Team $team, User $admin, User $coach, User $parentRep): void { // [type, category, title, amount_cents, date, created_by, team_id (null=übergreifend), notes] $entries = [ // ─── Saison 2024/2025 (Vorjahr) ───────────────────────── // Einnahmen ['income', 'sponsoring', 'Hauptsponsor Saison 2024/2025', 80000, '2024-09-01', $admin->id, $team->id, 'Vertrag über 2 Jahre'], ['income', 'membership', 'Mitgliedsbeiträge Herbst 2024', 52000, '2024-10-01', $admin->id, $team->id, null], ['income', 'catering', 'Catering Heimspiel vs. SG Westend', 9500, '2024-10-19', $parentRep->id, $team->id, 'Kuchen + Getränke'], ['income', 'catering', 'Kuchenverkauf Adventsturnier', 15500, '2024-12-07', $parentRep->id, $team->id, null], ['income', 'events', 'Eintritt Weihnachtsfeier 2024', 12000, '2024-12-21', $coach->id, $team->id, null], ['income', 'membership', 'Mitgliedsbeiträge Frühjahr 2025', 52000, '2025-01-15', $admin->id, $team->id, null], ['income', 'sponsoring', 'Bandenwerbung Sporthalle', 25000, '2025-02-01', $admin->id, null, 'Vereinsübergreifend'], ['income', 'tournament_fees', 'Startgeld-Rückerstattung Turnier', 3500, '2025-03-10', $coach->id, $team->id, 'Turnier abgesagt, Erstattung erhalten'], ['income', 'catering', 'Catering Heimspiel vs. TV Grünberg', 8200, '2025-04-05', $parentRep->id, $team->id, null], ['income', 'other', 'Spende Elternschaft', 20000, '2025-05-20', $admin->id, $team->id, 'Für neue Trikots'], // Ausgaben ['expense', 'venue_rental', 'Hallenmiete Sep-Dez 2024', 72000, '2024-09-15', $admin->id, $team->id, '4 Monate à 180€'], ['expense', 'equipment', 'Handbälle Größe 0 (12 Stück)', 17940, '2024-09-20', $coach->id, $team->id, 'Select Solera'], ['expense', 'training_material','Koordinationsleiter + Hütchen-Set', 5990, '2024-10-05', $coach->id, $team->id, null], ['expense', 'tournament_fees', 'Anmeldegebühr Adventsturnier', 5000, '2024-11-01', $coach->id, $team->id, null], ['expense', 'transport', 'Busfahrt Auswärtsspiel Bergheim', 28000, '2024-11-16', $admin->id, $team->id, null], ['expense', 'venue_rental', 'Hallenmiete Jan-Jun 2025', 108000, '2025-01-10', $admin->id, $team->id, '6 Monate à 180€'], ['expense', 'equipment', 'Tornetze Ersatz (2 Stück)', 12500, '2025-02-15', $admin->id, $team->id, null], ['expense', 'other', 'Schiedsrichterkosten Rückrunde', 9000, '2025-03-01', $admin->id, $team->id, '6 Heimspiele à 15€'], ['expense', 'transport', 'Fahrtkostenzuschuss Trainer', 15000, '2025-04-01', $admin->id, null, 'Vereinsübergreifend, monatlich'], ['expense', 'events', 'Saisonabschlussfeier 2024/2025', 22000, '2025-06-28', $parentRep->id, $team->id, 'Grillpaket + Getränke'], // ─── Saison 2025/2026 (aktuell) ────────────────────────── // Einnahmen — September ['income', 'sponsoring', 'Hauptsponsor Saison 2025/2026', 100000, '2025-09-01', $admin->id, $team->id, 'Vertrag verlängert, +200€'], ['income', 'sponsoring', 'Trikotsponsoring Autohaus Schmidt', 30000, '2025-09-15', $admin->id, $team->id, null], ['income', 'membership', 'Mitgliedsbeiträge Herbst 2025', 58000, '2025-09-20', $admin->id, $team->id, '29 Spieler à 20€'], // Einnahmen — Oktober ['income', 'catering', 'Catering Heimspiel vs. HSG Musterstadt', 8500, '2025-10-12', $parentRep->id, $team->id, 'Kuchen + Kaffee'], ['income', 'other', 'Spende Förderverein', 15000, '2025-10-15', $admin->id, null, 'Vereinsübergreifende Förderung'], // Einnahmen — November ['income', 'catering', 'Kuchenverkauf Herbstturnier', 12050, '2025-11-15', $parentRep->id, $team->id, 'Rekord! 24 Kuchen verkauft'], ['income', 'catering', 'Catering Heimspiel vs. TV Adler', 7200, '2025-11-23', $parentRep->id, $team->id, null], // Einnahmen — Dezember ['income', 'events', 'Einnahmen Weihnachtsfeier', 18500, '2025-12-20', $coach->id, $team->id, 'Tombola + Getränkeverkauf'], ['income', 'sponsoring', 'Weihnachtsspende Bäckerei Müller', 10000, '2025-12-22', $admin->id, $team->id, null], // Einnahmen — Januar/Februar 2026 ['income', 'membership', 'Mitgliedsbeiträge Frühjahr 2026', 58000, '2026-01-10', $admin->id, $team->id, null], ['income', 'catering', 'Catering Heimspiel vs. SC Nordpark', 6800, '2026-01-25', $parentRep->id, $team->id, null], ['income', 'tournament_fees', 'Startgeld Hallenturnier (Veranstalter)', 24000, '2026-02-08', $coach->id, $team->id, '8 Teams à 30€'], ['income', 'catering', 'Kuchenverkauf Hallenturnier', 19500, '2026-02-08', $parentRep->id, $team->id, 'Ganztagesturnier — sehr guter Umsatz'], // Ausgaben — September ['expense', 'equipment', 'Trikotdruck Rückennummern', 18500, '2025-09-05', $admin->id, $team->id, 'Satz Heim + Auswärts'], ['expense', 'equipment', 'Neue Handbälle (10 Stück)', 24990, '2025-09-10', $coach->id, $team->id, 'Select Solera Größe 0'], ['expense', 'training_material','Hütchen, Leibchen, Koordinationsleiter', 4500, '2025-09-20', $coach->id, $team->id, null], ['expense', 'venue_rental', 'Hallenmiete September 2025', 18000, '2025-09-25', $admin->id, $team->id, null], // Ausgaben — Oktober ['expense', 'venue_rental', 'Hallenmiete Oktober 2025', 18000, '2025-10-01', $admin->id, $team->id, null], ['expense', 'tournament_fees', 'Teilnahme Herbstcup', 7500, '2025-10-20', $coach->id, $team->id, null], ['expense', 'transport', 'Busfahrt Auswärtsspiel Musterstadt', 32000, '2025-10-26', $admin->id, $team->id, 'Reisebus 50 Plätze'], // Ausgaben — November ['expense', 'venue_rental', 'Hallenmiete November 2025', 18000, '2025-11-01', $admin->id, $team->id, null], ['expense', 'other', 'Schiedsrichterkosten 3 Heimspiele', 6000, '2025-11-30', $admin->id, $team->id, null], ['expense', 'equipment', 'Torwarthandschuhe (2 Paar)', 7980, '2025-11-10', $coach->id, $team->id, 'Kempa Caution'], // Ausgaben — Dezember ['expense', 'venue_rental', 'Hallenmiete Dezember 2025', 18000, '2025-12-01', $admin->id, $team->id, null], ['expense', 'events', 'Material Weihnachtsfeier', 8500, '2025-12-18', $parentRep->id, $team->id, 'Deko, Geschenke, Nikolaus'], ['expense', 'other', 'Versicherung Vereinshaftpflicht', 35000, '2025-12-15', $admin->id, null, 'Vereinsübergreifend, jährlich'], // Ausgaben — Januar/Februar 2026 ['expense', 'venue_rental', 'Hallenmiete Januar 2026', 18000, '2026-01-05', $admin->id, $team->id, null], ['expense', 'transport', 'Busfahrt Auswärtsspiel Südstadt', 24000, '2026-01-18', $admin->id, $team->id, null], ['expense', 'venue_rental', 'Hallenmiete Februar 2026', 18000, '2026-02-01', $admin->id, $team->id, null], ['expense', 'tournament_fees', 'Hallenmiete Zusatzstunden Turnier', 12000, '2026-02-06', $coach->id, $team->id, 'Ganztages-Hallennutzung'], ['expense', 'training_material','Markierungshütchen Nachbestellung', 1990, '2026-02-20', $coach->id, $team->id, null], ]; foreach ($entries as [$type, $category, $title, $amount, $date, $createdBy, $teamId, $notes]) { Finance::firstOrCreate( ['title' => $title, 'date' => $date], [ 'team_id' => $teamId, 'type' => $type, 'category' => $category, 'amount' => $amount, 'notes' => $notes, 'created_by' => $createdBy, ] ); } } }