376 lines
15 KiB
PHP
376 lines
15 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Migrations;
|
|
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Tests\TestCase;
|
|
|
|
class SeedMutualSmepCatalogTest extends TestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
Storage::fake('s3');
|
|
$this->createSchema();
|
|
DB::table('tenants')->insert(['codigo' => 'mutual_smep']);
|
|
$this->seedCategoriesAndAttributes();
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
foreach ([
|
|
'featured_items',
|
|
'featured_groups',
|
|
'catalog_items_attachments',
|
|
'attachments',
|
|
'variant_values',
|
|
'item_attributes',
|
|
'variantes',
|
|
'catalog_items',
|
|
'inventories',
|
|
'brands',
|
|
'attribute',
|
|
'categorias',
|
|
'tenants',
|
|
] as $table) {
|
|
Schema::dropIfExists($table);
|
|
}
|
|
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function test_it_loads_and_rolls_back_the_complete_catalog(): void
|
|
{
|
|
$migration = require database_path(
|
|
'migrations/2026_09_24_030000_seed_mutual_smep_catalog.php'
|
|
);
|
|
|
|
$migration->up();
|
|
|
|
$this->assertSame(142, DB::table('catalog_items')->count());
|
|
$this->assertSame(150, DB::table('inventories')->count());
|
|
$this->assertSame(15, DB::table('variantes')->count());
|
|
$this->assertSame(8, DB::table('item_attributes')->count());
|
|
$this->assertSame(17, DB::table('variant_values')->count());
|
|
$this->assertSame(511, DB::table('attachments')->count());
|
|
$this->assertSame(511, DB::table('catalog_items_attachments')->count());
|
|
$this->assertSame(
|
|
511,
|
|
count(Storage::disk('s3')->allFiles('tenants/mutual_smep/catalog-v2')),
|
|
);
|
|
$this->assertDatabaseCount('featured_groups', 7);
|
|
$this->assertSame([
|
|
'Productos destacados',
|
|
'Dormitorio y Blanco',
|
|
'Electrodomésticos',
|
|
'Climatización',
|
|
'Tecnología',
|
|
'Bicicletas y Aire Libre',
|
|
'Bazar',
|
|
], DB::table('featured_groups')->orderBy('group_order')->pluck('group_name')->all());
|
|
|
|
DB::table('featured_groups')->orderBy('group_order')->each(function (object $group): void {
|
|
$this->assertSame('manual', $group->source_type);
|
|
$this->assertSame('carousel', $group->group_layout);
|
|
$this->assertSame('column_with_image', $group->product_layout);
|
|
if ($group->code === 'productos-destacados-smep') {
|
|
$this->assertNull($group->category_id);
|
|
} else {
|
|
$this->assertNotNull($group->category_id);
|
|
$this->assertDatabaseHas('categorias', [
|
|
'id' => $group->category_id,
|
|
'tenant_code' => 'mutual_smep',
|
|
'categoria_id' => null,
|
|
'nombre' => $group->group_name,
|
|
]);
|
|
}
|
|
$this->assertGreaterThan(
|
|
0,
|
|
DB::table('featured_items')->where('featured_group_id', $group->id)->count(),
|
|
);
|
|
});
|
|
|
|
$highlightedGroupId = DB::table('featured_groups')
|
|
->where('code', 'productos-destacados-smep')
|
|
->value('id');
|
|
$highlightedSlugs = DB::table('featured_items')
|
|
->join('catalog_items', 'catalog_items.id', '=', 'featured_items.catalog_item_id')
|
|
->where('featured_items.featured_group_id', $highlightedGroupId)
|
|
->orderBy('featured_items.order')
|
|
->pluck('catalog_items.slug')
|
|
->all();
|
|
$variantProductSlugs = DB::table('catalog_items')
|
|
->whereExists(fn ($query) => $query
|
|
->selectRaw('1')
|
|
->from('variantes')
|
|
->whereColumn('variantes.catalog_item_id', 'catalog_items.id'))
|
|
->pluck('slug')
|
|
->all();
|
|
|
|
$this->assertCount(13, $highlightedSlugs);
|
|
$this->assertSame([], array_values(array_diff($variantProductSlugs, $highlightedSlugs)));
|
|
$this->assertSame(46, DB::table('featured_items')->count());
|
|
DB::table('featured_groups')
|
|
->where('code', '!=', 'productos-destacados-smep')
|
|
->orderBy('id')
|
|
->each(function (object $group): void {
|
|
$this->assertLessThanOrEqual(
|
|
6,
|
|
DB::table('featured_items')->where('featured_group_id', $group->id)->count(),
|
|
);
|
|
});
|
|
$this->assertDatabaseHas('catalog_items', [
|
|
'tenant_code' => 'mutual_smep',
|
|
'slug' => 'motorola-g15',
|
|
'precio' => 330000,
|
|
]);
|
|
$this->assertSame(10, DB::table('inventories')->min('real_stock'));
|
|
$this->assertSame(10, DB::table('inventories')->max('real_stock'));
|
|
|
|
$migration->down();
|
|
|
|
foreach ([
|
|
'featured_items',
|
|
'featured_groups',
|
|
'catalog_items_attachments',
|
|
'attachments',
|
|
'variant_values',
|
|
'item_attributes',
|
|
'variantes',
|
|
'catalog_items',
|
|
'inventories',
|
|
'brands',
|
|
] as $table) {
|
|
$this->assertSame(0, DB::table($table)->count(), $table);
|
|
}
|
|
$this->assertSame([], Storage::disk('s3')->allFiles('tenants/mutual_smep/catalog-v2'));
|
|
}
|
|
|
|
public function test_it_limits_existing_category_groups_and_restores_them_on_rollback(): void
|
|
{
|
|
$catalogMigration = require database_path(
|
|
'migrations/2026_09_24_030000_seed_mutual_smep_catalog.php'
|
|
);
|
|
$limitMigration = require database_path(
|
|
'migrations/2026_09_24_050000_limit_mutual_smep_category_featured_groups.php'
|
|
);
|
|
|
|
$catalogMigration->up();
|
|
|
|
$limitMigration->down();
|
|
$this->assertSame(155, DB::table('featured_items')->count());
|
|
|
|
$limitMigration->up();
|
|
$this->assertSame(46, DB::table('featured_items')->count());
|
|
|
|
$categoryGroupCounts = DB::table('featured_groups')
|
|
->leftJoin('featured_items', 'featured_items.featured_group_id', '=', 'featured_groups.id')
|
|
->where('featured_groups.code', '!=', 'productos-destacados-smep')
|
|
->groupBy('featured_groups.id', 'featured_groups.group_name', 'featured_groups.group_order')
|
|
->orderBy('featured_groups.group_order')
|
|
->selectRaw('featured_groups.group_name, COUNT(featured_items.id) AS item_count')
|
|
->pluck('item_count', 'featured_groups.group_name')
|
|
->map(fn ($count): int => (int) $count)
|
|
->all();
|
|
|
|
$this->assertSame([
|
|
'Dormitorio y Blanco' => 6,
|
|
'Electrodomésticos' => 6,
|
|
'Climatización' => 6,
|
|
'Tecnología' => 6,
|
|
'Bicicletas y Aire Libre' => 6,
|
|
'Bazar' => 3,
|
|
], $categoryGroupCounts);
|
|
|
|
$limitMigration->down();
|
|
$this->assertSame(155, DB::table('featured_items')->count());
|
|
|
|
$catalogMigration->down();
|
|
}
|
|
|
|
public function test_it_upgrades_legacy_catalog_images_and_restores_them_on_rollback(): void
|
|
{
|
|
$catalogMigration = require database_path(
|
|
'migrations/2026_09_24_030000_seed_mutual_smep_catalog.php'
|
|
);
|
|
$imageMigration = require database_path(
|
|
'migrations/2026_09_24_060000_optimize_mutual_smep_catalog_images.php'
|
|
);
|
|
|
|
$catalogMigration->up();
|
|
|
|
DB::table('attachments')->orderBy('id')->each(function (object $attachment): void {
|
|
$optimizedPath = (string) $attachment->path;
|
|
$legacyPath = str_replace('catalog-v2/', 'catalog-v1/', $optimizedPath);
|
|
$contents = Storage::disk('s3')->get($optimizedPath);
|
|
|
|
Storage::disk('s3')->put($legacyPath, $contents.'legacy');
|
|
Storage::disk('s3')->delete($optimizedPath);
|
|
DB::table('attachments')->where('id', $attachment->id)->update([
|
|
'path' => $legacyPath,
|
|
'size' => strlen($contents.'legacy'),
|
|
]);
|
|
});
|
|
|
|
$imageMigration->up();
|
|
|
|
$this->assertSame(511, count(Storage::disk('s3')->allFiles('tenants/mutual_smep/catalog-v2')));
|
|
$this->assertSame(0, DB::table('attachments')->where('path', 'like', '%catalog-v1/%')->count());
|
|
$this->assertSame(511, DB::table('attachments')->where('path', 'like', '%catalog-v2/%')->count());
|
|
|
|
$imageMigration->down();
|
|
|
|
$this->assertSame([], Storage::disk('s3')->allFiles('tenants/mutual_smep/catalog-v2'));
|
|
$this->assertSame(511, DB::table('attachments')->where('path', 'like', '%catalog-v1/%')->count());
|
|
|
|
$catalogMigration->down();
|
|
$this->assertSame([], Storage::disk('s3')->allFiles('tenants/mutual_smep/catalog-v1'));
|
|
}
|
|
|
|
private function seedCategoriesAndAttributes(): void
|
|
{
|
|
$manifest = json_decode(
|
|
(string) file_get_contents(database_path('data/mutual_smep_catalog_v1.json')),
|
|
true,
|
|
flags: JSON_THROW_ON_ERROR,
|
|
);
|
|
$parents = [];
|
|
|
|
foreach ($manifest['items'] as $item) {
|
|
$parentName = $item['parent_category'];
|
|
$parentId = $parents[$parentName] ??= DB::table('categorias')->insertGetId([
|
|
'tenant_code' => 'mutual_smep',
|
|
'categoria_id' => null,
|
|
'nombre' => $parentName,
|
|
]);
|
|
|
|
DB::table('categorias')->insertOrIgnore([
|
|
'tenant_code' => 'mutual_smep',
|
|
'categoria_id' => $parentId,
|
|
'nombre' => $item['category'],
|
|
]);
|
|
}
|
|
|
|
foreach (['color', 'medida_cama', 'almacenamiento', 'memoria_ram', 'rodado'] as $code) {
|
|
DB::table('attribute')->insert([
|
|
'tenant_codigo' => 'mutual_smep',
|
|
'codigo' => $code,
|
|
'nombre' => $code,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function createSchema(): void
|
|
{
|
|
Schema::create('tenants', fn (Blueprint $table) => $table->string('codigo')->primary());
|
|
Schema::create('categorias', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('tenant_code');
|
|
$table->unsignedBigInteger('categoria_id')->nullable();
|
|
$table->string('nombre');
|
|
$table->unique(['tenant_code', 'categoria_id', 'nombre']);
|
|
});
|
|
Schema::create('attribute', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('tenant_codigo');
|
|
$table->string('codigo');
|
|
$table->string('nombre');
|
|
});
|
|
Schema::create('brands', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('tenant_codigo');
|
|
$table->string('nombre');
|
|
$table->text('descripcion')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
Schema::create('inventories', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->unsignedBigInteger('sold_units')->default(0);
|
|
$table->unsignedBigInteger('refunded_units')->default(0);
|
|
$table->unsignedInteger('reserved_stock')->default(0);
|
|
$table->unsignedInteger('real_stock')->default(0);
|
|
});
|
|
Schema::create('catalog_items', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('tenant_code');
|
|
$table->unsignedBigInteger('category_id')->nullable();
|
|
$table->unsignedBigInteger('brand_id')->nullable();
|
|
$table->unsignedBigInteger('inventory_id')->nullable();
|
|
$table->string('type');
|
|
$table->string('slug');
|
|
$table->string('nombre');
|
|
$table->unsignedInteger('group_order')->default(0);
|
|
$table->text('descripcion')->nullable();
|
|
$table->decimal('precio', 10, 2);
|
|
$table->string('inventory_policy');
|
|
$table->string('inventory_subject');
|
|
$table->boolean('has_tickets')->default(false);
|
|
$table->unique(['tenant_code', 'slug']);
|
|
});
|
|
Schema::create('variantes', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->foreignId('catalog_item_id')->constrained('catalog_items')->cascadeOnDelete();
|
|
$table->unsignedBigInteger('inventory_id');
|
|
$table->text('descripcion')->nullable();
|
|
$table->decimal('precio', 10, 2)->nullable();
|
|
});
|
|
Schema::create('item_attributes', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->foreignId('catalog_item_id')->constrained('catalog_items')->cascadeOnDelete();
|
|
$table->unsignedBigInteger('attribute_id');
|
|
$table->boolean('allow_multi_select')->default(false);
|
|
$table->unsignedInteger('sort_order')->default(0);
|
|
$table->boolean('show_in_selector')->default(true);
|
|
$table->string('ticket_label')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
Schema::create('variant_values', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->foreignId('variant_id')->constrained('variantes')->cascadeOnDelete();
|
|
$table->foreignId('item_attribute_id')->constrained('item_attributes')->cascadeOnDelete();
|
|
$table->text('value')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
Schema::create('attachments', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->uuid('key')->unique();
|
|
$table->string('path');
|
|
$table->string('filename');
|
|
$table->string('type');
|
|
$table->string('mime_type');
|
|
$table->string('extension')->nullable();
|
|
$table->unsignedBigInteger('size')->default(0);
|
|
$table->timestamps();
|
|
});
|
|
Schema::create('catalog_items_attachments', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->foreignId('variant_id')->nullable()->constrained('variantes')->cascadeOnDelete();
|
|
$table->foreignId('catalog_item_id')->constrained('catalog_items')->cascadeOnDelete();
|
|
$table->foreignId('attachment_id')->constrained('attachments')->cascadeOnDelete();
|
|
$table->unsignedInteger('orden')->default(0);
|
|
$table->boolean('is_enabled')->default(true);
|
|
});
|
|
Schema::create('featured_groups', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('tenant_code');
|
|
$table->string('code');
|
|
$table->string('source_type');
|
|
$table->unsignedBigInteger('category_id')->nullable();
|
|
$table->string('product_layout');
|
|
$table->string('group_layout');
|
|
$table->string('group_name');
|
|
$table->unsignedInteger('group_order')->default(0);
|
|
});
|
|
Schema::create('featured_items', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->foreignId('featured_group_id')->constrained('featured_groups')->cascadeOnDelete();
|
|
$table->foreignId('catalog_item_id')->constrained('catalog_items')->cascadeOnDelete();
|
|
$table->unsignedInteger('order')->default(0);
|
|
});
|
|
}
|
|
}
|