diff --git a/app/Domains/Catalog/Requests/StoreProductRequest.php b/app/Domains/Catalog/Requests/StoreProductRequest.php index 36acdd0..e043328 100644 --- a/app/Domains/Catalog/Requests/StoreProductRequest.php +++ b/app/Domains/Catalog/Requests/StoreProductRequest.php @@ -31,6 +31,7 @@ class StoreProductRequest extends FormRequest 'nombre' => ['required', 'string', 'max:255'], 'descripcion' => ['nullable', 'string'], 'precio' => ['required', 'numeric', 'min:0'], + 'stock' => ['sometimes', 'integer', 'min:0'], 'attribute_ids' => ['sometimes', 'array'], 'attribute_ids.*' => [ 'required', diff --git a/app/Domains/Catalog/Services/ProductService.php b/app/Domains/Catalog/Services/ProductService.php index 0a3a096..063fbef 100644 --- a/app/Domains/Catalog/Services/ProductService.php +++ b/app/Domains/Catalog/Services/ProductService.php @@ -26,7 +26,8 @@ class ProductService return DB::transaction(function () use ($tenant, $data) { $attributeIds = $data['attribute_ids'] ?? []; $images = $data['images'] ?? []; - unset($data['attribute_ids'], $data['images']); + $stock = $data['stock'] ?? 0; + unset($data['attribute_ids'], $data['images'], $data['stock']); /** @var Product $product */ $product = Product::query()->create([ @@ -40,6 +41,12 @@ class ProductService $this->syncProductImages($product, $images); } + // Create default variant with stock + $this->createVariant($product, [ + 'stock' => $stock, + 'definitions' => [], + ]); + return $product->load(['attributes.options', 'attachments', 'brand', 'category']); }); } @@ -109,6 +116,17 @@ class ProductService $images = $data['images'] ?? []; unset($data['images']); + // If the variant being created has definitions, it is a real variant. + // Remove any default variants (those without definitions). + $hasDefinitions = ! empty($data['definitions']); + if ($hasDefinitions) { + $defaultVariants = $product->variants()->whereDoesntHave('definitions')->get(); + foreach ($defaultVariants as $defaultVariant) { + $this->deleteVariantAttachments($defaultVariant); + $product->deleteVariant($defaultVariant); + } + } + $variant = $product->createVariant($data); if (! empty($images)) { @@ -153,6 +171,14 @@ class ProductService $product = $variant->product; $this->deleteVariantAttachments($variant); $product->deleteVariant($variant); + + // Re-create a default variant with stock 0 if it has no variants left + if ($product->variants()->count() === 0) { + $product->createVariant([ + 'stock' => 0, + 'definitions' => [], + ]); + } }); } diff --git a/database/seeders/AttributeSeeder.php b/database/seeders/AttributeSeeder.php index 3661ad7..8f72d15 100644 --- a/database/seeders/AttributeSeeder.php +++ b/database/seeders/AttributeSeeder.php @@ -18,6 +18,52 @@ class AttributeSeeder extends Seeder $tenants = Tenant::all(); foreach ($tenants as $tenant) { + // Seed Color attribute + $existingColor = Attribute::query() + ->where('tenant_codigo', $tenant->codigo) + ->where('codigo', 'color') + ->first(); + + if ($existingColor) { + Product::deleteAttribute($existingColor); + } + + Product::createAttribute($tenant, [ + 'codigo' => 'color', + 'nombre' => 'Color', + 'type' => FieldType::Select->value, + 'is_required' => true, + 'metadata_schema' => [ + 'hex' => ['type' => 'string'], + ], + 'options' => [ + [ + 'value' => 'Negro', + 'label' => 'Negro', + 'sort_order' => 1, + 'metadata' => ['hex' => '#000000'], + ], + [ + 'value' => 'Gris', + 'label' => 'Gris', + 'sort_order' => 2, + 'metadata' => ['hex' => '#808080'], + ], + [ + 'value' => 'Blanco', + 'label' => 'Blanco', + 'sort_order' => 3, + 'metadata' => ['hex' => '#FFFFFF'], + ], + [ + 'value' => 'Azul', + 'label' => 'Azul', + 'sort_order' => 4, + 'metadata' => ['hex' => '#0000FF'], + ], + ], + ]); + // Seed Talle (Size - Text options) attribute $existingTalle = Attribute::query() ->where('tenant_codigo', $tenant->codigo) @@ -64,51 +110,6 @@ class AttributeSeeder extends Seeder ], ]); - // Seed Color attribute - $existingColor = Attribute::query() - ->where('tenant_codigo', $tenant->codigo) - ->where('codigo', 'color') - ->first(); - - if ($existingColor) { - Product::deleteAttribute($existingColor); - } - - Product::createAttribute($tenant, [ - 'codigo' => 'color', - 'nombre' => 'Color', - 'type' => FieldType::Select->value, - 'is_required' => true, - 'metadata_schema' => [ - 'hex' => ['type' => 'string'], - ], - 'options' => [ - [ - 'value' => 'Negro', - 'label' => 'Negro', - 'sort_order' => 1, - 'metadata' => ['hex' => '#000000'], - ], - [ - 'value' => 'Gris', - 'label' => 'Gris', - 'sort_order' => 2, - 'metadata' => ['hex' => '#808080'], - ], - [ - 'value' => 'Blanco', - 'label' => 'Blanco', - 'sort_order' => 3, - 'metadata' => ['hex' => '#FFFFFF'], - ], - [ - 'value' => 'Azul', - 'label' => 'Azul', - 'sort_order' => 4, - 'metadata' => ['hex' => '#0000FF'], - ], - ], - ]); } } } diff --git a/database/seeders/CategorySeeder.php b/database/seeders/CategorySeeder.php index e258203..7efcb17 100644 --- a/database/seeders/CategorySeeder.php +++ b/database/seeders/CategorySeeder.php @@ -16,7 +16,8 @@ class CategorySeeder extends Seeder 'Remeras', 'Pantalones', 'Zapatillas', - 'Buzos' + 'Buzos', + 'Accesorios' ]; foreach ($categories as $nombre) { diff --git a/database/seeders/ProductCatalogFromImagesSeeder.php b/database/seeders/ProductCatalogFromImagesSeeder.php index 6a11593..78ff1f7 100644 --- a/database/seeders/ProductCatalogFromImagesSeeder.php +++ b/database/seeders/ProductCatalogFromImagesSeeder.php @@ -42,6 +42,7 @@ class ProductCatalogFromImagesSeeder extends Seeder ], 'zapatillas_lecoq' => ['price' => 109999, 'stock' => 6], 'zapatillas_topper' => ['price' => 104999, 'stock' => 7], + 'pelota_mundial' => ['price' => 45000, 'stock' => 15], ]; /** @@ -220,6 +221,15 @@ class ProductCatalogFromImagesSeeder extends Seeder $variantGroup['files'] ); + if ($attributeIds->isEmpty()) { + $this->productService->createVariant($product, [ + 'stock' => $variantGroup['stock'], + 'definitions' => [], + 'images' => $images, + ]); + continue; + } + foreach ($sizes as $size) { $definitions = []; @@ -301,6 +311,19 @@ class ProductCatalogFromImagesSeeder extends Seeder ]; } + if ($groupKey === 'pelota_mundial') { + return [ + 'type' => 'pelota', + 'category_name' => 'Accesorios', + 'brand_name' => 'Adidas', + 'color' => null, + 'attribute_codes' => [], + 'product_slug' => $groupKey, + 'product_name' => 'Pelota Mundial 2026', + 'description' => 'Pelota oficial del mundial 2026 sin variantes.', + ]; + } + $segments = explode('_', $groupKey); $type = array_shift($segments); diff --git a/storage/public/catalog/pelota_mundial.webp b/storage/public/catalog/pelota_mundial.webp new file mode 100644 index 0000000..180ff1d Binary files /dev/null and b/storage/public/catalog/pelota_mundial.webp differ diff --git a/storage/public/catalog/pelota_mundial_1.jpg b/storage/public/catalog/pelota_mundial_1.jpg new file mode 100644 index 0000000..16843c2 Binary files /dev/null and b/storage/public/catalog/pelota_mundial_1.jpg differ