input('search'); // Búsqueda por ID $clientId = $request->input('client_id'); // Filtro Cliente $dateFrom = $request->input('date_from'); // Fecha Desde $dateTo = $request->input('date_to'); // Fecha Hasta // Query principal $sales = Sale::query() ->with(['client', 'details']) // 1. Filtro por ID de venta (si escriben en el buscador texto) ->when($search, function ($query, $search) { return $query->where('id', 'like', "%{$search}%"); }) // 2. Filtro por Cliente (Select2) ->when($clientId, function ($query, $clientId) { return $query->where('client_id', $clientId); }) // 3. Filtro por Rango de Fechas ->when($dateFrom, function ($query, $dateFrom) { return $query->whereDate('created_at', '>=', $dateFrom); }) ->when($dateTo, function ($query, $dateTo) { return $query->whereDate('created_at', '<=', $dateTo); }) ->latest() // Ordenar por fecha descendente ->paginate(10) ->withQueryString(); // Mantener filtros al cambiar de página // Traemos clientes para el select del filtro (solo id y nombre para optimizar) $clients = Client::orderBy('name')->get(['id', 'name']); return view('sales.index', compact('sales', 'clients')); } public function create() { // Traemos productos con stock y clientes para los selectores $products = Product::where('stock_quantity', '>', 0)->get(); $clients = Client::orderBy('name')->get(); return view('sales.create', compact('products', 'clients')); } public function store(Request $request) { // Validación estricta $request->validate([ 'client_id' => 'nullable|exists:clients,id', 'payment_method' => 'required|string', 'items' => 'required|array', 'items.*.product_id' => 'required|exists:products,id', 'items.*.quantity' => 'required|integer|min:1', ]); try { // Variable para guardar el objeto venta y usarlo fuera del closure $sale = DB::transaction(function () use ($request) { $totalSale = 0; foreach ($request->items as $item) { $product = Product::find($item['product_id']); $totalSale += $product->price * $item['quantity']; if ($product->stock_quantity < $item['quantity']) { throw new \Exception("No hay suficiente stock de " . $product->name); } } //cabecera $newSale = Sale::create([ //'user_id' => auth()->id(), //empleado logueado 'client_id' => $request->client_id, 'total' => $totalSale, 'payment_method' => $request->payment_method, ]); // 3. Guardar Detalle y Restar Stock foreach ($request->items as $itemData) { $product = Product::findOrFail($itemData['product_id']); SaleDetail::create([ 'sale_id' => $newSale->id, 'product_id' => $product->id, 'quantity' => $itemData['quantity'], 'price' => $product->price, ]); $product->decrement('stock_quantity', $itemData['quantity']); } return $newSale; // Retornamos la venta creada fuera de la transacción }); // 4. REDIRECCIÓN AL DETALLE return redirect()->route('sales.show', $sale)->with('success', '¡Venta registrada exitosamente!'); } catch (\Exception $e) { return back()->with('error', $e->getMessage())->withInput(); } } /** * Muestra el detalle de una venta específica. */ public function show(Sale $sale) { // Cargamos la venta con el cliente y los detalles $sale->load(['client', 'details.product']); return view('sales.show', compact('sale')); } }