Cambios en ventas para ver el "ticket" cuando registras la venta
This commit is contained in:
@@ -41,7 +41,8 @@ class SaleController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($request) {
|
// Variable para guardar el objeto venta y usarlo fuera del closure
|
||||||
|
$sale = DB::transaction(function () use ($request) {
|
||||||
$totalSale = 0;
|
$totalSale = 0;
|
||||||
|
|
||||||
foreach ($request->items as $item) {
|
foreach ($request->items as $item) {
|
||||||
@@ -54,7 +55,7 @@ class SaleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
//cabecera
|
//cabecera
|
||||||
$sale = Sale::create([
|
$newSale = Sale::create([
|
||||||
//'user_id' => auth()->id(), //empleado logueado
|
//'user_id' => auth()->id(), //empleado logueado
|
||||||
'client_id' => $request->client_id,
|
'client_id' => $request->client_id,
|
||||||
'total' => $totalSale,
|
'total' => $totalSale,
|
||||||
@@ -65,7 +66,7 @@ class SaleController extends Controller
|
|||||||
$product = Product::find($item['product_id']);
|
$product = Product::find($item['product_id']);
|
||||||
|
|
||||||
SaleDetail::create([
|
SaleDetail::create([
|
||||||
'sale_id' => $sale->id,
|
'sale_id' => $newSale->id,
|
||||||
'product_id' => $product->id,
|
'product_id' => $product->id,
|
||||||
'quantity' => $item['quantity'],
|
'quantity' => $item['quantity'],
|
||||||
'price' => $product->price,
|
'price' => $product->price,
|
||||||
@@ -74,15 +75,27 @@ class SaleController extends Controller
|
|||||||
$product->stock_quantity = $nuevoStock;
|
$product->stock_quantity = $nuevoStock;
|
||||||
$product->save();
|
$product->save();
|
||||||
}
|
}
|
||||||
|
return $newSale; // Retornamos la venta creada fuera de la transacción
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. REDIRECCIÓN DE ÉXITO
|
// 4. REDIRECCIÓN AL DETALLE (FACTURA)
|
||||||
// Redirigimos al usuario con un mensaje flash [cite: 668]
|
// Usamos la variable $sale que nos devolvió la transacción
|
||||||
return redirect()->route('sales.create')->with('success', '¡Venta registrada correctamente!');
|
return redirect()->route('sales.show', $sale)->with('success', '¡Venta registrada exitosamente!');
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Si algo falló (ej: falta stock), volvemos atrás con el error
|
// Si algo falló (ej: falta stock), volvemos atrás con el error
|
||||||
return back()->with('error', 'Error en la venta: ' . $e->getMessage());
|
return back()->with('error', 'Error en la venta: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,7 @@
|
|||||||
${{ number_format($sale->total, 2, ',', '.') }}
|
${{ number_format($sale->total, 2, ',', '.') }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 text-center">
|
<td class="px-6 py-4 text-center">
|
||||||
<button class="text-blue-400 hover:text-blue-300 font-medium text-sm transition-colors">
|
<a href="{{ route('sales.show', $sale) }}" class="font-medium text-blue-400 hover:underline mr-3">Ver Detalle</a>
|
||||||
Ver Detalle
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<x-layout title="Detalle de Venta #{{ $sale->id }}">
|
||||||
|
|
||||||
|
<!-- Mensajes de Alerta -->
|
||||||
|
<x-ui.alert />
|
||||||
|
|
||||||
|
<!-- Encabezado de Navegación -->
|
||||||
|
<div class="w-full flex justify-between items-center mb-8 print:hidden">
|
||||||
|
<a href="{{ route('sales.index') }}" class="text-gray-400 hover:text-white flex items-center gap-2 transition-colors">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
|
||||||
|
Volver al Historial
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button onclick="window.print()" class="px-5 py-2.5 bg-neutral-800 text-neon-lime border border-neon-lime/30 font-bold rounded-lg hover:bg-neon-lime hover:text-neutral-900 transition-colors flex items-center gap-2">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"></path></svg>
|
||||||
|
Imprimir / Guardar PDF
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contenedor "Hoja" de la Factura -->
|
||||||
|
<div class="max-w-3xl mx-auto bg-white text-neutral-900 rounded-xl shadow-2xl overflow-hidden print:shadow-none print:w-full print:max-w-none">
|
||||||
|
|
||||||
|
<!-- Cabecera Factura -->
|
||||||
|
<div class="p-8 border-b-2 border-gray-100 flex justify-between items-start bg-gray-50 print:bg-white">
|
||||||
|
<div>
|
||||||
|
<!-- Logo Simulado -->
|
||||||
|
<div class="flex items-center gap-2 mb-4">
|
||||||
|
<div class="h-8 w-8 bg-neutral-900 rounded-full flex items-center justify-center text-neon-lime">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
|
||||||
|
</div>
|
||||||
|
<span class="text-2xl font-black uppercase tracking-wide text-neutral-900">
|
||||||
|
CICLES<span class="text-lime-800"> LAUCK</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
Av. Francisco Ramírez 1389<br>
|
||||||
|
Paraná, Entre Ríos<br>
|
||||||
|
Tel: 343 422-0103
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-right">
|
||||||
|
<h2 class="text-sm font-bold text-gray-400 uppercase tracking-widest mb-1">Comprobante de Venta</h2>
|
||||||
|
<div class="text-4xl font-mono font-bold text-neutral-900">#{{ str_pad($sale->id, 5, '0', STR_PAD_LEFT) }}</div>
|
||||||
|
<div class="mt-2 text-sm text-gray-600">
|
||||||
|
Fecha: <strong>{{ $sale->created_at->format('d/m/Y') }}</strong><br>
|
||||||
|
Hora: {{ $sale->created_at->format('H:i') }} hs
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Datos del Cliente -->
|
||||||
|
<div class="p-8 grid grid-cols-2 gap-8">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xs font-bold text-gray-400 uppercase mb-2">Facturado A:</h3>
|
||||||
|
@if($sale->client)
|
||||||
|
<p class="font-bold text-lg">{{ $sale->client->name }}</p>
|
||||||
|
<p class="text-gray-600 text-sm">{{ $sale->client->phone }}</p>
|
||||||
|
<p class="text-gray-600 text-sm">{{ $sale->client->email }}</p>
|
||||||
|
@if($sale->client->address)
|
||||||
|
<p class="text-gray-600 text-sm mt-1">{{ $sale->client->address }}</p>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<p class="font-bold text-lg text-gray-500 italic">Consumidor Final</p>
|
||||||
|
<p class="text-gray-400 text-sm">Venta anónima de mostrador</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-right">
|
||||||
|
<h3 class="text-xs font-bold text-gray-400 uppercase mb-2">Método de Pago:</h3>
|
||||||
|
<span class="inline-block bg-gray-100 text-gray-800 text-sm font-bold px-3 py-1 rounded border border-gray-200">
|
||||||
|
{{ $sale->payment_method }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla de Detalles -->
|
||||||
|
<div class="px-8 pb-8">
|
||||||
|
<table class="w-full text-sm text-left">
|
||||||
|
<thead class="bg-gray-100 text-gray-600 uppercase font-bold text-xs">
|
||||||
|
<tr>
|
||||||
|
<th class="px-4 py-3 rounded-l-lg">Producto</th>
|
||||||
|
<th class="px-4 py-3 text-right">Precio Unit.</th>
|
||||||
|
<th class="px-4 py-3 text-center">Cant.</th>
|
||||||
|
<th class="px-4 py-3 text-right rounded-r-lg">Subtotal</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-100">
|
||||||
|
@foreach($sale->details as $detail)
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-4">
|
||||||
|
<div class="font-bold text-gray-900">{{ $detail->product->name }}</div>
|
||||||
|
<div class="text-xs text-gray-500 font-mono">{{ $detail->product->sku }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-right font-mono text-gray-600">
|
||||||
|
${{ number_format($detail->price, 2, ',', '.') }}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-center font-bold text-gray-900">
|
||||||
|
{{ $detail->quantity }}
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-4 text-right font-mono font-bold text-gray-900">
|
||||||
|
${{ number_format($detail->price * $detail->quantity, 2, ',', '.') }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
<tfoot class="border-t-2 border-gray-100">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="px-4 pt-6 text-right text-gray-500 uppercase font-bold text-sm">Total a Pagar</td>
|
||||||
|
<td class="px-4 pt-6 text-right text-3xl font-black text-gray-900">
|
||||||
|
${{ number_format($sale->total, 2, ',', '.') }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer Factura -->
|
||||||
|
<div class="bg-gray-50 p-6 text-center border-t border-gray-100 print:bg-white">
|
||||||
|
<p class="text-sm text-gray-500 font-medium">¡Gracias por tu compra!</p>
|
||||||
|
<p class="text-xs text-gray-400 mt-1">Documento no valido como factura.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Estilos específicos para impresión -->
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
body * {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.max-w-3xl, .max-w-3xl * {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.max-w-3xl {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
/* Ocultar elementos del layout */
|
||||||
|
nav, footer, header {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</x-layout>
|
||||||
+1
-7
@@ -43,13 +43,7 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
Route::resource('productos', ProductosController::class)->parameters([
|
Route::resource('productos', ProductosController::class)->parameters([
|
||||||
'productos' => 'product'
|
'productos' => 'product'
|
||||||
]);
|
]);
|
||||||
|
Route::resource('sales', SaleController::class)->only(['index', 'create', 'store', 'show']);
|
||||||
Route::controller(SaleController::class)->prefix('sales')->name('sales.')->group(function () {
|
|
||||||
Route::get('/', 'index')->name('index');
|
|
||||||
Route::get('/create', 'create')->name('create');
|
|
||||||
Route::post('/', 'store')->name('store');
|
|
||||||
Route::get('/{sale}', 'show')->name('show');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user