diff --git a/app/Domains/Event/Enums/EventDateStatus.php b/app/Domains/Event/Enums/EventDateStatus.php new file mode 100644 index 0000000..591b7c2 --- /dev/null +++ b/app/Domains/Event/Enums/EventDateStatus.php @@ -0,0 +1,12 @@ + $eventDate->syncValidityTime()); @@ -52,6 +57,8 @@ class EventDate extends Model return [ 'date' => 'date:Y-m-d', 'validity_time_id' => 'integer', + 'rescheduled_to_event_date_id' => 'integer', + 'cancelled_at' => 'datetime', ]; } @@ -67,6 +74,18 @@ class EventDate extends Model return $this->belongsTo(ValidityTime::class); } + /** @return BelongsTo */ + public function rescheduledTo(): BelongsTo + { + return $this->belongsTo(self::class, 'rescheduled_to_event_date_id'); + } + + /** @return HasMany */ + public function rescheduledFrom(): HasMany + { + return $this->hasMany(self::class, 'rescheduled_to_event_date_id'); + } + /** @return HasMany */ public function variants(): HasMany { @@ -94,6 +113,27 @@ class EventDate extends Model return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_end); } + public function getStatusAttribute(): EventDateStatus + { + if ($this->rescheduled_to_event_date_id !== null) { + return EventDateStatus::Rescheduled; + } + + if ($this->cancelled_at !== null) { + return EventDateStatus::Cancelled; + } + + if (now()->lt($this->startsAt())) { + return EventDateStatus::Scheduled; + } + + if (now()->lt($this->endsAt())) { + return EventDateStatus::InProgress; + } + + return EventDateStatus::Completed; + } + private function syncTenantDateText(): void { $tenant = $this->tenant()->first(); @@ -104,7 +144,10 @@ class EventDate extends Model $tenant->update([ 'event_date_text' => app(EventDateTextFormatter::class)->format( - $tenant->eventDates()->pluck('date') + $tenant->eventDates() + ->whereNull('rescheduled_to_event_date_id') + ->whereNull('cancelled_at') + ->pluck('date') ), ]); } diff --git a/database/migrations/2026_09_11_000000_add_rescheduling_state_to_event_dates.php b/database/migrations/2026_09_11_000000_add_rescheduling_state_to_event_dates.php new file mode 100644 index 0000000..54c999f --- /dev/null +++ b/database/migrations/2026_09_11_000000_add_rescheduling_state_to_event_dates.php @@ -0,0 +1,30 @@ +foreignId('rescheduled_to_event_date_id') + ->nullable() + ->after('validity_time_id') + ->constrained('event_dates') + ->restrictOnDelete(); + $table->dateTime('cancelled_at') + ->nullable() + ->after('rescheduled_to_event_date_id'); + }); + } + + public function down(): void + { + Schema::table('event_dates', function (Blueprint $table): void { + $table->dropConstrainedForeignId('rescheduled_to_event_date_id'); + $table->dropColumn('cancelled_at'); + }); + } +}; diff --git a/tests/Unit/Event/EventModelsTest.php b/tests/Unit/Event/EventModelsTest.php index 0650596..459aa8e 100644 --- a/tests/Unit/Event/EventModelsTest.php +++ b/tests/Unit/Event/EventModelsTest.php @@ -3,9 +3,11 @@ namespace Tests\Unit\Event; use App\Domains\Catalog\Models\Variant; +use App\Domains\Event\Enums\EventDateStatus; use App\Domains\Event\Models\EventDate; use App\Domains\Tenant\Models\Tenant; use App\Domains\Ticket\Models\ValidityTime; +use Illuminate\Support\Carbon; use Tests\TestCase; class EventModelsTest extends TestCase @@ -30,6 +32,37 @@ class EventModelsTest extends TestCase $this->assertInstanceOf(ValidityTime::class, $eventDate->validityTime()->getRelated()); $this->assertInstanceOf(EventDate::class, (new ValidityTime)->eventDate()->getRelated()); $this->assertInstanceOf(Variant::class, $eventDate->variants()->getRelated()); + $this->assertInstanceOf(EventDate::class, $eventDate->rescheduledTo()->getRelated()); + $this->assertInstanceOf(EventDate::class, $eventDate->rescheduledFrom()->getRelated()); + } + + public function test_event_date_status_is_computed_in_business_priority_order(): void + { + Carbon::setTestNow('2026-10-09 10:00:00'); + + try { + $eventDate = new EventDate([ + 'date' => '2026-10-09', + 'time_start' => '09:00:00', + 'time_end' => '18:30:00', + ]); + + $this->assertSame(EventDateStatus::InProgress, $eventDate->status); + + $eventDate->date = '2026-10-10'; + $this->assertSame(EventDateStatus::Scheduled, $eventDate->status); + + $eventDate->date = '2026-10-08'; + $this->assertSame(EventDateStatus::Completed, $eventDate->status); + + $eventDate->cancelled_at = now(); + $this->assertSame(EventDateStatus::Cancelled, $eventDate->status); + + $eventDate->rescheduled_to_event_date_id = 123; + $this->assertSame(EventDateStatus::Rescheduled, $eventDate->status); + } finally { + Carbon::setTestNow(); + } } public function test_tenant_has_many_event_dates(): void