From 106cf017dcc19a926a1143474e84ba3e8e6c26bf Mon Sep 17 00:00:00 2001 From: ncoronel Date: Fri, 11 Sep 2026 12:28:37 -0300 Subject: [PATCH] feat(ticket): resolve effective event dates and handle reschedule chains --- .../Services/EffectiveEventDateResolver.php | 37 +++++++++++++++++++ .../Services/TicketValidityResolver.php | 23 +++++++++++- .../Ticket/TicketGeneratorServiceTest.php | 4 +- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 app/Domains/Event/Services/EffectiveEventDateResolver.php diff --git a/app/Domains/Event/Services/EffectiveEventDateResolver.php b/app/Domains/Event/Services/EffectiveEventDateResolver.php new file mode 100644 index 0000000..4606da0 --- /dev/null +++ b/app/Domains/Event/Services/EffectiveEventDateResolver.php @@ -0,0 +1,37 @@ +getKey() === null + ? 'object:'.spl_object_id($current) + : 'key:'.$current->getKey(); + + if (isset($visited[$identity])) { + return null; + } + + $visited[$identity] = true; + + if ($current->rescheduled_to_event_date_id === null) { + return $current->cancelled_at === null ? $current : null; + } + + $current->loadMissing('rescheduledTo'); + $current = $current->rescheduledTo; + + if ($current === null) { + return null; + } + } + } +} diff --git a/app/Domains/Ticket/Services/TicketValidityResolver.php b/app/Domains/Ticket/Services/TicketValidityResolver.php index 0c0c34d..69714d5 100644 --- a/app/Domains/Ticket/Services/TicketValidityResolver.php +++ b/app/Domains/Ticket/Services/TicketValidityResolver.php @@ -4,6 +4,8 @@ namespace App\Domains\Ticket\Services; use App\Domains\Catalog\Models\Variant; use App\Domains\Catalog\Models\VariantDefinition; +use App\Domains\Event\Models\EventDate; +use App\Domains\Event\Services\EffectiveEventDateResolver; use App\Domains\Ticket\Models\Ticket; use App\Domains\Ticket\Models\ValidityTime; use Illuminate\Support\Collection; @@ -17,6 +19,14 @@ use Illuminate\Support\Collection; */ class TicketValidityResolver { + private readonly EffectiveEventDateResolver $effectiveEventDateResolver; + + public function __construct(?EffectiveEventDateResolver $effectiveEventDateResolver = null) + { + $this->effectiveEventDateResolver = $effectiveEventDateResolver + ?? new EffectiveEventDateResolver; + } + /** Relaciones necesarias para resolver tickets sin consultas N+1. */ public const RELATIONS = [ 'sourceVariant.eventDates.validityTime', @@ -56,7 +66,18 @@ class TicketValidityResolver ]); $dimensions = collect(); - $eventDates = $variant->selectedEventDates(); + $selectedEventDates = $variant->selectedEventDates(); + $eventDates = $selectedEventDates + ->map(fn (EventDate $eventDate): ?EventDate => $this->effectiveEventDateResolver->resolve($eventDate)) + ->filter() + ->unique(fn (EventDate $eventDate): int => $eventDate->getKey() ?? spl_object_id($eventDate)) + ->values(); + + if ($selectedEventDates->isNotEmpty() && $eventDates->isEmpty()) { + return ResolvedTicketValidity::unresolvable(); + } + + $eventDates->each->loadMissing('validityTime'); if ($eventDates->contains(fn ($eventDate): bool => $eventDate->validityTime === null)) { return ResolvedTicketValidity::unresolvable(); diff --git a/tests/Feature/Ticket/TicketGeneratorServiceTest.php b/tests/Feature/Ticket/TicketGeneratorServiceTest.php index b1415cc..5edbf37 100644 --- a/tests/Feature/Ticket/TicketGeneratorServiceTest.php +++ b/tests/Feature/Ticket/TicketGeneratorServiceTest.php @@ -471,7 +471,9 @@ class TicketGeneratorServiceTest extends TestCase $this->assertCount(2, $ticket->resolvedValidityGroups()); $this->assertTrue($ticket->resolvedValidityGroups()->every( fn ($group): bool => $group->validityTimes->count() === 2 - && $group->validityTimes->contains($lunch) + && $group->validityTimes->contains( + fn (ValidityTime $validityTime): bool => $validityTime->is($lunch) + ) )); $this->assertEqualsCanonicalizing( $dates->pluck('validity_time_id')->all(),