seed(AuthorizationSeeder::class); WebsiteType::query()->create([ 'codigo' => 'onticket', 'nombre' => 'OnTicket', ]); } public function test_authentication_is_required(): void { $this->postJson('/api/v1/adminapp/tenant/foods', ['variants' => []]) ->assertUnauthorized(); } public function test_it_creates_one_food_item_with_multiple_variants(): void { [$tenant, $firstDate, $secondDate] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/foods', [ 'variants' => [ $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 1700, 10000), $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 900, 8000), ], ]) ->assertOk() ->assertJsonPath('data.name', 'Comida') ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.schedule', 'Almuerzo') ->assertJsonPath('data.variants.0.service', 'Comedor') ->assertJsonPath('data.variants.0.stock', 1700) ->assertJsonPath('data.variants.0.price', '10000.00') ->assertJsonPath('data.variants.1.price', '8000.00'); $this->getJson('/api/v1/adminapp/tenant/foods') ->assertOk() ->assertJsonPath('data.name', 'Comida') ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.schedule', 'Almuerzo') ->assertJsonPath('data.variants.1.service', 'Vianda'); $this->assertDatabaseCount('catalog_items', 1); $this->assertDatabaseCount('variantes', 2); $this->assertDatabaseCount('inventories', 2); $this->assertDatabaseCount('variant_event_dates', 2); $this->assertDatabaseCount('variant_values', 4); $this->assertDatabaseHas('catalog_items', [ 'tenant_code' => $tenant->codigo, 'slug' => 'comida', 'nombre' => 'Comida', 'category_id' => Category::query() ->where('tenant_code', $tenant->codigo) ->where('nombre', 'Comidas') ->sole() ->id, 'precio' => 8000, ]); } public function test_it_updates_variants_with_an_id_and_creates_variants_without_one(): void { [$tenant, $firstDate, $secondDate] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $created = $this->postJson('/api/v1/adminapp/tenant/foods', [ 'variants' => [ $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 100, 10000), ], ])->assertOk(); $variantId = $created->json('data.variants.0.id'); $updated = $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 80, 8500); $updated['id'] = $variantId; $updated['description'] = 'Cena para llevar'; $this->postJson('/api/v1/adminapp/tenant/foods', [ 'variants' => [ $updated, $this->variantPayload($firstDate->id, 'Almuerzo', 'Vianda', 50, 9000), ], ]) ->assertOk() ->assertJsonCount(2, 'data.variants') ->assertJsonPath('data.variants.0.id', $variantId) ->assertJsonPath('data.variants.0.event_date_id', $secondDate->id) ->assertJsonPath('data.variants.0.schedule', 'Cena') ->assertJsonPath('data.variants.0.service', 'Vianda') ->assertJsonPath('data.variants.0.description', 'Cena para llevar') ->assertJsonPath('data.variants.0.stock', 80) ->assertJsonPath('data.variants.0.price', '8500.00'); $this->assertDatabaseCount('catalog_items', 1); $this->assertDatabaseCount('variantes', 2); $this->assertDatabaseHas('variantes', [ 'id' => $variantId, 'event_date_id' => $secondDate->id, 'descripcion' => 'Cena para llevar', 'precio' => 8500, ]); } public function test_it_rejects_duplicate_combinations(): void { [$tenant, $firstDate] = $this->configuredTenant(); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->postJson('/api/v1/adminapp/tenant/foods', [ 'variants' => [ $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 100, 10000), $this->variantPayload($firstDate->id, ' almuerzo ', ' comedor ', 100, 10000), ], ]) ->assertUnprocessable() ->assertJsonValidationErrors(['variants.1']); $this->assertDatabaseCount('catalog_items', 0); } /** @return array{Tenant, mixed, mixed} */ private function configuredTenant(): array { $tenant = Tenant::query()->create([ 'codigo' => 'fiesta_futbol_infantil', 'nombre' => 'Fiesta FĂștbol Infantil', 'dominio' => 'fiesta.test', 'website_type_code' => 'onticket', ]); $eventDateAttribute = Attribute::query()->create([ 'tenant_codigo' => $tenant->codigo, 'codigo' => 'event_date', 'nombre' => 'Fecha', 'type' => FieldType::EventDate, 'is_required' => true, ]); $scheduleAttribute = Attribute::query()->create([ 'tenant_codigo' => $tenant->codigo, 'codigo' => 'horario', 'nombre' => 'Horario', 'type' => FieldType::Select, 'is_required' => true, ]); $scheduleAttribute->options()->createMany([ ['value' => 'Almuerzo', 'label' => 'Almuerzo', 'sort_order' => 1], ['value' => 'Cena', 'label' => 'Cena', 'sort_order' => 2], ]); $serviceAttribute = Attribute::query()->create([ 'tenant_codigo' => $tenant->codigo, 'codigo' => 'servicio', 'nombre' => 'Servicio', 'type' => FieldType::Select, 'is_required' => true, ]); $serviceAttribute->options()->createMany([ ['value' => 'Comedor', 'label' => 'Comedor', 'sort_order' => 1], ['value' => 'Vianda', 'label' => 'Vianda', 'sort_order' => 2], ]); $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', ]); $menu = Menu::query()->create([ 'code' => 'adminapp.fiesta-futbol-infantil.comida', 'label' => 'Comida', 'route' => '/admin/comidas', ]); $tenant->menues()->attach($menu->code); $this->assertNotNull($eventDateAttribute); return [$tenant, $firstDate, $secondDate]; } /** @return array */ private function variantPayload( int $eventDateId, string $schedule, string $service, int $stock, float $price, ): array { return [ 'event_date_id' => $eventDateId, 'schedule' => $schedule, 'service' => $service, 'description' => null, 'stock' => $stock, 'price' => $price, ]; } private function createAdminAppUser(Tenant $tenant): User { return User::factory()->create([ 'rol_codigo' => RoleCode::AdminApp->value, 'tenant_codigo' => $tenant->codigo, ]); } }