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:
2026-08-14 09:00:51 -03:00
parent bfdaf1c38b
commit 573d4fe5e6
59 changed files with 1185 additions and 679 deletions

View File

@@ -1,6 +1,5 @@
<?php
use App\Domains\Ticket\Enums\TicketGenerationPolicy;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
@@ -11,15 +10,15 @@ return new class extends Migration
public function up(): void
{
Schema::table('catalog_items', function (Blueprint $table): void {
$table->enum('ticket_generation_policy', TicketGenerationPolicy::values())
->default(TicketGenerationPolicy::PerEventDate->value)
$table->enum('ticket_generation_policy', ['per_event_date', 'one_per_unit'])
->default('per_event_date')
->after('has_tickets');
});
DB::table('catalog_items')
->where('tenant_code', 'fiesta_futbol_infantil')
->update([
'ticket_generation_policy' => TicketGenerationPolicy::OnePerUnit->value,
'ticket_generation_policy' => 'one_per_unit',
]);
}

View File

@@ -0,0 +1,66 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
DB::table('catalog_items')
->join('event_dates', function ($join): void {
$join->on('event_dates.tenant_code', '=', 'catalog_items.tenant_code')
->on('event_dates.validity_time_id', '=', 'catalog_items.validity_time_id');
})
->whereNotNull('catalog_items.validity_time_id')
->select([
'catalog_items.id as catalog_item_id',
'event_dates.id as event_date_id',
])
->orderBy('catalog_items.id')
->orderBy('event_dates.id')
->each(function (object $association): void {
$variantIds = DB::table('variantes')
->where('catalog_item_id', $association->catalog_item_id)
->pluck('id');
if ($variantIds->isEmpty()) {
return;
}
DB::table('variantes')
->whereIn('id', $variantIds)
->whereNull('event_date_id')
->update(['event_date_id' => $association->event_date_id]);
DB::table('variant_event_dates')->insertOrIgnore(
$variantIds->map(fn (int $variantId): array => [
'variant_id' => $variantId,
'event_date_id' => $association->event_date_id,
])->all()
);
});
Schema::table('catalog_items', function (Blueprint $table): void {
if (DB::getDriverName() !== 'sqlite') {
$table->dropForeign(['validity_time_id']);
}
$table->dropColumn('validity_time_id');
});
}
public function down(): void
{
Schema::table('catalog_items', function (Blueprint $table): void {
$table->foreignId('validity_time_id')
->nullable()
->after('has_tickets')
->constrained('validity_times')
->cascadeOnUpdate()
->nullOnDelete();
});
}
};

View File

@@ -0,0 +1,37 @@
<?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::dropIfExists('ticket_validity_group_times');
Schema::dropIfExists('ticket_validity_groups');
}
public function down(): void
{
Schema::create('ticket_validity_groups', function (Blueprint $table): void {
$table->id();
$table->foreignId('ticket_id')
->constrained('tickets')
->cascadeOnUpdate()
->cascadeOnDelete();
});
Schema::create('ticket_validity_group_times', function (Blueprint $table): void {
$table->foreignId('ticket_validity_group_id')
->constrained('ticket_validity_groups')
->cascadeOnUpdate()
->cascadeOnDelete();
$table->foreignId('validity_time_id')
->constrained('validity_times')
->cascadeOnUpdate()
->restrictOnDelete();
$table->primary(['ticket_validity_group_id', 'validity_time_id']);
});
}
};

View File

@@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (DB::getDriverName() === 'sqlite') {
return;
}
Schema::table('tickets', function (Blueprint $table): void {
$table->dropForeign(['source_catalog_item_id']);
$table->dropForeign(['source_variant_id']);
$table->foreign('source_catalog_item_id')
->references('id')
->on('catalog_items')
->cascadeOnUpdate()
->restrictOnDelete();
$table->foreign('source_variant_id')
->references('id')
->on('variantes')
->cascadeOnUpdate()
->restrictOnDelete();
});
}
public function down(): void
{
if (DB::getDriverName() === 'sqlite') {
return;
}
Schema::table('tickets', function (Blueprint $table): void {
$table->dropForeign(['source_catalog_item_id']);
$table->dropForeign(['source_variant_id']);
$table->foreign('source_catalog_item_id')
->references('id')
->on('catalog_items')
->cascadeOnUpdate()
->nullOnDelete();
$table->foreign('source_variant_id')
->references('id')
->on('variantes')
->cascadeOnUpdate()
->nullOnDelete();
});
}
};

View File

@@ -0,0 +1,24 @@
<?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('catalog_items', function (Blueprint $table): void {
$table->dropColumn('ticket_generation_policy');
});
}
public function down(): void
{
Schema::table('catalog_items', function (Blueprint $table): void {
$table->enum('ticket_generation_policy', ['one_per_unit'])
->default('one_per_unit')
->after('has_tickets');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('tickets', function (Blueprint $table): void {
$table->string('name')->nullable()->change();
$table->text('description')->nullable()->change();
});
}
public function down(): void
{
DB::table('tickets')->whereNull('name')->update(['name' => '']);
DB::table('tickets')->whereNull('description')->update(['description' => '']);
Schema::table('tickets', function (Blueprint $table): void {
$table->string('name')->nullable(false)->change();
$table->text('description')->nullable(false)->change();
});
}
};

View File

@@ -0,0 +1,30 @@
<?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('catalog_items', function (Blueprint $table): void {
$table->softDeletes();
});
Schema::table('variantes', function (Blueprint $table): void {
$table->softDeletes();
});
}
public function down(): void
{
Schema::table('variantes', function (Blueprint $table): void {
$table->dropSoftDeletes();
});
Schema::table('catalog_items', function (Blueprint $table): void {
$table->dropSoftDeletes();
});
}
};

View File

@@ -12,7 +12,6 @@ use App\Domains\Catalog\Models\FeaturedGroup;
use App\Domains\Catalog\Services\CatalogService;
use App\Domains\Event\Models\EventDate;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Ticket\Enums\TicketGenerationPolicy;
use App\Domains\Ticket\Enums\ValidityTimeType;
use App\Domains\Ticket\Models\ValidityTime;
use Illuminate\Database\Seeder;
@@ -155,6 +154,7 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
CatalogItem::query()
->where('tenant_code', $tenant->codigo)
->orderByRaw("CASE WHEN type = 'bundle' THEN 0 ELSE 1 END")
->orderBy('id')
->each(fn (CatalogItem $item) => $this->catalogService->delete($item));
}
@@ -167,7 +167,6 @@ class FiestaFutbolInfantilProductSeeder extends Seeder
'inventory_policy' => InventoryPolicy::Tracked->value,
...$data,
'has_tickets' => true,
'ticket_generation_policy' => TicketGenerationPolicy::OnePerUnit->value,
]);
}

View File

@@ -241,14 +241,17 @@ class MenuSeeder extends Seeder
Role::query()
->whereIn('codigo', [RoleCode::Admin->value, RoleCode::AdminApp->value])
->orderBy('codigo')
->each(fn (Role $role) => $role->menus()->sync($allRoleMenuCodes));
Role::query()
->where('codigo', RoleCode::User->value)
->orderBy('codigo')
->each(fn (Role $role) => $role->menus()->sync($userMenuCodes));
Role::query()
->where('codigo', RoleCode::Scanner->value)
->orderBy('codigo')
->each(fn (Role $role) => $role->menus()->sync($scannerMenuCodes));
$tenants = Tenant::all();