seed(AuthorizationSeeder::class); WebsiteType::query()->create([ 'codigo' => 'onticket', 'nombre' => 'OnTicket', ]); } public function test_authentication_is_required(): void { $this->postJson('/api/v1/adminapp/tenant/entries', ['entries' => []]) ->assertUnauthorized(); } public function test_it_creates_multiple_entries_with_dates_and_tracked_inventory(): void { $tenant = $this->createFiestaTenant(); $this->createEventDateAttribute($tenant); $firstDate = $tenant->eventDates()->create([ 'date' => '2026-10-09', 'time_start' => '00:00', 'time_end' => '23:59', ]); $secondDate = $tenant->eventDates()->create([ 'date' => '2026-10-10', 'time_start' => '00:00', 'time_end' => '23:59', ]); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/entries', [ 'entries' => [ [ 'title' => 'Abono', 'description' => 'Acceso para ambas jornadas', 'event_date_ids' => [$firstDate->id, $secondDate->id], 'stock' => 1000, 'price' => 10000, ], [ 'title' => 'Entrada diaria', 'description' => null, 'event_date_ids' => [$firstDate->id], 'stock' => 250, 'price' => 5000.50, ], ], ]) ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonPath('data.0.title', 'Abono') ->assertJsonPath('data.0.event_date_ids', [$firstDate->id, $secondDate->id]) ->assertJsonPath('data.0.stock', 1000) ->assertJsonPath('data.0.price', '10000.00') ->assertJsonPath('data.1.title', 'Entrada diaria') ->assertJsonPath('data.1.price', '5000.50'); $this->assertDatabaseCount('catalog_items', 2); $this->assertDatabaseCount('variantes', 2); $this->assertDatabaseCount('inventories', 2); $this->assertDatabaseCount('variant_event_dates', 3); $entryCategory = Category::query() ->where('tenant_code', $tenant->codigo) ->where('nombre', 'Entradas') ->sole(); CatalogItem::query()->each(function (CatalogItem $entry) use ($tenant, $entryCategory): void { $this->assertSame($tenant->codigo, $entry->tenant_code); $this->assertSame($entryCategory->id, $entry->category_id); $this->assertSame('entrada', $entry->event_product_type->value); $this->assertSame('tracked', $entry->inventory_policy->value); $this->assertTrue($entry->has_tickets); $this->assertNull($entry->inventory_id); }); } public function test_it_updates_entries_with_an_id_and_creates_entries_without_one(): void { $tenant = $this->createFiestaTenant(); $this->createEventDateAttribute($tenant); $firstDate = $tenant->eventDates()->create([ 'date' => '2026-10-09', 'time_start' => '00:00', 'time_end' => '23:59', ]); $secondDate = $tenant->eventDates()->create([ 'date' => '2026-10-10', 'time_start' => '00:00', 'time_end' => '23:59', ]); Sanctum::actingAs($this->createAdminAppUser($tenant)); $createdId = $this->postJson('/api/v1/adminapp/tenant/entries', [ 'entries' => [[ 'title' => 'Abono original', 'description' => null, 'event_date_ids' => [$firstDate->id], 'stock' => 10, 'price' => 100, ]], ])->assertOk()->json('data.0.id'); $this->postJson('/api/v1/adminapp/tenant/entries', [ 'entries' => [ [ 'id' => $createdId, 'title' => 'Abono actualizado', 'description' => 'Ahora incluye ambas fechas', 'event_date_ids' => [$firstDate->id, $secondDate->id], 'stock' => 20, 'price' => 250, ], [ 'title' => 'Entrada nueva', 'description' => null, 'event_date_ids' => [$secondDate->id], 'stock' => 30, 'price' => 300, ], ], ]) ->assertOk() ->assertJsonCount(2, 'data') ->assertJsonPath('data.0.id', $createdId) ->assertJsonPath('data.0.title', 'Abono actualizado') ->assertJsonPath('data.0.event_date_ids', [$firstDate->id, $secondDate->id]) ->assertJsonPath('data.0.stock', 20) ->assertJsonPath('data.1.title', 'Entrada nueva'); $this->assertDatabaseCount('catalog_items', 2); $this->assertDatabaseHas('catalog_items', [ 'id' => $createdId, 'nombre' => 'Abono actualizado', 'precio' => 250, ]); } public function test_dates_must_belong_to_the_authenticated_tenant(): void { $tenant = $this->createFiestaTenant(); $otherTenant = $this->createTenant('other'); $this->createEventDateAttribute($tenant); $foreignDate = $otherTenant->eventDates()->create([ 'date' => '2026-10-09', 'time_start' => '00:00', 'time_end' => '23:59', ]); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/entries', [ 'entries' => [[ 'title' => 'Entrada inválida', 'description' => null, 'event_date_ids' => [$foreignDate->id], 'stock' => 10, 'price' => 100, ]], ]) ->assertUnprocessable() ->assertJsonValidationErrors(['entries.0.event_date_ids.0']); $this->assertDatabaseCount('catalog_items', 0); } public function test_the_endpoint_is_only_available_for_fiesta_futbol_infantil(): void { $tenant = $this->createTenant('other'); $this->createEventDateAttribute($tenant); $eventDate = $tenant->eventDates()->create([ 'date' => '2026-10-09', 'time_start' => '00:00', 'time_end' => '23:59', ]); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/entries', [ 'entries' => [[ 'title' => 'Entrada', 'description' => null, 'event_date_ids' => [$eventDate->id], 'stock' => 10, 'price' => 100, ]], ])->assertNotFound(); } private function createFiestaTenant(): Tenant { return $this->createTenant('fiesta_futbol_infantil'); } private function createTenant(string $code): Tenant { return Tenant::query()->create([ 'codigo' => $code, 'nombre' => ucfirst($code), 'dominio' => "{$code}.test", 'website_type_code' => 'onticket', ]); } private function createEventDateAttribute(Tenant $tenant): void { Attribute::query()->create([ 'tenant_codigo' => $tenant->codigo, 'codigo' => 'event_date', 'nombre' => 'Fecha', 'type' => FieldType::EventDate, 'is_required' => true, ]); } private function createAdminAppUser(Tenant $tenant): User { return User::factory()->create([ 'rol_codigo' => RoleCode::AdminApp->value, 'tenant_codigo' => $tenant->codigo, ]); } }