Refactor ticket validity handling and improve tests
- Updated tests for Accommodation, Entry, Food, Merchandise, and Sale controllers to use soft deletes for variants and ensure proper inventory counts. - Enhanced ticket generation logic to resolve validity from soft-deleted catalog sources. - Introduced a new TicketValidityResolver service to manage ticket validity based on event dates and variant definitions. - Removed unnecessary database assertions and improved the clarity of validity checks in tests. - Added comprehensive tests for the new TicketValidityResolver service, ensuring correct handling of event dates and multi-select options. - Cleaned up unused code and assertions in existing tests for better maintainability.
This commit is contained in:
@@ -9,12 +9,12 @@ use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Catalog\Services\CatalogService;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Notification\Events\TicketsAvailable;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Shared\Enums\FieldType;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Enums\TicketGenerationPolicy;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Exceptions\TicketGenerationException;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
@@ -70,8 +70,14 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
$this->assertSame($item->descripcion, $ticket->description);
|
||||
$this->assertSame($item->id, $ticket->source_catalog_item_id);
|
||||
$this->assertNull($ticket->source_variant_id);
|
||||
$this->assertTrue($ticket->validityGroups->isEmpty());
|
||||
$this->assertTrue($ticket->resolvedValidityGroups()->isEmpty());
|
||||
}
|
||||
|
||||
$this->assertDatabaseHas('tickets', [
|
||||
'id' => $tickets->first()->id,
|
||||
'name' => null,
|
||||
'description' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_rejects_an_item_without_tickets_enabled(): void
|
||||
@@ -98,6 +104,10 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
'value' => 'black',
|
||||
'label' => 'Negro',
|
||||
]);
|
||||
$color->options()->create([
|
||||
'value' => 'blue',
|
||||
'label' => 'Azul',
|
||||
]);
|
||||
$size = Attribute::query()->create([
|
||||
'tenant_codigo' => $this->tenant->codigo,
|
||||
'codigo' => 'size',
|
||||
@@ -118,17 +128,33 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
]);
|
||||
$variant = $item->variants()->create([
|
||||
'inventory_id' => Inventory::query()->create()->id,
|
||||
'descripcion' => 'Descripción de la variante',
|
||||
]);
|
||||
$variant->definitions()->createMany([
|
||||
['item_attribute_id' => $itemColor->id, 'value' => 'black'],
|
||||
['item_attribute_id' => $itemSize->id, 'value' => 'xl'],
|
||||
]);
|
||||
$colorDefinition = $variant->definitions()
|
||||
->where('item_attribute_id', $itemColor->id)
|
||||
->sole();
|
||||
|
||||
$ticket = $this->service
|
||||
->generate($item, $this->user, 1, $variant->id)
|
||||
->sole();
|
||||
|
||||
$this->assertSame('Shirt (Negro, XL)', $ticket->name);
|
||||
$this->assertSame('Descripción de la variante', $ticket->description);
|
||||
|
||||
$item->update([
|
||||
'nombre' => 'Remera',
|
||||
'descripcion' => 'Descripción actualizada del producto',
|
||||
]);
|
||||
$colorDefinition->update(['value' => 'blue']);
|
||||
$variant->update(['descripcion' => 'Descripción actualizada de la variante']);
|
||||
$ticket = $ticket->fresh();
|
||||
|
||||
$this->assertSame('Remera (Azul, XL)', $ticket->name);
|
||||
$this->assertSame('Descripción actualizada de la variante', $ticket->description);
|
||||
}
|
||||
|
||||
public function test_it_generates_tickets_for_every_bundle_component_and_quantity(): void
|
||||
@@ -273,9 +299,9 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
$tickets = $this->service->generate($item, $this->user, 2, $variant->id);
|
||||
|
||||
$this->assertCount(2, $tickets);
|
||||
$this->assertTrue($tickets->every(fn ($ticket): bool => $ticket->validityGroups->count() === 1));
|
||||
$this->assertTrue($tickets->every(fn ($ticket): bool => $ticket->resolvedValidityGroups()->count() === 1));
|
||||
$this->assertTrue($tickets->every(
|
||||
fn ($ticket): bool => $ticket->validityGroups->sole()->validityTimes
|
||||
fn ($ticket): bool => $ticket->resolvedValidityGroups()->sole()->validityTimes
|
||||
->pluck('id')
|
||||
->sort()
|
||||
->values()
|
||||
@@ -291,7 +317,7 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function test_a_multi_date_variant_generates_one_ticket_for_each_selected_date(): void
|
||||
public function test_it_generates_one_ticket_per_unit_covering_all_selected_dates(): void
|
||||
{
|
||||
$item = $this->createTicketableItem('multi-date-pass');
|
||||
$dates = collect(['2026-08-20', '2026-08-21'])->map(fn (string $date) => EventDate::query()->create([
|
||||
@@ -305,61 +331,27 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
]);
|
||||
$variant->eventDates()->sync($dates->pluck('id'));
|
||||
|
||||
$tickets = $this->service->generate($item, $this->user, 1, $variant->id);
|
||||
$tickets->each->loadMissing('validityGroups.validityTimes');
|
||||
|
||||
$this->assertCount(2, $tickets);
|
||||
$this->assertSame(
|
||||
$dates->pluck('validity_time_id')->all(),
|
||||
$tickets->map(fn ($ticket): int => $ticket->validityGroups->sole()->validityTimes->sole()->id)->all(),
|
||||
);
|
||||
$this->assertSame(
|
||||
['2026-08-20 00:00:00', '2026-08-21 00:00:00'],
|
||||
$tickets->map(fn ($ticket): string => $ticket->validityGroups->sole()->validityTimes->sole()->fixed_starts_at->format('Y-m-d H:i:s'))->all(),
|
||||
);
|
||||
$this->assertSame(
|
||||
['Multi-date-pass (20/08/2026)', 'Multi-date-pass (21/08/2026)'],
|
||||
$tickets->pluck('name')->all(),
|
||||
);
|
||||
$this->assertSame([$variant->id], $tickets->pluck('source_variant_id')->unique()->values()->all());
|
||||
}
|
||||
|
||||
public function test_one_per_unit_policy_generates_one_ticket_covering_all_selected_dates(): void
|
||||
{
|
||||
$item = $this->createTicketableItem('multi-date-pass');
|
||||
$item->update([
|
||||
'ticket_generation_policy' => TicketGenerationPolicy::OnePerUnit,
|
||||
]);
|
||||
$dates = collect(['2026-08-20', '2026-08-21'])->map(fn (string $date) => EventDate::query()->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'date' => $date,
|
||||
'time_start' => '00:00:00',
|
||||
'time_end' => '23:59:59',
|
||||
]));
|
||||
$variant = $item->variants()->create([
|
||||
'inventory_id' => Inventory::query()->create()->id,
|
||||
]);
|
||||
$variant->eventDates()->sync($dates->pluck('id'));
|
||||
|
||||
$tickets = $this->service->generate($item, $this->user, 2, $variant->id);
|
||||
$tickets->each->loadMissing('validityGroups.validityTimes');
|
||||
|
||||
$this->assertCount(2, $tickets);
|
||||
$this->assertTrue($tickets->every(fn ($ticket): bool => $ticket->validityGroups->count() === 2));
|
||||
$this->assertTrue($tickets->every(fn ($ticket): bool => $ticket->resolvedValidityGroups()->count() === 2));
|
||||
$this->assertTrue($tickets->every(
|
||||
fn ($ticket): bool => $ticket->validityGroups->every(
|
||||
fn ($ticket): bool => $ticket->resolvedValidityGroups()->every(
|
||||
fn ($group): bool => $group->validityTimes->count() === 1
|
||||
)
|
||||
));
|
||||
$this->assertEqualsCanonicalizing(
|
||||
$dates->pluck('validity_time_id')->all(),
|
||||
$tickets->flatMap(fn ($ticket) => $ticket->allValidityTimes())->pluck('id')->unique()->all(),
|
||||
$tickets->flatMap(
|
||||
fn ($ticket) => $ticket->resolvedValidityGroups()
|
||||
->flatMap(fn ($group) => $group->validityTimes)
|
||||
)->pluck('id')->unique()->all(),
|
||||
);
|
||||
$this->assertTrue($tickets->every(
|
||||
fn ($ticket): bool => $ticket->name === 'Multi-date-pass (20/08/2026, 21/08/2026)'
|
||||
));
|
||||
$this->assertTrue($tickets->every(
|
||||
fn ($ticket): bool => $ticket->allValidityTimes()
|
||||
fn ($ticket): bool => $ticket->resolvedValidityGroups()
|
||||
->flatMap(fn ($group) => $group->validityTimes)
|
||||
->map(fn (ValidityTime $validityTime): array => [
|
||||
$validityTime->fixed_starts_at->format('Y-m-d H:i:s'),
|
||||
$validityTime->fixed_expires_at->format('Y-m-d H:i:s'),
|
||||
@@ -371,10 +363,36 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
));
|
||||
}
|
||||
|
||||
public function test_ticket_validity_is_resolved_from_soft_deleted_catalog_sources(): void
|
||||
{
|
||||
$item = $this->createTicketableItem('archived-ticket-source');
|
||||
$eventDate = EventDate::query()->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'date' => '2026-07-21',
|
||||
'time_start' => '00:00:00',
|
||||
'time_end' => '23:59:59',
|
||||
]);
|
||||
$variant = $item->variants()->create([
|
||||
'inventory_id' => Inventory::query()->create()->id,
|
||||
]);
|
||||
$variant->eventDates()->attach($eventDate);
|
||||
$ticket = $this->service->generate($item, $this->user, 1, $variant->id)->sole();
|
||||
|
||||
app(CatalogService::class)->delete($item);
|
||||
$ticket = $ticket->fresh();
|
||||
|
||||
$this->assertTrue($ticket->sourceCatalogItem->trashed());
|
||||
$this->assertTrue($ticket->sourceVariant->trashed());
|
||||
$this->assertTrue($ticket->isValid());
|
||||
$this->assertSame(
|
||||
'2026-07-21 23:59:59',
|
||||
$ticket->getEffectiveExpiresAt()->format('Y-m-d H:i:s'),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_common_schedule_is_anded_into_each_alternative_event_date_group(): void
|
||||
{
|
||||
$item = $this->createTicketableItem('multi-date-lunch');
|
||||
$item->update(['ticket_generation_policy' => TicketGenerationPolicy::OnePerUnit]);
|
||||
$dates = collect(['2026-08-20', '2026-08-21'])->map(fn (string $date) => EventDate::query()->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'date' => $date,
|
||||
@@ -408,16 +426,14 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
]);
|
||||
|
||||
$ticket = $this->service->generate($item, $this->user, 1, $variant->id)->sole();
|
||||
$ticket->loadMissing('validityGroups.validityTimes');
|
||||
|
||||
$this->assertCount(2, $ticket->validityGroups);
|
||||
$this->assertTrue($ticket->validityGroups->every(
|
||||
$this->assertCount(2, $ticket->resolvedValidityGroups());
|
||||
$this->assertTrue($ticket->resolvedValidityGroups()->every(
|
||||
fn ($group): bool => $group->validityTimes->count() === 2
|
||||
&& $group->validityTimes->contains($lunch)
|
||||
));
|
||||
$this->assertEqualsCanonicalizing(
|
||||
$dates->pluck('validity_time_id')->all(),
|
||||
$ticket->validityGroups
|
||||
$ticket->resolvedValidityGroups()
|
||||
->flatMap->validityTimes
|
||||
->reject(fn (ValidityTime $validityTime): bool => $validityTime->is($lunch))
|
||||
->pluck('id')
|
||||
|
||||
Reference in New Issue
Block a user