diff --git a/api/app/Console/Commands/Artist/DemoteExpiredOptions.php b/api/app/Console/Commands/Artist/DemoteExpiredOptions.php new file mode 100644 index 00000000..a3d7f8eb --- /dev/null +++ b/api/app/Console/Commands/Artist/DemoteExpiredOptions.php @@ -0,0 +1,79 @@ +withoutGlobalScope(OrganisationScope::class) + ->where('booking_status', ArtistEngagementStatus::Option->value) + ->whereNotNull('option_expires_at') + ->where('option_expires_at', '<=', now()) + ->whereNull('deleted_at') + ->get(); + + $demotedIds = []; + foreach ($expired as $engagement) { + // Re-check status under fresh state — another worker / a + // user UI action may have already transitioned this row. + if ($engagement->booking_status !== ArtistEngagementStatus::Option) { + continue; + } + + $service->transitionStatus($engagement, ArtistEngagementStatus::Draft); + + activity('artist_engagement') + ->performedOn($engagement) + ->event('option_expired') + ->withProperties([ + 'organisation_id' => $engagement->organisation_id, + 'event_id' => $engagement->event_id, + 'option_expires_at' => optional($engagement->option_expires_at)->toIso8601String(), + ]) + ->log('option_expired'); + + $demotedIds[] = (string) $engagement->id; + } + + $count = count($demotedIds); + $this->info("Demoted {$count} option(s) on ".now()->toDateString().'.'); + if ($count > 0) { + $this->line('IDs: '.implode(', ', $demotedIds)); + } + + return self::SUCCESS; + } +} diff --git a/api/routes/console.php b/api/routes/console.php index 2713d22a..ee2ff4bf 100644 --- a/api/routes/console.php +++ b/api/routes/console.php @@ -10,6 +10,13 @@ Artisan::command('inspire', function () { Schedule::command('invitations:expire')->daily(); +// RFC-TIMETABLE v0.2 — demote engagements whose option_expires_at has +// passed back to Draft. Daily at 03:00 Europe/Amsterdam (matches the +// scheduler's nightly window for low-traffic state changes). +Schedule::command('artist:demote-expired-options') + ->dailyAt('03:00') + ->timezone('Europe/Amsterdam'); + // Telescope retention — dev-only (mirrors AppServiceProvider's // environment gate). 48h is enough for debugging without filling the // dev database.