258 lines
7.5 KiB
PHP
258 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Cotizacion;
|
|
use App\Models\CotizacionItem;
|
|
use App\Models\Producto;
|
|
use App\Models\Client;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Str;
|
|
|
|
class CotizacionController extends Controller
|
|
{
|
|
// Listado de cotizaciones
|
|
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 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');
|
|
}
|
|
}
|
|
}
|