39957-vm/receipt.php
Flatlogic Bot 3cfcfefee3 v 0.1
2026-05-11 21:40:02 +00:00

206 lines
9.5 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/pos_app.php';
$projectName = pos_project_name();
$projectDescription = pos_project_description();
$projectImageUrl = pos_project_image_url();
$pageTitle = $projectName . ' | Recibo';
$currentUser = pos_require_login();
$flashes = pos_pull_flashes();
$saleId = (int)($_GET['id'] ?? 0);
$sale = $saleId > 0 ? pos_sale_by_id($saleId) : null;
$cssVersion = file_exists(__DIR__ . '/assets/css/custom.css') ? (string)filemtime(__DIR__ . '/assets/css/custom.css') : (string)time();
$jsVersion = file_exists(__DIR__ . '/assets/js/main.js') ? (string)filemtime(__DIR__ . '/assets/js/main.js') : (string)time();
$flashClassMap = [
'success' => 'text-bg-success',
'danger' => 'text-bg-danger',
'warning' => 'text-bg-warning',
'secondary' => 'text-bg-secondary',
'info' => 'text-bg-primary',
];
if (!$sale) {
http_response_code(404);
}
?>
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?= htmlspecialchars($pageTitle) ?></title>
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta name="robots" content="noindex, nofollow" />
<meta property="og:title" content="<?= htmlspecialchars($pageTitle) ?>" />
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<meta property="twitter:title" content="<?= htmlspecialchars($pageTitle) ?>" />
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php if ($projectImageUrl !== ''): ?>
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?= htmlspecialchars($cssVersion) ?>" rel="stylesheet">
</head>
<body class="app-body receipt-body">
<header class="topbar border-bottom">
<nav class="navbar py-3">
<div class="container-xxl d-flex justify-content-between align-items-center gap-3">
<a class="navbar-brand brand-mark" href="index.php">
<span class="brand-mark__icon"><i class="bi bi-receipt-cutoff"></i></span>
<span>
<small class="text-uppercase text-muted d-block">Detalle</small>
<strong><?= htmlspecialchars($projectName) ?></strong>
</span>
</a>
<div class="d-flex gap-2 flex-wrap justify-content-end">
<span class="user-chip">
<strong><?= htmlspecialchars($currentUser['name']) ?></strong>
<small><?= htmlspecialchars($currentUser['role_label']) ?></small>
</span>
<a class="btn btn-outline-secondary" href="index.php#ventas">Volver al POS</a>
</div>
</div>
</nav>
</header>
<div class="toast-container position-fixed top-0 end-0 p-3">
<?php foreach ($flashes as $flash): ?>
<?php $flashClass = $flashClassMap[$flash['type']] ?? 'text-bg-dark'; ?>
<div class="toast align-items-center border-0 js-app-toast <?= htmlspecialchars($flashClass) ?>" role="status" aria-live="polite" aria-atomic="true" data-bs-delay="3600">
<div class="d-flex">
<div class="toast-body"><?= htmlspecialchars($flash['message']) ?></div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Cerrar"></button>
</div>
</div>
<?php endforeach; ?>
</div>
<main class="container-xxl py-4 py-lg-5">
<?php if (!$sale): ?>
<section class="panel receipt-panel mx-auto" style="max-width: 760px;">
<span class="eyebrow">Detalle no disponible</span>
<h1 class="section-title">No encontramos ese recibo.</h1>
<p class="section-copy">Puede que el identificador sea incorrecto o que el recibo ya no exista en esta base de datos.</p>
<a class="btn btn-dark" href="index.php#ventas">Volver a ventas recientes</a>
</section>
<?php else: ?>
<section class="row g-4 align-items-start">
<div class="col-lg-8">
<article class="panel receipt-panel printable-area" id="receipt-card">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
<div>
<span class="eyebrow">Recibo simple</span>
<h1 class="display-heading mb-1"><?= htmlspecialchars((string)$sale['receipt_number']) ?></h1>
<p class="section-copy mb-0">Venta registrada el <?= htmlspecialchars(pos_format_datetime((string)$sale['created_at'])) ?>.</p>
</div>
<span class="badge text-bg-light border px-3 py-2"><?= htmlspecialchars((string)$sale['cashier_role']) ?></span>
</div>
<div class="receipt-metadata mb-4">
<div>
<span>Cajero</span>
<strong><?= htmlspecialchars((string)$sale['cashier_name']) ?></strong>
</div>
<div>
<span>Código</span>
<strong><?= htmlspecialchars((string)$sale['cashier_code']) ?></strong>
</div>
<div>
<span>Items</span>
<strong><?= htmlspecialchars((string)$sale['item_count']) ?></strong>
</div>
<div>
<span>Total</span>
<strong><?= htmlspecialchars(pos_format_money((float)$sale['total'])) ?></strong>
</div>
</div>
<div class="table-responsive">
<table class="table table-borderless receipt-table align-middle mb-0">
<thead>
<tr>
<th>Producto</th>
<th>Categoría</th>
<th class="text-center">Cantidad</th>
<th class="text-end">Precio</th>
<th class="text-end">Importe</th>
</tr>
</thead>
<tbody>
<?php foreach ($sale['items'] as $item): ?>
<tr>
<td>
<strong><?= htmlspecialchars((string)($item['name'] ?? 'Producto')) ?></strong>
<div class="text-muted small"><?= htmlspecialchars((string)($item['unit_label'] ?? 'unidad')) ?></div>
</td>
<td><?= htmlspecialchars((string)($item['category'] ?? '—')) ?></td>
<td class="text-center"><?= htmlspecialchars((string)($item['quantity'] ?? 0)) ?></td>
<td class="text-end"><?= htmlspecialchars(pos_format_money((float)($item['price'] ?? 0))) ?></td>
<td class="text-end"><strong><?= htmlspecialchars(pos_format_money((float)($item['line_total'] ?? 0))) ?></strong></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="receipt-total-card mt-4">
<div>
<span>Subtotal</span>
<strong><?= htmlspecialchars(pos_format_money((float)$sale['subtotal'])) ?></strong>
</div>
<div>
<span>Total a pagar</span>
<strong><?= htmlspecialchars(pos_format_money((float)$sale['total'])) ?></strong>
</div>
</div>
<div class="d-print-none d-flex flex-wrap gap-3 mt-4">
<a class="btn btn-dark btn-lg" href="index.php#catalogo">
<i class="bi bi-plus-circle me-2"></i>Nueva venta
</a>
<button class="btn btn-outline-secondary btn-lg" type="button" id="print-receipt">
<i class="bi bi-printer me-2"></i>Imprimir recibo
</button>
</div>
</article>
</div>
<div class="col-lg-4 d-print-none">
<aside class="panel h-100">
<span class="eyebrow">Confirmación</span>
<h2 class="section-title">La venta quedó registrada.</h2>
<p class="section-copy">Ya puedes iniciar una nueva venta o volver al listado para revisar otros recibos.</p>
<div class="mini-grid single-column mt-4">
<div class="mini-card">
<i class="bi bi-check2-circle"></i>
<strong>Stock actualizado</strong>
<p>El inventario ya se descontó automáticamente.</p>
</div>
<div class="mini-card">
<i class="bi bi-journal-text"></i>
<strong>Historial guardado</strong>
<p>El recibo queda visible en “Ventas recientes”.</p>
</div>
<div class="mini-card">
<i class="bi bi-arrow-repeat"></i>
<strong>Siguiente paso</strong>
<p>Regresa al catálogo para iniciar otra operación.</p>
</div>
</div>
</aside>
</div>
</section>
<?php endif; ?>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" defer></script>
<script src="assets/js/main.js?v=<?= htmlspecialchars($jsVersion) ?>" defer></script>
</body>
</html>