load('cliente', 'items.producto'); $pdf = Pdf::loadView('cotizaciones.pdf', compact('cotizacion')) ->setPaper('letter', 'portrait'); return $pdf->download('Cotizacion_'.$cotizacion->id.'.pdf'); } public function index() { $user = auth()->user(); if ($user->hasRole('super-admin')) { $cotizaciones = Cotizacion::with(['cliente','items'])->latest()->get(); } else { $cotizaciones = Cotizacion::with(['cliente','items']) ->where('company_id', $user->company_id) ->latest() ->get(); } return view('cotizaciones.index', compact('cotizaciones')); } // Formulario POS para crear cotización public function create() { $user = auth()->user(); if ($user->hasRole('super-admin')) { $clientes = Client::orderBy('nombre')->get(); $productos = Producto::orderBy('nombre')->get(); } else { $clientes = Client::where('company_id', $user->company_id) ->orderBy('nombre') ->get(); $productos = Producto::where('company_id', $user->company_id) ->orderBy('nombre') ->get(); } return view('cotizaciones.create', compact('clientes','productos')); } // Guardar cotización + items public function store(Request $request) { $user = auth()->user(); $request->validate([ 'cliente_id' => 'required|exists:clients,id', 'productos' => 'required|array', 'productos.*.producto_id' => 'required|exists:productos,id', 'productos.*.cantidad' => 'required|integer|min:1', 'productos.*.precio_unitario' => 'required|numeric|min:0', ]); $token = Str::uuid()->toString(); $subtotal = 0; $totalDescuento = 0; foreach ($request->productos as $item) { $bruto = $item['cantidad'] * $item['precio_unitario']; // porcentaje enviado desde la vista $porcentaje = $item['descuento_porcentaje'] ?? 0; // monto del descuento $descuentoMonto = $item['descuento_monto'] ?? ($bruto * ($porcentaje / 100)); $subtotal += $bruto; $totalDescuento += $descuentoMonto; } $neto = $subtotal - $totalDescuento; $iva = $neto * 0.16; $total = $neto + $iva; $cotizacion = Cotizacion::create([ 'company_id' => $user->company_id, 'cliente_id' => $request->cliente_id, 'token' => $token, 'estado' => 'pendiente', 'subtotal' => $subtotal, 'iva' => $iva, 'total' => $total ]); foreach ($request->productos as $item) { CotizacionItem::create([ 'cotizacion_id' => $cotizacion->id, 'producto_id' => $item['producto_id'], 'cantidad' => $item['cantidad'], 'precio_unitario' => $item['precio_unitario'], 'descuento_porcentaje' => $item['descuento_porcentaje'] ?? 0, 'descuento_monto' => $item['descuento_monto'] ?? 0, 'total' => ($item['precio_unitario'] * $item['cantidad']) - ($item['descuento_monto'] ?? 0) ]); } audit_log( 'created', 'cotizaciones', $cotizacion, null, $cotizacion->toArray() ); return redirect()->route('cotizaciones.index') ->with('success', 'Cotización creada correctamente.'); } // Editar cotización public function edit(Cotizacion $cotizacion) { $this->authorizeCotizacion($cotizacion); $user = auth()->user(); if ($user->hasRole('super-admin')) { $clientes = Client::orderBy('nombre')->get(); $productos = Producto::orderBy('nombre')->get(); } else { $clientes = Client::where('company_id', $user->company_id)->get(); $productos = Producto::where('company_id', $user->company_id)->get(); } return view('cotizaciones.edit', compact('cotizacion','clientes','productos')); } // Actualizar cotización public function update(Request $request, Cotizacion $cotizacion) { $this->authorizeCotizacion($cotizacion); $request->validate([ 'cliente_id' => 'required|exists:clients,id', 'productos' => 'required|array', 'productos.*.producto_id' => 'required|exists:productos,id', 'productos.*.cantidad' => 'required|integer|min:1', 'productos.*.precio_unitario' => 'required|numeric|min:0', ]); $oldData = $cotizacion->toArray(); $subtotal = 0; foreach ($request->productos as $item) { $subtotal += $item['cantidad'] * $item['precio_unitario']; } $iva = $subtotal * 0.16; $total = $subtotal + $iva; $cotizacion->update([ 'cliente_id' => $request->cliente_id, 'subtotal' => $subtotal, 'iva' => $iva, 'total' => $total, ]); $cotizacion->items()->delete(); foreach ($request->productos as $item) { CotizacionItem::create([ 'cotizacion_id' => $cotizacion->id, 'producto_id' => $item['producto_id'], 'cantidad' => $item['cantidad'], 'precio_unitario' => $item['precio_unitario'], 'total' => $item['cantidad'] * $item['precio_unitario'], ]); } audit_log( 'updated', 'cotizaciones', $cotizacion, $oldData, $cotizacion->fresh()->toArray() ); return redirect()->route('cotizaciones.index') ->with('success', 'Cotización actualizada correctamente.'); } // Eliminar cotización public function destroy(Cotizacion $cotizacion) { $this->authorizeCotizacion($cotizacion); $deletedData = $cotizacion->toArray(); $cotizacion->delete(); audit_log( 'deleted', 'cotizaciones', $cotizacion, $deletedData, null ); return redirect()->route('cotizaciones.index') ->with('success', 'Cotización eliminada correctamente.'); } // Vista pública public function showCliente($token) { $cotizacion = Cotizacion::where('token', $token) ->with('items.producto') ->firstOrFail(); if ($cotizacion->estado === 'pendiente') { $cotizacion->update(['estado' => 'vista']); } return view('cotizaciones.show_cliente', compact('cotizacion')); } public function aprobar($token) { $cotizacion = Cotizacion::where('token', $token)->firstOrFail(); $cotizacion->update([ 'estado' => 'aprobada', 'aprobada_en' => now(), 'aprobada_por' => 'Cliente' ]); return view('cotizaciones.aprobada', compact('cotizacion')); } public function rechazar($token) { $cotizacion = Cotizacion::where('token', $token)->firstOrFail(); $cotizacion->update([ 'estado' => 'rechazada', 'aprobada_en' => now(), 'aprobada_por' => 'Cliente' ]); return view('cotizaciones.rechazada', compact('cotizacion')); } // Seguridad multiempresa private function authorizeCotizacion(Cotizacion $cotizacion) { $user = auth()->user(); if (!$user->hasRole('super-admin') && $cotizacion->company_id !== $user->company_id) { abort(403, 'No autorizado'); } } }