feat(event): suspend event dates instead of cancelling
This commit is contained in:
parent
96df431d60
commit
1880fc8147
|
|
@ -56,10 +56,10 @@ class EventController extends Controller
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancelDate(Request $request, EventDate $eventDate): EventDateResource
|
public function suspendDate(Request $request, EventDate $eventDate): EventDateResource
|
||||||
{
|
{
|
||||||
return EventDateResource::make(
|
return EventDateResource::make(
|
||||||
$this->eventService->cancelDateForTenant(
|
$this->eventService->suspendDateForTenant(
|
||||||
$request->user()->tenant()->firstOrFail(),
|
$request->user()->tenant()->firstOrFail(),
|
||||||
$eventDate,
|
$eventDate,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace App\Domains\Event\Enums;
|
||||||
enum EventDateStatus: string
|
enum EventDateStatus: string
|
||||||
{
|
{
|
||||||
case Rescheduled = 'rescheduled';
|
case Rescheduled = 'rescheduled';
|
||||||
case Cancelled = 'cancelled';
|
case Suspended = 'suspended';
|
||||||
case Scheduled = 'scheduled';
|
case Scheduled = 'scheduled';
|
||||||
case InProgress = 'in_progress';
|
case InProgress = 'in_progress';
|
||||||
case Completed = 'completed';
|
case Completed = 'completed';
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use Illuminate\Support\Carbon;
|
||||||
'time_start',
|
'time_start',
|
||||||
'time_end',
|
'time_end',
|
||||||
'rescheduled_to_event_date_id',
|
'rescheduled_to_event_date_id',
|
||||||
'cancelled_at',
|
'suspended_at',
|
||||||
])]
|
])]
|
||||||
class EventDate extends Model
|
class EventDate extends Model
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +58,7 @@ class EventDate extends Model
|
||||||
'date' => 'date:Y-m-d',
|
'date' => 'date:Y-m-d',
|
||||||
'validity_time_id' => 'integer',
|
'validity_time_id' => 'integer',
|
||||||
'rescheduled_to_event_date_id' => 'integer',
|
'rescheduled_to_event_date_id' => 'integer',
|
||||||
'cancelled_at' => 'datetime',
|
'suspended_at' => 'datetime',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,8 +119,8 @@ class EventDate extends Model
|
||||||
return EventDateStatus::Rescheduled;
|
return EventDateStatus::Rescheduled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->cancelled_at !== null) {
|
if ($this->suspended_at !== null) {
|
||||||
return EventDateStatus::Cancelled;
|
return EventDateStatus::Suspended;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now()->lt($this->startsAt())) {
|
if (now()->lt($this->startsAt())) {
|
||||||
|
|
@ -146,7 +146,7 @@ class EventDate extends Model
|
||||||
'event_date_text' => app(EventDateTextFormatter::class)->format(
|
'event_date_text' => app(EventDateTextFormatter::class)->format(
|
||||||
$tenant->eventDates()
|
$tenant->eventDates()
|
||||||
->whereNull('rescheduled_to_event_date_id')
|
->whereNull('rescheduled_to_event_date_id')
|
||||||
->whereNull('cancelled_at')
|
->whereNull('suspended_at')
|
||||||
->pluck('date')
|
->pluck('date')
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class EventDateResource extends JsonResource
|
||||||
'end_time' => substr($this->time_end, 0, 5),
|
'end_time' => substr($this->time_end, 0, 5),
|
||||||
'status' => $this->status->value,
|
'status' => $this->status->value,
|
||||||
'rescheduled_to_event_date_id' => $this->rescheduled_to_event_date_id,
|
'rescheduled_to_event_date_id' => $this->rescheduled_to_event_date_id,
|
||||||
'cancelled_at' => $this->cancelled_at?->toISOString(),
|
'suspended_at' => $this->suspended_at?->toISOString(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class EffectiveEventDateResolver
|
||||||
$visited[$identity] = true;
|
$visited[$identity] = true;
|
||||||
|
|
||||||
if ($current->rescheduled_to_event_date_id === null) {
|
if ($current->rescheduled_to_event_date_id === null) {
|
||||||
return $current->cancelled_at === null ? $current : null;
|
return $current->suspended_at === null ? $current : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$current->loadMissing('rescheduledTo');
|
$current->loadMissing('rescheduledTo');
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,9 @@ class EventService
|
||||||
return DB::transaction(function () use ($tenant, $eventDate, $data): EventDate {
|
return DB::transaction(function () use ($tenant, $eventDate, $data): EventDate {
|
||||||
$source = $this->lockedDateForTenant($tenant, $eventDate);
|
$source = $this->lockedDateForTenant($tenant, $eventDate);
|
||||||
|
|
||||||
if ($source->cancelled_at !== null) {
|
if ($source->suspended_at !== null) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'event_date' => ['No se puede reprogramar una fecha cancelada.'],
|
'event_date' => ['No se puede reprogramar una fecha suspendida.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,22 +111,22 @@ class EventService
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancelDateForTenant(Tenant $tenant, EventDate $eventDate): EventDate
|
public function suspendDateForTenant(Tenant $tenant, EventDate $eventDate): EventDate
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($tenant, $eventDate): EventDate {
|
return DB::transaction(function () use ($tenant, $eventDate): EventDate {
|
||||||
$date = $this->lockedDateForTenant($tenant, $eventDate);
|
$date = $this->lockedDateForTenant($tenant, $eventDate);
|
||||||
|
|
||||||
if ($date->rescheduled_to_event_date_id !== null) {
|
if ($date->rescheduled_to_event_date_id !== null) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'event_date' => ['No se puede cancelar una fecha que ya fue reprogramada.'],
|
'event_date' => ['No se puede suspender una fecha que ya fue reprogramada.'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($date->cancelled_at !== null) {
|
if ($date->suspended_at !== null) {
|
||||||
return $date->load('validityTime');
|
return $date->load('validityTime');
|
||||||
}
|
}
|
||||||
|
|
||||||
$date->update(['cancelled_at' => now()]);
|
$date->update(['suspended_at' => now()]);
|
||||||
$this->disableTicketsWithoutUsableDates($tenant, $date);
|
$this->disableTicketsWithoutUsableDates($tenant, $date);
|
||||||
|
|
||||||
return $date->fresh('validityTime');
|
return $date->fresh('validityTime');
|
||||||
|
|
@ -166,9 +166,9 @@ class EventService
|
||||||
return $current->is($expected);
|
return $current->is($expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function disableTicketsWithoutUsableDates(Tenant $tenant, EventDate $cancelledDate): void
|
private function disableTicketsWithoutUsableDates(Tenant $tenant, EventDate $suspendedDate): void
|
||||||
{
|
{
|
||||||
$affectedDateIds = collect([$cancelledDate->getKey()]);
|
$affectedDateIds = collect([$suspendedDate->getKey()]);
|
||||||
$frontier = $affectedDateIds;
|
$frontier = $affectedDateIds;
|
||||||
|
|
||||||
while ($frontier->isNotEmpty()) {
|
while ($frontier->isNotEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ Route::prefix('v1/adminapp/tenant')
|
||||||
Route::put('event', [EventController::class, 'update']);
|
Route::put('event', [EventController::class, 'update']);
|
||||||
Route::post('event-dates', [EventController::class, 'storeDate']);
|
Route::post('event-dates', [EventController::class, 'storeDate']);
|
||||||
Route::post('event-dates/{eventDate}/reschedule', [EventController::class, 'rescheduleDate']);
|
Route::post('event-dates/{eventDate}/reschedule', [EventController::class, 'rescheduleDate']);
|
||||||
Route::post('event-dates/{eventDate}/cancel', [EventController::class, 'cancelDate']);
|
Route::post('event-dates/{eventDate}/suspend', [EventController::class, 'suspendDate']);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('event_dates', function (Blueprint $table): void {
|
||||||
|
$table->renameColumn('cancelled_at', 'suspended_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('event_dates', function (Blueprint $table): void {
|
||||||
|
$table->renameColumn('suspended_at', 'cancelled_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -216,11 +216,11 @@ class AdminAppEventControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_cancelling_disables_only_tickets_without_another_usable_date(): void
|
public function test_suspending_disables_only_tickets_without_another_usable_date(): void
|
||||||
{
|
{
|
||||||
$tenant = $this->createTenant('acme');
|
$tenant = $this->createTenant('acme');
|
||||||
$admin = $this->createAdminAppUser($tenant);
|
$admin = $this->createAdminAppUser($tenant);
|
||||||
$cancelledDate = $tenant->eventDates()->create([
|
$suspendedDate = $tenant->eventDates()->create([
|
||||||
'date' => '2027-10-09',
|
'date' => '2027-10-09',
|
||||||
'time_start' => '09:00',
|
'time_start' => '09:00',
|
||||||
'time_end' => '18:30',
|
'time_end' => '18:30',
|
||||||
|
|
@ -230,16 +230,17 @@ class AdminAppEventControllerTest extends TestCase
|
||||||
'time_start' => '09:00',
|
'time_start' => '09:00',
|
||||||
'time_end' => '18:30',
|
'time_end' => '18:30',
|
||||||
]);
|
]);
|
||||||
$singleDateVariant = $this->createVariant($tenant, $cancelledDate->id);
|
$singleDateVariant = $this->createVariant($tenant, $suspendedDate->id);
|
||||||
$multipleDateVariant = $this->createVariant($tenant);
|
$multipleDateVariant = $this->createVariant($tenant);
|
||||||
$multipleDateVariant->eventDates()->sync([$cancelledDate->id, $otherDate->id]);
|
$multipleDateVariant->eventDates()->sync([$suspendedDate->id, $otherDate->id]);
|
||||||
$singleDateTicket = $this->createTicket($tenant, $admin, $singleDateVariant);
|
$singleDateTicket = $this->createTicket($tenant, $admin, $singleDateVariant);
|
||||||
$multipleDateTicket = $this->createTicket($tenant, $admin, $multipleDateVariant);
|
$multipleDateTicket = $this->createTicket($tenant, $admin, $multipleDateVariant);
|
||||||
Sanctum::actingAs($admin);
|
Sanctum::actingAs($admin);
|
||||||
|
|
||||||
$this->postJson("/api/v1/adminapp/tenant/event-dates/{$cancelledDate->id}/cancel")
|
$this->postJson("/api/v1/adminapp/tenant/event-dates/{$suspendedDate->id}/suspend")
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertJsonPath('data.status', 'cancelled');
|
->assertJsonPath('data.status', 'suspended')
|
||||||
|
->assertJsonPath('data.suspended_at', fn ($value) => is_string($value));
|
||||||
|
|
||||||
$this->assertNotNull($singleDateTicket->fresh()->disabled_at);
|
$this->assertNotNull($singleDateTicket->fresh()->disabled_at);
|
||||||
$this->assertNull($multipleDateTicket->fresh()->disabled_at);
|
$this->assertNull($multipleDateTicket->fresh()->disabled_at);
|
||||||
|
|
@ -249,7 +250,7 @@ class AdminAppEventControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_cancelling_a_reschedule_destination_disables_tickets_from_predecessor_dates(): void
|
public function test_suspending_a_reschedule_destination_disables_tickets_from_predecessor_dates(): void
|
||||||
{
|
{
|
||||||
$tenant = $this->createTenant('acme');
|
$tenant = $this->createTenant('acme');
|
||||||
$admin = $this->createAdminAppUser($tenant);
|
$admin = $this->createAdminAppUser($tenant);
|
||||||
|
|
@ -271,7 +272,7 @@ class AdminAppEventControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
Sanctum::actingAs($admin);
|
Sanctum::actingAs($admin);
|
||||||
|
|
||||||
$this->postJson("/api/v1/adminapp/tenant/event-dates/{$destination->id}/cancel")
|
$this->postJson("/api/v1/adminapp/tenant/event-dates/{$destination->id}/suspend")
|
||||||
->assertOk();
|
->assertOk();
|
||||||
|
|
||||||
$this->assertNotNull($ticket->fresh()->disabled_at);
|
$this->assertNotNull($ticket->fresh()->disabled_at);
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,8 @@ class EventModelsTest extends TestCase
|
||||||
$eventDate->date = '2026-10-08';
|
$eventDate->date = '2026-10-08';
|
||||||
$this->assertSame(EventDateStatus::Completed, $eventDate->status);
|
$this->assertSame(EventDateStatus::Completed, $eventDate->status);
|
||||||
|
|
||||||
$eventDate->cancelled_at = now();
|
$eventDate->suspended_at = now();
|
||||||
$this->assertSame(EventDateStatus::Cancelled, $eventDate->status);
|
$this->assertSame(EventDateStatus::Suspended, $eventDate->status);
|
||||||
|
|
||||||
$eventDate->rescheduled_to_event_date_id = 123;
|
$eventDate->rescheduled_to_event_date_id = 123;
|
||||||
$this->assertSame(EventDateStatus::Rescheduled, $eventDate->status);
|
$this->assertSame(EventDateStatus::Rescheduled, $eventDate->status);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue