From 16e2ca679cbd28acafc37138ac301c3f2c373df6 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Thu, 24 Sep 2026 11:52:45 -0300 Subject: [PATCH] feat(seeder): implement Mutual SMEP attribute seeder and associated tests --- ..._24_020000_seed_mutual_smep_attributes.php | 139 ++++++++++++++++++ database/seeders/AttributeSeeder.php | 6 +- database/seeders/DatabaseSeeder.php | 1 + .../seeders/MutualSmepAttributeSeeder.php | 122 +++++++++++++++ .../Seeders/MutualSmepAttributeSeederTest.php | 119 +++++++++++++++ 5 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_09_24_020000_seed_mutual_smep_attributes.php create mode 100644 database/seeders/MutualSmepAttributeSeeder.php create mode 100644 tests/Feature/Seeders/MutualSmepAttributeSeederTest.php diff --git a/database/migrations/2026_09_24_020000_seed_mutual_smep_attributes.php b/database/migrations/2026_09_24_020000_seed_mutual_smep_attributes.php new file mode 100644 index 0000000..7d5a17e --- /dev/null +++ b/database/migrations/2026_09_24_020000_seed_mutual_smep_attributes.php @@ -0,0 +1,139 @@ +> */ + private const ATTRIBUTES = [ + [ + 'codigo' => 'color', + 'nombre' => 'Color', + 'metadata_schema' => [ + 'hex' => ['type' => 'string'], + ], + 'options' => [ + ['value' => 'Negro', 'label' => 'Negro', 'metadata' => ['hex' => '#000000']], + ['value' => 'Blanco', 'label' => 'Blanco', 'metadata' => ['hex' => '#FFFFFF']], + ['value' => 'Gris', 'label' => 'Gris', 'metadata' => ['hex' => '#808080']], + ['value' => 'Plata', 'label' => 'Plata', 'metadata' => ['hex' => '#C0C0C0']], + ['value' => 'Azul', 'label' => 'Azul', 'metadata' => ['hex' => '#0000FF']], + ['value' => 'Rojo', 'label' => 'Rojo', 'metadata' => ['hex' => '#DC3545']], + ['value' => 'Rosa', 'label' => 'Rosa', 'metadata' => ['hex' => '#FFC0CB']], + ['value' => 'Multicolor', 'label' => 'Multicolor'], + ], + ], + [ + 'codigo' => 'medida_cama', + 'nombre' => 'Medida', + 'options' => [ + ['value' => 'Cuna', 'label' => 'Cuna'], + ['value' => '1 Plaza', 'label' => '1 Plaza'], + ['value' => '1 1/2 Plazas', 'label' => '1 1/2 Plazas'], + ['value' => '2 Plazas', 'label' => '2 Plazas'], + ['value' => '2 1/2 Plazas', 'label' => '2 1/2 Plazas'], + ['value' => 'Queen', 'label' => 'Queen'], + ['value' => 'King', 'label' => 'King'], + ], + ], + [ + 'codigo' => 'almacenamiento', + 'nombre' => 'Almacenamiento', + 'options' => [ + ['value' => '128 GB', 'label' => '128 GB'], + ['value' => '256 GB', 'label' => '256 GB'], + ['value' => '512 GB', 'label' => '512 GB'], + ['value' => '1 TB', 'label' => '1 TB'], + ], + ], + [ + 'codigo' => 'memoria_ram', + 'nombre' => 'Memoria RAM', + 'options' => [ + ['value' => '4 GB', 'label' => '4 GB'], + ['value' => '8 GB', 'label' => '8 GB'], + ['value' => '16 GB', 'label' => '16 GB'], + ], + ], + [ + 'codigo' => 'rodado', + 'nombre' => 'Rodado', + 'options' => [ + ['value' => '16', 'label' => '16'], + ['value' => '26', 'label' => '26'], + ['value' => '29', 'label' => '29'], + ], + ], + ]; + + public function up(): void + { + if (! DB::table('tenants')->where('codigo', self::TENANT_CODE)->exists()) { + return; + } + + DB::transaction(function (): void { + foreach (self::ATTRIBUTES as $definition) { + $attributeId = $this->upsertAttribute($definition); + + DB::table('attribute_options')->where('attribute_id', $attributeId)->delete(); + + foreach ($definition['options'] as $index => $option) { + DB::table('attribute_options')->insert([ + 'attribute_id' => $attributeId, + 'value' => $option['value'], + 'label' => $option['label'], + 'sort_order' => $index + 1, + 'metadata' => isset($option['metadata']) + ? json_encode($option['metadata'], JSON_THROW_ON_ERROR) + : null, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + } + }); + } + + public function down(): void + { + DB::table('attribute') + ->where('tenant_codigo', self::TENANT_CODE) + ->whereIn('codigo', array_column(self::ATTRIBUTES, 'codigo')) + ->delete(); + } + + /** @param array $definition */ + private function upsertAttribute(array $definition): int + { + $attributeId = DB::table('attribute') + ->where('tenant_codigo', self::TENANT_CODE) + ->where('codigo', $definition['codigo']) + ->value('id'); + $values = [ + 'nombre' => $definition['nombre'], + 'is_required' => false, + 'metadata_schema' => isset($definition['metadata_schema']) + ? json_encode($definition['metadata_schema'], JSON_THROW_ON_ERROR) + : null, + 'type' => 'select', + 'updated_at' => now(), + ]; + + if ($attributeId !== null) { + DB::table('attribute')->where('id', $attributeId)->update($values); + + return (int) $attributeId; + } + + return (int) DB::table('attribute')->insertGetId([ + 'tenant_codigo' => self::TENANT_CODE, + 'codigo' => $definition['codigo'], + ...$values, + 'created_at' => now(), + ]); + } +}; diff --git a/database/seeders/AttributeSeeder.php b/database/seeders/AttributeSeeder.php index f19e67b..142d8b1 100644 --- a/database/seeders/AttributeSeeder.php +++ b/database/seeders/AttributeSeeder.php @@ -3,10 +3,10 @@ namespace Database\Seeders; use App\Domains\Commerce\Catalog\Models\Attribute; -use App\Shared\Enums\FieldType; use App\Domains\Core\Tenant\Models\Tenant; use App\Domains\Ticketing\Ticket\Enums\ValidityTimeType; use App\Domains\Ticketing\Ticket\Models\ValidityTime; +use App\Shared\Enums\FieldType; use Illuminate\Database\Seeder; class AttributeSeeder extends Seeder @@ -25,6 +25,10 @@ class AttributeSeeder extends Seeder continue; } + if ($tenant->codigo === 'mutual_smep') { + continue; + } + $this->seedAttribute($tenant, [ 'codigo' => 'color', 'nombre' => 'Color', diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d599f7f..e1185e3 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -29,6 +29,7 @@ class DatabaseSeeder extends Seeder TenantSeeder::class, DesfilePuraTendenciaSeeder::class, AttributeSeeder::class, + MutualSmepAttributeSeeder::class, CategorySeeder::class, MutualSmepCategorySeeder::class, BrandSeeder::class, diff --git a/database/seeders/MutualSmepAttributeSeeder.php b/database/seeders/MutualSmepAttributeSeeder.php new file mode 100644 index 0000000..9b6d0ca --- /dev/null +++ b/database/seeders/MutualSmepAttributeSeeder.php @@ -0,0 +1,122 @@ +> */ + private const ATTRIBUTES = [ + [ + 'codigo' => 'color', + 'nombre' => 'Color', + 'metadata_schema' => [ + 'hex' => ['type' => 'string'], + ], + 'options' => [ + ['value' => 'Negro', 'label' => 'Negro', 'metadata' => ['hex' => '#000000']], + ['value' => 'Blanco', 'label' => 'Blanco', 'metadata' => ['hex' => '#FFFFFF']], + ['value' => 'Gris', 'label' => 'Gris', 'metadata' => ['hex' => '#808080']], + ['value' => 'Plata', 'label' => 'Plata', 'metadata' => ['hex' => '#C0C0C0']], + ['value' => 'Azul', 'label' => 'Azul', 'metadata' => ['hex' => '#0000FF']], + ['value' => 'Rojo', 'label' => 'Rojo', 'metadata' => ['hex' => '#DC3545']], + ['value' => 'Rosa', 'label' => 'Rosa', 'metadata' => ['hex' => '#FFC0CB']], + ['value' => 'Multicolor', 'label' => 'Multicolor'], + ], + ], + [ + 'codigo' => 'medida_cama', + 'nombre' => 'Medida', + 'options' => [ + ['value' => 'Cuna', 'label' => 'Cuna'], + ['value' => '1 Plaza', 'label' => '1 Plaza'], + ['value' => '1 1/2 Plazas', 'label' => '1 1/2 Plazas'], + ['value' => '2 Plazas', 'label' => '2 Plazas'], + ['value' => '2 1/2 Plazas', 'label' => '2 1/2 Plazas'], + ['value' => 'Queen', 'label' => 'Queen'], + ['value' => 'King', 'label' => 'King'], + ], + ], + [ + 'codigo' => 'almacenamiento', + 'nombre' => 'Almacenamiento', + 'options' => [ + ['value' => '128 GB', 'label' => '128 GB'], + ['value' => '256 GB', 'label' => '256 GB'], + ['value' => '512 GB', 'label' => '512 GB'], + ['value' => '1 TB', 'label' => '1 TB'], + ], + ], + [ + 'codigo' => 'memoria_ram', + 'nombre' => 'Memoria RAM', + 'options' => [ + ['value' => '4 GB', 'label' => '4 GB'], + ['value' => '8 GB', 'label' => '8 GB'], + ['value' => '16 GB', 'label' => '16 GB'], + ], + ], + [ + 'codigo' => 'rodado', + 'nombre' => 'Rodado', + 'options' => [ + ['value' => '16', 'label' => '16'], + ['value' => '26', 'label' => '26'], + ['value' => '29', 'label' => '29'], + ], + ], + ]; + + public function run(): void + { + if (! Tenant::query()->where('codigo', self::TENANT_CODE)->exists()) { + return; + } + + DB::transaction(function (): void { + foreach (self::ATTRIBUTES as $definition) { + $options = $definition['options']; + unset($definition['options']); + + $attribute = Attribute::query()->updateOrCreate( + [ + 'tenant_codigo' => self::TENANT_CODE, + 'codigo' => $definition['codigo'], + ], + [ + ...$definition, + 'type' => FieldType::Select->value, + 'is_required' => false, + 'metadata_schema' => $definition['metadata_schema'] ?? null, + ], + ); + + $attribute->options()->delete(); + $attribute->options()->createMany( + collect($options) + ->values() + ->map(fn (array $option, int $index): array => [ + ...$option, + 'sort_order' => $index + 1, + ]) + ->all() + ); + } + }); + } + + public function down(): void + { + Attribute::query() + ->where('tenant_codigo', self::TENANT_CODE) + ->whereIn('codigo', array_column(self::ATTRIBUTES, 'codigo')) + ->delete(); + } +} diff --git a/tests/Feature/Seeders/MutualSmepAttributeSeederTest.php b/tests/Feature/Seeders/MutualSmepAttributeSeederTest.php new file mode 100644 index 0000000..8f84965 --- /dev/null +++ b/tests/Feature/Seeders/MutualSmepAttributeSeederTest.php @@ -0,0 +1,119 @@ +string('codigo')->primary(); + }); + Schema::create('attribute', function (Blueprint $table): void { + $table->id(); + $table->string('tenant_codigo'); + $table->string('codigo'); + $table->string('nombre'); + $table->boolean('is_required')->default(false); + $table->json('metadata_schema')->nullable(); + $table->string('type'); + $table->timestamps(); + $table->unique(['tenant_codigo', 'codigo']); + }); + Schema::create('attribute_options', function (Blueprint $table): void { + $table->id(); + $table->foreignId('attribute_id')->constrained('attribute')->cascadeOnDelete(); + $table->string('value'); + $table->string('label'); + $table->unsignedInteger('sort_order')->default(0); + $table->json('metadata')->nullable(); + $table->timestamps(); + }); + } + + protected function tearDown(): void + { + Schema::dropIfExists('attribute_options'); + Schema::dropIfExists('attribute'); + Schema::dropIfExists('tenants'); + + parent::tearDown(); + } + + public function test_it_creates_the_attributes_and_options_idempotently(): void + { + $this->createTenant('mutual_smep'); + + $this->seed(MutualSmepAttributeSeeder::class); + $this->seed(MutualSmepAttributeSeeder::class); + + $this->assertSame(5, DB::table('attribute')->where('tenant_codigo', 'mutual_smep')->count()); + $this->assertSame(25, DB::table('attribute_options')->count()); + $this->assertDatabaseHas('attribute', [ + 'tenant_codigo' => 'mutual_smep', + 'codigo' => 'medida_cama', + 'nombre' => 'Medida', + 'type' => 'select', + 'is_required' => false, + ]); + $this->assertDatabaseHas('attribute_options', [ + 'value' => '512 GB', + 'label' => '512 GB', + ]); + } + + public function test_down_only_removes_mutual_smep_attributes(): void + { + $this->createTenant('mutual_smep'); + $this->createTenant('other'); + DB::table('attribute')->insert([ + 'tenant_codigo' => 'other', + 'codigo' => 'color', + 'nombre' => 'Color', + 'type' => 'select', + 'created_at' => now(), + 'updated_at' => now(), + ]); + + $seeder = app(MutualSmepAttributeSeeder::class); + $seeder->run(); + $seeder->down(); + + $this->assertDatabaseMissing('attribute', ['tenant_codigo' => 'mutual_smep']); + $this->assertDatabaseHas('attribute', [ + 'tenant_codigo' => 'other', + 'codigo' => 'color', + ]); + } + + public function test_the_data_migration_runs_and_rolls_back_the_attributes(): void + { + $this->createTenant('mutual_smep'); + $migration = require database_path( + 'migrations/2026_09_24_020000_seed_mutual_smep_attributes.php' + ); + + $migration->up(); + + $this->assertSame(5, DB::table('attribute')->where('tenant_codigo', 'mutual_smep')->count()); + $this->assertSame(25, DB::table('attribute_options')->count()); + + $migration->down(); + + $this->assertDatabaseMissing('attribute', ['tenant_codigo' => 'mutual_smep']); + $this->assertSame(0, DB::table('attribute_options')->count()); + } + + private function createTenant(string $code): void + { + DB::table('tenants')->insert(['codigo' => $code]); + } +}