feat: Implement sale management features including controllers, services, resources, and routes; add PDF generation for sales and modifications

This commit is contained in:
2026-08-04 11:44:13 -03:00
parent 3f9bcd84c4
commit 1de08c1ca4
22 changed files with 700 additions and 104 deletions

View File

@@ -0,0 +1,63 @@
<!doctype html>
<html lang="es">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
@page { margin: 28px 30px 58px; }
body { color: #17211b; font-family: DejaVu Sans, sans-serif; font-size: 8px; margin: 0; }
h1 { font-size: 21px; margin: 0 0 3px; }
.subtitle { color: #66736b; margin: 0 0 18px; }
table { border-collapse: collapse; table-layout: fixed; width: 100%; }
thead { display: table-header-group; }
tr { page-break-inside: avoid; }
th { background: #26382e; color: #fff; font-size: 7px; letter-spacing: .35px; padding: 7px 5px; text-align: left; text-transform: uppercase; }
td { border-bottom: 1px solid #dfe7e2; overflow-wrap: break-word; padding: 7px 5px; vertical-align: top; }
tbody tr:nth-child(even) { background: #f7f9f8; }
.date { width: 10%; }
.time { width: 7%; }
.sale { width: 8%; }
.customer { width: 17%; }
.attribute { width: 10%; }
.value { width: 16%; }
.actor { width: 16%; }
.empty { color: #66736b; padding: 24px; text-align: center; }
</style>
</head>
<body>
<h1>Historial de modificaciones de ventas</h1>
<p class="subtitle">{{ $tenant->nombre }} · Generado el {{ $generatedAt->format('d/m/Y H:i') }}</p>
<table>
<thead>
<tr>
<th class="date">Fecha</th>
<th class="time">Hora</th>
<th class="sale">Venta</th>
<th class="customer">Cliente</th>
<th class="attribute">Campo</th>
<th class="value">Valor anterior</th>
<th class="value">Valor nuevo</th>
<th class="actor">Modificado por</th>
</tr>
</thead>
<tbody>
@forelse ($modifications as $modification)
@php($sale = $modification->trackable)
<tr>
<td>{{ $modification->changed_at->format('d/m/Y') }}</td>
<td>{{ $modification->changed_at->format('H:i:s') }}</td>
<td>#{{ $modification->trackable_id }}</td>
<td>{{ $sale?->nombre_apellido ?: 'Sin nombre' }}</td>
<td>{{ $modification->attribute }}</td>
<td>{{ $modification->old_value ?? '-' }}</td>
<td>{{ $modification->new_value ?? '-' }}</td>
<td>{{ $modification->user?->nombre_apellido ?? 'Sistema' }}</td>
</tr>
@empty
<tr><td class="empty" colspan="8">Todavía no hay modificaciones registradas.</td></tr>
@endforelse
</tbody>
</table>
</body>
</html>

View File

@@ -0,0 +1,65 @@
<!doctype html>
<html lang="es">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
@page { margin: 28px 34px 58px; }
body { color: #17211b; font-family: DejaVu Sans, sans-serif; font-size: 9px; margin: 0; }
h1 { font-size: 21px; margin: 0 0 3px; }
.subtitle { color: #66736b; margin: 0 0 18px; }
.summary { background: #eef5f1; border-left: 4px solid #198754; margin-bottom: 16px; padding: 9px 12px; }
.summary strong { font-size: 14px; }
table { border-collapse: collapse; width: 100%; }
thead { display: table-header-group; }
tr { page-break-inside: avoid; }
th { background: #26382e; color: #fff; font-size: 8px; letter-spacing: .4px; padding: 7px 6px; text-align: left; text-transform: uppercase; }
td { border-bottom: 1px solid #dfe7e2; padding: 7px 6px; vertical-align: top; }
tbody tr:nth-child(even) { background: #f7f9f8; }
.number { text-align: right; }
.center { text-align: center; }
.empty { color: #66736b; padding: 24px; text-align: center; }
</style>
</head>
<body>
<h1>Historial de ventas</h1>
<p class="subtitle">{{ $tenant->nombre }} · Generado el {{ $generatedAt->format('d/m/Y H:i') }}</p>
<div class="summary">
Total de ventas confirmadas en este reporte: <strong>${{ number_format((float) $confirmedSalesTotal, 2, ',', '.') }}</strong>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Fecha</th>
<th>Cliente</th>
<th class="center">Cantidad</th>
<th>Estado</th>
<th class="number">Importe</th>
<th class="center">Tickets</th>
</tr>
</thead>
<tbody>
@forelse ($sales as $sale)
<tr>
<td>#{{ $sale->id }}</td>
<td>{{ $sale->created_at?->format('d/m/Y H:i') ?? '-' }}</td>
<td>{{ $sale->nombre_apellido ?: 'Sin nombre' }}</td>
<td class="center">{{ (int) ($sale->quantity ?? 0) }}</td>
<td>{{ match ($sale->status) {
'paid' => 'Confirmado',
'created', 'pending_payment' => 'Esperando pago',
default => 'Anulado',
} }}</td>
<td class="number">${{ number_format((float) $sale->total, 2, ',', '.') }}</td>
<td class="center">{{ (int) ($sale->tickets_count ?? 0) }}</td>
</tr>
@empty
<tr><td class="empty" colspan="7">No hay ventas para los criterios seleccionados.</td></tr>
@endforelse
</tbody>
</table>
</body>
</html>