feat(ticket): implement validity time management for tickets and catalog items

- Added ValidityTime model and migration to manage ticket validity periods.
- Updated TicketGeneratorService to resolve and assign validity times to tickets.
- Refactored ticket generation logic to remove legacy date fields and use validity time.
- Introduced timezone support for tenants to handle service dates correctly.
- Updated migrations to remove deprecated columns and add foreign keys for validity times.
- Modified seeders and tests to accommodate new validity time structure.
- Enhanced tests to validate ticket generation and validity time behavior.
This commit is contained in:
2026-08-06 15:52:19 -03:00
parent 7f01adab73
commit 448ffb4102
30 changed files with 556 additions and 381 deletions

View File

@@ -51,11 +51,7 @@ class TicketGeneratorServiceTest extends TestCase
public function test_it_generates_tickets_from_a_standard_catalog_item(): void
{
$item = $this->createTicketableItem(
'single-day',
now()->subHour(),
now()->addDay(),
);
$item = $this->createTicketableItem('single-day');
$tickets = $this->service->generate($item, $this->user, 2);
@@ -68,8 +64,8 @@ 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->starts_at->equalTo($item->minimum_use_date));
$this->assertTrue($ticket->expires_at->equalTo($item->maximum_use_date));
$this->assertNull($ticket->validity_time_id);
$this->assertNull($ticket->service_date);
}
}
@@ -84,41 +80,10 @@ class TicketGeneratorServiceTest extends TestCase
$this->service->generate($item->fresh(), $this->user);
}
public function test_it_generates_a_ticket_when_its_maximum_use_date_was_reached(): void
{
$item = $this->createTicketableItem('expired', maximumUseDate: now());
$tickets = $this->service->generate($item, $this->user);
$this->assertCount(1, $tickets);
$this->assertTrue($tickets->first()->expires_at->equalTo($item->maximum_use_date));
}
public function test_variant_dates_override_and_inherit_catalog_item_dates(): void
{
$item = $this->createTicketableItem(
'variant-dates',
now()->subDay(),
now()->addMonth(),
);
$inventory = Inventory::query()->create();
$variant = $item->variants()->create([
'inventory_id' => $inventory->id,
'maximum_use_date' => now()->addWeek(),
]);
$ticket = $this->service
->generate($item, $this->user, sourceVariantId: $variant->id)
->firstOrFail();
$this->assertTrue($ticket->starts_at->equalTo($item->minimum_use_date));
$this->assertTrue($ticket->expires_at->equalTo($variant->maximum_use_date));
}
public function test_it_generates_tickets_for_every_bundle_component_and_quantity(): void
{
$first = $this->createTicketableItem('first', maximumUseDate: now()->addDay());
$second = $this->createTicketableItem('second', maximumUseDate: now()->addDays(2));
$first = $this->createTicketableItem('first');
$second = $this->createTicketableItem('second');
$bundle = $this->createBundle('bundle');
$bundle->bundleComponents()->createMany([
['component_catalog_item_id' => $first->id, 'quantity' => 2],
@@ -134,18 +99,12 @@ class TicketGeneratorServiceTest extends TestCase
$this->assertCount(2, $tickets->where('name', $second->nombre));
}
public function test_bundle_component_uses_its_variant_dates(): void
public function test_bundle_component_preserves_its_source_variant(): void
{
$component = $this->createTicketableItem(
'variant-component',
now()->subDay(),
now()->addMonth(),
);
$component = $this->createTicketableItem('variant-component');
$inventory = Inventory::query()->create();
$variant = $component->variants()->create([
'inventory_id' => $inventory->id,
'minimum_use_date' => now()->addDay(),
'maximum_use_date' => now()->addWeek(),
]);
$bundle = $this->createBundle('variant-bundle');
$bundle->bundleComponents()->create([
@@ -160,8 +119,6 @@ class TicketGeneratorServiceTest extends TestCase
$this->assertSame($component->id, $ticket->source_catalog_item_id);
$this->assertSame($variant->id, $ticket->source_variant_id);
$this->assertTrue($ticket->starts_at->equalTo($variant->minimum_use_date));
$this->assertTrue($ticket->expires_at->equalTo($variant->maximum_use_date));
}
public function test_bundle_generation_is_rolled_back_when_a_component_is_invalid(): void
@@ -244,9 +201,9 @@ class TicketGeneratorServiceTest extends TestCase
->assertJsonPath('data.has_generated_tickets', false);
}
public function test_paid_status_is_confirmed_when_ticket_maximum_use_date_was_reached(): void
public function test_paid_status_is_confirmed_when_ticket_is_generated(): void
{
$item = $this->createTicketableItem('expired-paid-ticket', maximumUseDate: now());
$item = $this->createTicketableItem('paid-ticket');
$purchase = $this->createPurchase($item, 1);
$purchase->markAsPaid();
@@ -258,11 +215,8 @@ class TicketGeneratorServiceTest extends TestCase
]);
}
private function createTicketableItem(
string $slug,
mixed $minimumUseDate = null,
mixed $maximumUseDate = null,
): CatalogItem {
private function createTicketableItem(string $slug): CatalogItem
{
return CatalogItem::query()->create([
'tenant_code' => $this->tenant->codigo,
'slug' => $slug,
@@ -270,8 +224,6 @@ class TicketGeneratorServiceTest extends TestCase
'descripcion' => "Descripción de {$slug}",
'precio' => 10,
'has_tickets' => true,
'minimum_use_date' => $minimumUseDate,
'maximum_use_date' => $maximumUseDate,
]);
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Tests\Feature\Ticket;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
class TicketValiditySchemaTest extends TestCase
{
use RefreshDatabase;
public function test_ticket_validity_time_schema_is_available(): void
{
$this->assertEqualsCanonicalizing([
'id',
'type',
'start_time',
'end_time',
'fixed_starts_at',
'fixed_expires_at',
'active',
'created_at',
'updated_at',
], Schema::getColumnListing('validity_times'));
$this->assertTrue(Schema::hasColumns('tickets', [
'validity_time_id',
'service_date',
]));
$this->assertFalse(Schema::hasColumn('tickets', 'starts_at'));
$this->assertFalse(Schema::hasColumn('tickets', 'expires_at'));
$this->assertTrue(Schema::hasColumn('catalog_items', 'validity_time_id'));
$this->assertFalse(Schema::hasColumn('catalog_items', 'minimum_use_date'));
$this->assertFalse(Schema::hasColumn('catalog_items', 'maximum_use_date'));
$this->assertTrue(Schema::hasColumn('attribute_options', 'validity_time_id'));
$this->assertFalse(Schema::hasColumn('variantes', 'minimum_use_date'));
$this->assertFalse(Schema::hasColumn('variantes', 'maximum_use_date'));
$this->assertTrue(Schema::hasColumn('tenants', 'timezone'));
}
}