withCount('serials') ->where('is_active', true); // Filtro por búsqueda de texto (nombre, SKU, código de barras) if ($request->has('q') && $request->q) { $products->where(function($query) use ($request) { $query->where('name', 'like', "%{$request->q}%") ->orWhere('sku', 'like', "%{$request->q}%") ->orWhere('barcode', $request->q); }); } // Filtro por categoría (independiente de la búsqueda de texto) if ($request->has('category_id') && $request->category_id) { $products->where('category_id', $request->category_id); } // Calcular el valor total del inventario $totalInventoryValue = DB::table('inventory_warehouse') ->join('prices', 'inventory_warehouse.inventory_id', '=', 'prices.inventory_id') ->join('inventories', 'inventory_warehouse.inventory_id', '=', 'inventories.id') ->where('inventories.is_active', true) ->sum(DB::raw('inventory_warehouse.stock * prices.cost')); $products = $products->orderBy('name') ->paginate(config('app.pagination')); return ApiResponse::OK->response([ 'products' => $products, 'total_inventory_value' => round($totalInventoryValue, 2) ]); } public function show(Inventory $inventario) { return ApiResponse::OK->response([ 'model' => $inventario->load(['category', 'price', 'unitOfMeasure'])->loadCount('serials') ]); } public function store(InventoryStoreRequest $request) { $product = $this->productService->createProduct($request->validated()); return ApiResponse::OK->response([ 'model' => $product ]); } public function update(InventoryUpdateRequest $request, Inventory $inventario) { $product = $this->productService->updateProduct($inventario, $request->validated()); return ApiResponse::OK->response([ 'model' => $product ]); } public function destroy(Inventory $inventario) { $inventario->delete(); return ApiResponse::OK->response(); } /** * Obtener productos disponibles en un almacén específico */ public function getProductsByWarehouse(Request $request, int $warehouseId) { $query = Inventory::query() ->with(['category', 'price', 'unitOfMeasure']) ->where('is_active', true) ->whereHas('warehouses', function ($q) use ($warehouseId) { $q->where('warehouse_id', $warehouseId) ->where('stock', '>', 0); }); // Filtro por búsqueda de texto if ($request->has('q') && $request->q) { $query->where(function($q) use ($request) { $q->where('name', 'like', "%{$request->q}%") ->orWhere('sku', 'like', "%{$request->q}%") ->orWhere('barcode', $request->q); }); } // Filtro por categoría if ($request->has('category_id') && $request->category_id) { $query->where('category_id', $request->category_id); } $products = $query->orderBy('name')->get(); // Agregar el stock específico de este almacén a cada producto $products->each(function ($product) use ($warehouseId) { $warehouseStock = $product->warehouses() ->where('warehouse_id', $warehouseId) ->first(); $product->warehouse_stock = $warehouseStock ? $warehouseStock->pivot->stock : 0; }); return ApiResponse::OK->response([ 'products' => $products, 'warehouse_id' => $warehouseId, ]); } /** * Importar productos desde Excel */ public function import(InventoryImportRequest $request) { try { $import = new ProductsImport(); Excel::import($import, $request->file('file')); $stats = $import->getStats(); return ApiResponse::OK->response([ 'message' => 'Importación completada exitosamente.', 'imported' => $stats['imported'], 'updated' => $stats['updated'], 'skipped' => $stats['skipped'], 'errors' => $stats['errors'], ]); } catch (ValidationException $e) { $failures = $e->failures(); $errors = []; foreach ($failures as $failure) { $errors[] = [ 'row' => $failure->row(), 'attribute' => $failure->attribute(), 'errors' => $failure->errors(), 'values' => $failure->values(), ]; } return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error de validación en el archivo.', 'errors' => $errors, ]); } catch (\Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Error al importar productos: ' . $e->getMessage(), ]); } } /** * Descargar plantilla de Excel para importación */ public function downloadTemplate() { $headers = [ 'nombre', 'sku', 'codigo_barras', 'categoria', 'precio_venta', 'impuesto' ]; $exampleData = [ [ 'nombre' => 'Samsung Galaxy A55', 'sku' => 'SAM-A55-BLK', 'codigo_barras' => '7502276853456', 'categoria' => 'Electrónica', 'precio_venta' => 7500.00, 'impuesto' => 16 ], [ 'nombre' => 'Coca Cola 600ml', 'sku' => 'COCA-600', 'codigo_barras' => '750227686666', 'categoria' => 'Bebidas', 'precio_venta' => 18.00, 'impuesto' => 8 ], [ 'nombre' => 'Laptop HP Pavilion 15', 'sku' => 'HP-LAP-15', 'codigo_barras' => '7502276854443', 'categoria' => 'Computadoras', 'precio_venta' => 12000.00, 'impuesto' => 16 ], ]; return Excel::download( new class($headers, $exampleData) implements FromArray, WithHeadings { use Exportable; public function __construct(private array $headers, private array $data) {} public function array(): array { return $this->data; } public function headings(): array { return $this->headers; } }, 'plantilla_productos.xlsx' ); } }