diff --git a/app/Http/Controllers/ProductImportController.php b/app/Http/Controllers/ProductImportController.php index fead2fd..f654dff 100644 --- a/app/Http/Controllers/ProductImportController.php +++ b/app/Http/Controllers/ProductImportController.php @@ -180,7 +180,10 @@ public function store(Request $request) // (Though usually file is sorted, better safe) $groupedData = collect($previewData)->groupBy('name'); - DB::transaction(function () use ($groupedData, &$count) { + $allSkus = collect($previewData)->pluck('sku')->unique()->filter()->toArray(); + $existingSkus = ProductVariant::whereIn('sku', $allSkus)->pluck('sku')->map(fn($sku) => strtolower($sku))->flip()->toArray(); + + DB::transaction(function () use ($groupedData, &$count, $existingSkus) { foreach ($groupedData as $productName => $variants) { // Use the FIRST valid row's creation data for the product // Find a row that has valid product data? Or just take the first one? @@ -218,7 +221,7 @@ public function store(Request $request) if (!empty($row['errors'])) continue; // Check duplicate SKU again to be safe - if (ProductVariant::where('sku', $row['sku'])->exists()) continue; + if (isset($existingSkus[strtolower($row['sku'])])) continue; $variantImage = null; // Currently template supports 1 image per row, usually mapping to Product Image. // If user provides specific variant image logic, we'd need another column. diff --git a/tests/Feature/ProductImportPerformanceTest.php b/tests/Feature/ProductImportPerformanceTest.php new file mode 100644 index 0000000..50d6c1c --- /dev/null +++ b/tests/Feature/ProductImportPerformanceTest.php @@ -0,0 +1,96 @@ +create(); + $this->actingAs($user); + + $category = Category::create(['name' => 'Test Cat', 'code' => 'TC']); + $subCategory = SubCategory::create(['name' => 'Test Sub', 'category_id' => $category->id, 'code' => 'TS']); + $unit = Unit::create(['name' => 'Test Unit', 'short_name' => 'TU']); + + // Seed some existing data to simulate a real-world scenario where check is necessary + $existingCount = 50; + for ($i = 0; $i < $existingCount; $i++) { + $p = Product::create([ + 'name' => "Existing Product $i", + 'category_id' => $category->id, + 'sub_category_id' => $subCategory->id, + 'description' => 'Test', + ]); + ProductVariant::create([ + 'product_id' => $p->id, + 'unit_id' => $unit->id, + 'sku' => "SKU-$i", + 'selling_price' => 100, + 'quantity' => 10, + 'unit_value' => 1, + ]); + } + + // Prepare import data + $importCount = 500; + $previewData = []; + + for ($i = 0; $i < $importCount; $i++) { + // First 50 are duplicates (SKU-0 to SKU-49) + // Next are new + $sku = "SKU-$i"; + + $previewData[] = [ + 'row_id' => $i, + 'name' => "Import Product $i", + 'category_id' => $category->id, + 'sub_category_id' => $subCategory->id, + 'description' => 'Desc', + 'unit_id' => $unit->id, + 'unit_value' => 1, + 'sku' => $sku, + 'selling_price' => 120, + 'limit_price' => 110, + 'quantity' => 20, + 'alert_quantity' => 5, + 'image_url' => null, + 'errors' => [] + ]; + } + + session(['product_import_preview_data' => $previewData]); + + // Enable query logging to count queries + DB::enableQueryLog(); + + $startTime = microtime(true); + + $response = $this->post(route('products.import.store')); + + $endTime = microtime(true); + $duration = $endTime - $startTime; + $queryLog = DB::getQueryLog(); + $queryCount = count($queryLog); + + echo "\nPerformance Results:\n"; + echo "Time: " . number_format($duration, 4) . " seconds\n"; + echo "Queries: " . $queryCount . "\n"; + + // Assert redirect to confirm no crash + $response->assertRedirect(route('products.index')); + } +}