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 para crear cotización public function create() { $user = auth()->user(); $clientes = Client::where('company_id', $user->company_id)->get(); $productos = Producto::where('company_id', $user->company_id)->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', ]); // Generar token único para link $token = Str::uuid()->toString(); // Calcular totales $subtotal = 0; foreach ($request->productos as $item) { $subtotal += $item['cantidad'] * $item['precio_unitario']; } $iva = $subtotal * 0.16; // 16% IVA $total = $subtotal + $iva; $cotizacionData = [ 'company_id' => $user->company_id, 'cliente_id' => $request->cliente_id, 'token' => $token, 'estado' => 'pendiente', 'subtotal' => $subtotal, 'iva' => $iva, 'total' => $total ]; // Guardamos en variable para auditoría $cotizacion = Cotizacion::create($cotizacionData); // Guardar items 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'], ]); } // Auditoría audit_log( 'created', 'cotizaciones', $cotizacion, null, $cotizacion->toArray() ); return redirect()->route('cotizaciones.index') ->with('success', 'Cotización creada correctamente.'); } // Formulario para editar cotización public function edit(Cotizacion $cotizacion) { $this->authorizeCotizacion($cotizacion); $user = auth()->user(); $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 y sus items 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', ]); // Capturar estado anterior para auditoría $oldData = $cotizacion->toArray(); // Recalcular totales $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, ]); // Reemplazar items (simplificación) $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'], ]); } // Auditoría $newData = $cotizacion->fresh()->toArray(); audit_log( 'updated', 'cotizaciones', $cotizacion, $oldData, $newData ); 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.'); } // Mostrar cotización vía link público 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')); } // Aprobar cotización public function aprobar($token) { $cotizacion = Cotizacion::where('token', $token)->firstOrFail(); $cotizacion->update([ 'estado' => 'aprobada', 'aprobada_en' => now(), 'aprobada_por' => 'Cliente' ]); audit_log( 'updated', 'cotizaciones', $cotizacion, null, $cotizacion->toArray() ); return view('cotizaciones.aprobada', compact('cotizacion')); } // Rechazar cotización public function rechazar($token) { $cotizacion = Cotizacion::where('token', $token)->firstOrFail(); $cotizacion->update([ 'estado' => 'rechazada', 'aprobada_en' => now(), 'aprobada_por' => 'Cliente' ]); audit_log( 'updated', 'cotizaciones', $cotizacion, null, $cotizacion->toArray() ); 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'); } } }