574 lines
24 KiB
PHP
574 lines
24 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Database\Seeders;
|
||
|
||
use App\Enums\Artist\ArtistEngagementStatus;
|
||
use App\Models\Artist;
|
||
use App\Models\ArtistContact;
|
||
use App\Models\ArtistEngagement;
|
||
use App\Models\Event;
|
||
use App\Models\Genre;
|
||
use App\Models\Organisation;
|
||
use App\Models\Performance;
|
||
use App\Models\Stage;
|
||
use App\Models\StageDay;
|
||
use Carbon\CarbonImmutable;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Str;
|
||
|
||
/**
|
||
* Seeds the org-wide artist roster + per-event timetable fixtures.
|
||
*
|
||
* Three entry points:
|
||
* - seedOrganisationPool() — 8 genres + 125 artists at the organisation level
|
||
* - seedForFestival() — multi-day festival with sub-events (Echt Feesten)
|
||
* - seedForFlatEvent() — single-day event (Braderie, Koningsdag, Nacht)
|
||
* - seedForSeries() — recurring series with weekly sub-events (IJsbaan)
|
||
*
|
||
* Every event seeded gets:
|
||
* - 2–5 stages (with stage_days for active days)
|
||
* - "scheduled" engagements: artists with one or more performances on a stage
|
||
* - "unscheduled" engagements: artists linked to the event but without
|
||
* any performance row yet (= booked but not slotted in the timetable)
|
||
*/
|
||
final class ArtistTimetableDevSeeder
|
||
{
|
||
/**
|
||
* Curated genre palette used across the org pool.
|
||
*
|
||
* @var list<array{name: string, color: string}>
|
||
*/
|
||
private const GENRE_SPECS = [
|
||
['name' => 'Hardstyle', 'color' => '#e85d75'],
|
||
['name' => 'Techno', 'color' => '#1f6feb'],
|
||
['name' => 'Indie', 'color' => '#7c3aed'],
|
||
['name' => 'Live band', 'color' => '#0ea5e9'],
|
||
['name' => 'House', 'color' => '#22c55e'],
|
||
['name' => 'Hip-hop', 'color' => '#f59e0b'],
|
||
['name' => 'Drum & Bass', 'color' => '#dc2626'],
|
||
['name' => 'Akoestisch', 'color' => '#6366f1'],
|
||
];
|
||
|
||
// =========================================================================
|
||
// Public entry points
|
||
// =========================================================================
|
||
|
||
/**
|
||
* Seed an organisation-level roster: 8 genres + N artists.
|
||
*
|
||
* @return array{genres: Collection<string, Genre>, artists: Collection<int, Artist>}
|
||
*/
|
||
public static function seedOrganisationPool(Organisation $org, int $artistCount = 125): array
|
||
{
|
||
$genres = self::createGenres($org);
|
||
$artists = self::createArtistPool($org, $genres, $artistCount);
|
||
|
||
return ['genres' => $genres, 'artists' => $artists];
|
||
}
|
||
|
||
/**
|
||
* Seed Echt Feesten festival fixture: 5 stages, ~30 scheduled
|
||
* engagements over 3 days, ~10 unscheduled engagements. Preserves
|
||
* the prototype audit fixture's special test cases (B2B pair on
|
||
* Mainstage Saturday, multi-perf engagement, parked performance).
|
||
*
|
||
* @param array<int, Event> $subEvents Sub-events keyed 0..n in date order
|
||
* @param array{genres: Collection<string, Genre>, artists: Collection<int, Artist>} $pool
|
||
* @return array{stages: int, engagements: int, performances: int, unscheduled: int}
|
||
*/
|
||
public static function seedForFestival(Organisation $org, Event $festival, array $subEvents, array $pool): array
|
||
{
|
||
if (count($subEvents) < 3) {
|
||
return ['stages' => 0, 'engagements' => 0, 'performances' => 0, 'unscheduled' => 0];
|
||
}
|
||
|
||
$stages = self::createStages($festival, [
|
||
['name' => 'Mainstage', 'color' => '#e85d75', 'capacity' => 4500, 'sort_order' => 1],
|
||
['name' => 'Havana', 'color' => '#0ea5e9', 'capacity' => 1200, 'sort_order' => 2],
|
||
['name' => 'Stairway', 'color' => '#7c3aed', 'capacity' => 800, 'sort_order' => 3],
|
||
['name' => 'Socialite', 'color' => '#22c55e', 'capacity' => 600, 'sort_order' => 4],
|
||
['name' => 'Tent op het Plein', 'color' => '#f59e0b', 'capacity' => 400, 'sort_order' => 5],
|
||
], $subEvents);
|
||
|
||
// Reserve the first 50 artists from the org pool for festival
|
||
// engagements. (Other events draw from later slices to keep
|
||
// some artists reserved for "unscheduled" / pure-pool variety.)
|
||
$bucket = $pool['artists']->slice(0, 50)->values();
|
||
$bucketIdx = 0;
|
||
|
||
$engagements = 0;
|
||
$performances = 0;
|
||
$unscheduled = 0;
|
||
|
||
// ── Scheduled engagements: 30 across 3 days ──
|
||
// Status mix loosely follows RFC §5.3: lots of contracted/confirmed,
|
||
// some optioned/requested, a couple of drafts and one cancelled.
|
||
$statusMix = [
|
||
ArtistEngagementStatus::Contracted, ArtistEngagementStatus::Contracted,
|
||
ArtistEngagementStatus::Contracted, ArtistEngagementStatus::Contracted,
|
||
ArtistEngagementStatus::Confirmed, ArtistEngagementStatus::Confirmed,
|
||
ArtistEngagementStatus::Confirmed, ArtistEngagementStatus::Option,
|
||
ArtistEngagementStatus::Option, ArtistEngagementStatus::Requested,
|
||
];
|
||
$statusIdx = 0;
|
||
|
||
$hourSlots = [16, 17, 18, 19, 20, 21, 22, 23];
|
||
|
||
foreach ($subEvents as $dayIdx => $subEvent) {
|
||
// Each sub-event gets 10 scheduled engagements across the 5 stages
|
||
for ($i = 0; $i < 10; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
$status = $statusMix[$statusIdx++ % count($statusMix)];
|
||
|
||
// unique(artist_id, event_id): only one engagement per
|
||
// (artist, sub-event). Since we wrap the pool, skip if
|
||
// we'd violate the constraint.
|
||
$existing = ArtistEngagement::query()
|
||
->where('artist_id', $artist->id)
|
||
->where('event_id', $subEvent->id)
|
||
->exists();
|
||
if ($existing) {
|
||
continue;
|
||
}
|
||
|
||
$engagement = self::createEngagement($artist, $subEvent, $status);
|
||
$engagements++;
|
||
|
||
$hour = $hourSlots[$i % count($hourSlots)];
|
||
$minute = ($i % 4) * 15;
|
||
$stage = $stages->values()[$i % $stages->count()];
|
||
self::createPerformance($engagement, $subEvent, $stage, $hour, $minute, 60);
|
||
$performances++;
|
||
|
||
// ~25% of contracted/confirmed engagements get a 2nd perf (D17 multi-perf)
|
||
if ($i % 4 === 0 && in_array($status, [ArtistEngagementStatus::Contracted, ArtistEngagementStatus::Confirmed], true)) {
|
||
self::createPerformance($engagement, $subEvent, $stage, $hour + 4, $minute, 45);
|
||
$performances++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── Unscheduled engagements: 12 artists linked but no performance ──
|
||
$unscheduledStatusMix = [
|
||
ArtistEngagementStatus::Draft, ArtistEngagementStatus::Draft,
|
||
ArtistEngagementStatus::Requested, ArtistEngagementStatus::Requested,
|
||
ArtistEngagementStatus::Option, ArtistEngagementStatus::Option,
|
||
ArtistEngagementStatus::Confirmed, ArtistEngagementStatus::Offered,
|
||
];
|
||
$u = 0;
|
||
for ($i = 0; $i < 12; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
$subEvent = $subEvents[$i % count($subEvents)];
|
||
|
||
$existing = ArtistEngagement::query()
|
||
->where('artist_id', $artist->id)
|
||
->where('event_id', $subEvent->id)
|
||
->exists();
|
||
if ($existing) {
|
||
continue;
|
||
}
|
||
|
||
self::createEngagement($artist, $subEvent, $unscheduledStatusMix[$u++ % count($unscheduledStatusMix)]);
|
||
$engagements++;
|
||
$unscheduled++;
|
||
}
|
||
|
||
// ── One parked performance (= scheduled but stage_id = null) ──
|
||
// Exercises the Session 4 "wachtrij" UI.
|
||
$parkedArtist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
$parkedSub = $subEvents[0];
|
||
if (! ArtistEngagement::query()->where('artist_id', $parkedArtist->id)->where('event_id', $parkedSub->id)->exists()) {
|
||
$parkedEngagement = self::createEngagement($parkedArtist, $parkedSub, ArtistEngagementStatus::Confirmed);
|
||
Performance::create([
|
||
'engagement_id' => $parkedEngagement->id,
|
||
'event_id' => $parkedSub->id,
|
||
'stage_id' => null,
|
||
'lane' => 0,
|
||
'start_at' => CarbonImmutable::parse($parkedSub->start_date)->setTime(0, 0),
|
||
'end_at' => CarbonImmutable::parse($parkedSub->start_date)->setTime(1, 0),
|
||
'version' => 0,
|
||
]);
|
||
$engagements++;
|
||
$performances++;
|
||
}
|
||
|
||
// ── B2B pair on Mainstage Saturday with 3-min offset (B2B detector seed) ──
|
||
// Two artists overlap on the same stage/lane within 3 minutes.
|
||
$zaterdag = $subEvents[1];
|
||
$b2bA = $bucket[$bucketIdx++ % $bucket->count()];
|
||
$b2bB = $bucket[$bucketIdx++ % $bucket->count()];
|
||
$mainstage = $stages['Mainstage'] ?? $stages->first();
|
||
|
||
foreach ([['artist' => $b2bA, 'minute' => 30], ['artist' => $b2bB, 'minute' => 33]] as $pair) {
|
||
if (ArtistEngagement::query()->where('artist_id', $pair['artist']->id)->where('event_id', $zaterdag->id)->exists()) {
|
||
continue;
|
||
}
|
||
$eng = self::createEngagement($pair['artist'], $zaterdag, ArtistEngagementStatus::Contracted);
|
||
self::createPerformance($eng, $zaterdag, $mainstage, 23, $pair['minute'], 60);
|
||
$engagements++;
|
||
$performances++;
|
||
}
|
||
|
||
return ['stages' => $stages->count(), 'engagements' => $engagements, 'performances' => $performances, 'unscheduled' => $unscheduled];
|
||
}
|
||
|
||
/**
|
||
* Seed a flat single-day event (e.g. Braderie, Koningsdag, Nacht).
|
||
*
|
||
* @param array{genres: Collection<string, Genre>, artists: Collection<int, Artist>} $pool
|
||
* @param array{
|
||
* stages?: list<array{name: string, color: string, capacity: int, sort_order: int}>,
|
||
* scheduled?: int,
|
||
* unscheduled?: int,
|
||
* pool_offset?: int,
|
||
* start_hour?: int,
|
||
* end_hour?: int,
|
||
* force_status?: ArtistEngagementStatus
|
||
* } $config
|
||
* @return array{stages: int, engagements: int, performances: int, unscheduled: int}
|
||
*/
|
||
public static function seedForFlatEvent(Organisation $org, Event $event, array $pool, array $config = []): array
|
||
{
|
||
$stageSpecs = $config['stages'] ?? [
|
||
['name' => 'Hoofdpodium', 'color' => '#e85d75', 'capacity' => 1500, 'sort_order' => 1],
|
||
['name' => 'Tweede Podium', 'color' => '#0ea5e9', 'capacity' => 600, 'sort_order' => 2],
|
||
];
|
||
$stages = self::createStages($event, $stageSpecs, [$event]);
|
||
|
||
$scheduledCount = $config['scheduled'] ?? 10;
|
||
$unscheduledCount = $config['unscheduled'] ?? 5;
|
||
$offset = $config['pool_offset'] ?? 50;
|
||
$startHour = $config['start_hour'] ?? 14;
|
||
$endHour = $config['end_hour'] ?? 23;
|
||
$forceStatus = $config['force_status'] ?? null;
|
||
|
||
$bucket = $pool['artists']->slice($offset, $scheduledCount + $unscheduledCount + 4)->values();
|
||
$bucketIdx = 0;
|
||
|
||
$engagements = 0;
|
||
$performances = 0;
|
||
$unscheduled = 0;
|
||
|
||
$statusMix = $forceStatus !== null ? [$forceStatus] : [
|
||
ArtistEngagementStatus::Contracted, ArtistEngagementStatus::Contracted,
|
||
ArtistEngagementStatus::Confirmed, ArtistEngagementStatus::Confirmed,
|
||
ArtistEngagementStatus::Option, ArtistEngagementStatus::Requested,
|
||
];
|
||
$statusIdx = 0;
|
||
|
||
$totalHours = max(1, $endHour - $startHour);
|
||
|
||
for ($i = 0; $i < $scheduledCount; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
if (ArtistEngagement::query()->where('artist_id', $artist->id)->where('event_id', $event->id)->exists()) {
|
||
continue;
|
||
}
|
||
|
||
$status = $statusMix[$statusIdx++ % count($statusMix)];
|
||
$engagement = self::createEngagement($artist, $event, $status);
|
||
$engagements++;
|
||
|
||
$stage = $stages->values()[$i % $stages->count()];
|
||
$hour = $startHour + ($i % $totalHours);
|
||
$minute = ($i % 4) * 15;
|
||
self::createPerformance($engagement, $event, $stage, $hour, $minute, 60);
|
||
$performances++;
|
||
}
|
||
|
||
$unscheduledStatusMix = $forceStatus !== null ? [$forceStatus] : [
|
||
ArtistEngagementStatus::Draft,
|
||
ArtistEngagementStatus::Requested,
|
||
ArtistEngagementStatus::Option,
|
||
ArtistEngagementStatus::Confirmed,
|
||
];
|
||
for ($i = 0; $i < $unscheduledCount; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
if (ArtistEngagement::query()->where('artist_id', $artist->id)->where('event_id', $event->id)->exists()) {
|
||
continue;
|
||
}
|
||
self::createEngagement($artist, $event, $unscheduledStatusMix[$i % count($unscheduledStatusMix)]);
|
||
$engagements++;
|
||
$unscheduled++;
|
||
}
|
||
|
||
return ['stages' => $stages->count(), 'engagements' => $engagements, 'performances' => $performances, 'unscheduled' => $unscheduled];
|
||
}
|
||
|
||
/**
|
||
* Seed a recurring series (e.g. IJsbaan): stages live on the parent
|
||
* event, and each weekly sub-event gets its own scheduled +
|
||
* unscheduled engagements.
|
||
*
|
||
* @param array<int, Event> $subEvents
|
||
* @param array{genres: Collection<string, Genre>, artists: Collection<int, Artist>} $pool
|
||
* @param array{
|
||
* stages?: list<array{name: string, color: string, capacity: int, sort_order: int}>,
|
||
* per_week_scheduled?: int,
|
||
* per_week_unscheduled?: int,
|
||
* pool_offset?: int
|
||
* } $config
|
||
* @return array{stages: int, engagements: int, performances: int, unscheduled: int}
|
||
*/
|
||
public static function seedForSeries(Organisation $org, Event $parent, array $subEvents, array $pool, array $config = []): array
|
||
{
|
||
$stageSpecs = $config['stages'] ?? [
|
||
['name' => 'Schaatspaviljoen', 'color' => '#0ea5e9', 'capacity' => 800, 'sort_order' => 1],
|
||
['name' => 'Verwarmde Tent', 'color' => '#22c55e', 'capacity' => 300, 'sort_order' => 2],
|
||
];
|
||
$stages = self::createStages($parent, $stageSpecs, $subEvents);
|
||
|
||
$perWeekScheduled = $config['per_week_scheduled'] ?? 3;
|
||
$perWeekUnscheduled = $config['per_week_unscheduled'] ?? 2;
|
||
$offset = $config['pool_offset'] ?? 80;
|
||
|
||
$bucket = $pool['artists']->slice($offset, ($perWeekScheduled + $perWeekUnscheduled) * count($subEvents) + 4)->values();
|
||
$bucketIdx = 0;
|
||
|
||
$engagements = 0;
|
||
$performances = 0;
|
||
$unscheduled = 0;
|
||
|
||
foreach ($subEvents as $weekIdx => $week) {
|
||
for ($i = 0; $i < $perWeekScheduled; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
if (ArtistEngagement::query()->where('artist_id', $artist->id)->where('event_id', $week->id)->exists()) {
|
||
continue;
|
||
}
|
||
|
||
$status = [ArtistEngagementStatus::Contracted, ArtistEngagementStatus::Confirmed, ArtistEngagementStatus::Option][$i % 3];
|
||
$engagement = self::createEngagement($artist, $week, $status);
|
||
$engagements++;
|
||
|
||
$stage = $stages->values()[$i % $stages->count()];
|
||
self::createPerformance($engagement, $week, $stage, 14 + $i * 2, 0, 60);
|
||
$performances++;
|
||
}
|
||
|
||
for ($i = 0; $i < $perWeekUnscheduled; $i++) {
|
||
$artist = $bucket[$bucketIdx++ % $bucket->count()];
|
||
if (ArtistEngagement::query()->where('artist_id', $artist->id)->where('event_id', $week->id)->exists()) {
|
||
continue;
|
||
}
|
||
$status = [ArtistEngagementStatus::Draft, ArtistEngagementStatus::Requested, ArtistEngagementStatus::Option][$i % 3];
|
||
self::createEngagement($artist, $week, $status);
|
||
$engagements++;
|
||
$unscheduled++;
|
||
}
|
||
}
|
||
|
||
return ['stages' => $stages->count(), 'engagements' => $engagements, 'performances' => $performances, 'unscheduled' => $unscheduled];
|
||
}
|
||
|
||
// =========================================================================
|
||
// Internal helpers
|
||
// =========================================================================
|
||
|
||
/** @return Collection<string, Genre> */
|
||
private static function createGenres(Organisation $org): Collection
|
||
{
|
||
$genres = collect();
|
||
foreach (self::GENRE_SPECS as $i => $spec) {
|
||
$genres[$spec['name']] = Genre::create([
|
||
'organisation_id' => $org->id,
|
||
'name' => $spec['name'],
|
||
'color' => $spec['color'],
|
||
'sort_order' => $i + 1,
|
||
'is_active' => true,
|
||
]);
|
||
}
|
||
|
||
return $genres;
|
||
}
|
||
|
||
/**
|
||
* @param Collection<string, Genre> $genres
|
||
* @return Collection<int, Artist>
|
||
*/
|
||
private static function createArtistPool(Organisation $org, Collection $genres, int $count): Collection
|
||
{
|
||
$names = self::generateArtistNames($count);
|
||
$countries = ['NL', 'NL', 'NL', 'NL', 'NL', 'BE', 'BE', 'DE', 'UK', 'FR'];
|
||
$genreList = $genres->values();
|
||
|
||
$artists = collect();
|
||
foreach ($names as $idx => $name) {
|
||
$genre = $genreList[$idx % $genreList->count()];
|
||
$artist = Artist::create([
|
||
'organisation_id' => $org->id,
|
||
'name' => $name,
|
||
'default_genre_id' => $genre->id,
|
||
'default_draw' => fake()->numberBetween(50, 5000),
|
||
'star_rating' => fake()->numberBetween(1, 5),
|
||
'home_base_country' => $countries[$idx % count($countries)],
|
||
]);
|
||
$artists->push($artist);
|
||
|
||
// ~30% get a tour-manager contact for downstream advance/portal flows
|
||
if ($idx % 3 === 0) {
|
||
ArtistContact::create([
|
||
'artist_id' => $artist->id,
|
||
'name' => 'Tour Manager '.$artist->name,
|
||
'email' => 'tm-'.$artist->slug.'@example.test',
|
||
'phone' => '+316123'.str_pad((string) ($idx + 1), 5, '0', STR_PAD_LEFT),
|
||
'role' => 'tour_manager',
|
||
'is_primary' => true,
|
||
'receives_briefing' => true,
|
||
'receives_infosheet' => true,
|
||
]);
|
||
}
|
||
}
|
||
|
||
return $artists;
|
||
}
|
||
|
||
/**
|
||
* Generate $count unique artist names by combining curated parts.
|
||
* The first ~40 entries are hand-picked "real-feeling" names so the
|
||
* top of the list looks human; the remainder are generated.
|
||
*
|
||
* @return list<string>
|
||
*/
|
||
private static function generateArtistNames(int $count): array
|
||
{
|
||
$curated = [
|
||
'Donker & Licht', 'Voltage Collective', 'Roos & de Wolf', 'Rotterdam Brass',
|
||
'Nachtwacht DJs', 'De Lichtbrigade', 'Dijkdoorbraak', 'Kaapse Gasten',
|
||
'Polderpop', 'De Stadsklokken', 'Noorderzon Project', 'Bij de Buren',
|
||
'Echo van de Maas', 'Kortsluiting', 'Bonte Hond', 'Rauwe Diamant',
|
||
'Maan & Sterren', 'Zondagmiddag Sessies', 'Het Geluidsmuseum', 'Storm op Zee',
|
||
'De Jonge Honden', 'Volksfeest Brass Band', 'Tussen de Wolken', 'Plat Vlaams',
|
||
'Stille Helden', 'De Veerboot', 'Holland Heat', 'Springstof',
|
||
'Café Onder de Brug', 'Mannen van Staal', 'Dijk Disco', 'Studio West',
|
||
'Het Kleine Orkest', 'Hard tegen Hart', 'Nachtsessie Live', 'Boterham met Tien',
|
||
'De Klompendansers', 'Kraakheldere Stemmen', 'Vrijdagavondblues', 'Broeders Beeld',
|
||
];
|
||
|
||
$cores = [
|
||
'Voltage', 'Echo', 'Lumen', 'Donker', 'Licht', 'Storm', 'Zon', 'Maan',
|
||
'Stadse', 'Polder', 'Noord', 'Zuid', 'Oost', 'West', 'Maas', 'IJssel',
|
||
'Rotterdam', 'Amsterdam', 'Utrecht', 'Eindhoven', 'Brabant', 'Limburg',
|
||
'Kraak', 'Veer', 'Brug', 'Plein', 'Markt', 'Park', 'Tuin', 'Bos',
|
||
'Ster', 'Wolk', 'Regen', 'Wind', 'Vuur', 'IJs', 'Sneeuw', 'Mist',
|
||
];
|
||
$suffixes = [
|
||
'Collective', 'Project', 'Sound', 'Live', 'Crew', 'Brothers', 'Sisters',
|
||
'Sessions', 'Orkest', 'Band', 'Trio', 'Quartet', 'DJs', 'System',
|
||
'Allstars', 'Republic', 'Union', 'Express', 'Riders', 'Beats',
|
||
];
|
||
|
||
$names = $curated;
|
||
$taken = array_flip($names);
|
||
$i = 0;
|
||
while (count($names) < $count) {
|
||
$core = $cores[$i % count($cores)];
|
||
$suffix = $suffixes[(int) ($i / count($cores)) % count($suffixes)];
|
||
$candidate = $core.' '.$suffix;
|
||
if (! isset($taken[$candidate])) {
|
||
$names[] = $candidate;
|
||
$taken[$candidate] = true;
|
||
}
|
||
$i++;
|
||
// Safety: avoid infinite loop if combinations exhaust
|
||
if ($i > count($cores) * count($suffixes) * 2) {
|
||
$names[] = $core.' '.$suffix.' '.Str::upper(Str::random(3));
|
||
}
|
||
}
|
||
|
||
return array_slice($names, 0, $count);
|
||
}
|
||
|
||
/**
|
||
* Create stages on $event and stage_days entries linking each stage
|
||
* to every $performanceDay (the parent event itself for flat events,
|
||
* each sub-event for festivals/series).
|
||
*
|
||
* @param list<array{name: string, color: string, capacity: int, sort_order: int}> $specs
|
||
* @param array<int, Event> $performanceDays
|
||
* @return Collection<string, Stage>
|
||
*/
|
||
private static function createStages(Event $event, array $specs, array $performanceDays): Collection
|
||
{
|
||
$stages = collect();
|
||
foreach ($specs as $spec) {
|
||
$stage = Stage::create([
|
||
'event_id' => $event->id,
|
||
'name' => $spec['name'],
|
||
'color' => $spec['color'],
|
||
'capacity' => $spec['capacity'],
|
||
'sort_order' => $spec['sort_order'],
|
||
]);
|
||
$stages[$spec['name']] = $stage;
|
||
|
||
foreach ($performanceDays as $day) {
|
||
StageDay::create([
|
||
'stage_id' => $stage->id,
|
||
'event_id' => $day->id,
|
||
]);
|
||
}
|
||
}
|
||
|
||
return $stages;
|
||
}
|
||
|
||
private static function createEngagement(Artist $artist, Event $event, ArtistEngagementStatus $status): ArtistEngagement
|
||
{
|
||
$attrs = [
|
||
'artist_id' => $artist->id,
|
||
'event_id' => $event->id,
|
||
'booking_status' => $status,
|
||
'fee_currency' => 'EUR',
|
||
'buma_applicable' => true,
|
||
'buma_percentage' => 7.00,
|
||
'buma_handled_by' => 'organisation',
|
||
'vat_applicable' => true,
|
||
'vat_percentage' => 21.00,
|
||
'payment_status' => 'none',
|
||
'crew_count' => fake()->numberBetween(1, 4),
|
||
'guests_count' => fake()->numberBetween(0, 6),
|
||
'advancing_completed_count' => 0,
|
||
'advancing_total_count' => 0,
|
||
];
|
||
|
||
if ($status === ArtistEngagementStatus::Option) {
|
||
$attrs['option_expires_at'] = CarbonImmutable::now()->addDays(fake()->numberBetween(7, 30));
|
||
}
|
||
if ($status === ArtistEngagementStatus::Requested) {
|
||
$attrs['requested_at'] = CarbonImmutable::now()->subDays(fake()->numberBetween(1, 14));
|
||
}
|
||
if ($status === ArtistEngagementStatus::Contracted) {
|
||
$attrs['fee_amount'] = fake()->randomFloat(2, 750, 18000);
|
||
}
|
||
if ($status === ArtistEngagementStatus::Confirmed) {
|
||
$attrs['fee_amount'] = fake()->randomFloat(2, 500, 12000);
|
||
}
|
||
|
||
return ArtistEngagement::create($attrs);
|
||
}
|
||
|
||
private static function createPerformance(
|
||
ArtistEngagement $engagement,
|
||
Event $day,
|
||
Stage $stage,
|
||
int $hour,
|
||
int $minute,
|
||
int $minutes,
|
||
): Performance {
|
||
$dayOffset = intdiv($hour, 24);
|
||
$start = CarbonImmutable::parse($day->start_date)
|
||
->addDays($dayOffset)
|
||
->setTime($hour % 24, $minute);
|
||
|
||
return Performance::create([
|
||
'engagement_id' => $engagement->id,
|
||
'event_id' => $day->id,
|
||
'stage_id' => $stage->id,
|
||
'lane' => 0,
|
||
'start_at' => $start,
|
||
'end_at' => $start->addMinutes($minutes),
|
||
'version' => 0,
|
||
]);
|
||
}
|
||
}
|