Files
whatsapp/modules/registro_exams/views/etiqueta.php
T
2026-04-18 23:41:01 -05:00

326 lines
9.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* modules/registro_exams/views/etiqueta.php
* Vista de impresión A6 (148 × 105 mm) para etiqueta de muestra.
* URL: /erp.php?m=registro_exams&v=etiqueta&id=N
*
* - Se abre en nueva pestaña y lanza window.print() automáticamente.
* - Sin sidebar; página mínima optimizada para impresión.
*/
require_once APP_ROOT . '/config/config.php';
$ordenId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if ($ordenId <= 0) {
http_response_code(400);
die('Orden no especificada.');
}
// Cargar datos de la orden directamente (sin AJAX, para que el HTML sea self-contained)
try {
$pdo = Database::getInstance()->getConnection();
$stmt = $pdo->prepare('
SELECT
o.id, o.codigo, o.prioridad, o.estado,
o.requiere_ayuno, o.horas_ayuno,
o.medico_nombre, o.diagnostico, o.creado_at,
p.nombre_completo, p.numero_documento, p.tipo_documento,
p.fecha_nacimiento, p.eps
FROM exam_ordenes o
JOIN lab_pacientes p ON p.id = o.paciente_id
WHERE o.id = ?
');
$stmt->execute([$ordenId]);
$orden = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$orden) {
http_response_code(404);
die('Orden no encontrada.');
}
// Ítems
$stmt = $pdo->prepare('
SELECT ei.estado, et.nombre AS tipo_nombre, et.codigo AS tipo_codigo,
et.requiere_ayuno, et.horas_ayuno
FROM exam_items ei
JOIN exam_tipos et ON et.id = ei.exam_tipo_id
WHERE ei.orden_id = ?
ORDER BY et.nombre
');
$stmt->execute([$ordenId]);
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Edad
$edad = null;
if ($orden['fecha_nacimiento']) {
$edad = (new DateTime($orden['fecha_nacimiento']))->diff(new DateTime())->y;
}
// Ayuno requerido (por la orden o por algún ítem)
$ayunoReq = (bool) $orden['requiere_ayuno'];
foreach ($items as $it) {
if ($it['requiere_ayuno']) { $ayunoReq = true; break; }
}
$horasValues = array_filter(array_column($items, 'horas_ayuno'));
$horasAyuno = $orden['horas_ayuno'] ?? ($horasValues ? max($horasValues) : 0);
} catch (\Throwable $e) {
http_response_code(500);
die('Error al cargar datos: ' . htmlspecialchars($e->getMessage()));
}
$fecha = $orden['creado_at']
? (new DateTime($orden['creado_at']))->format('d/m/Y H:i')
: date('d/m/Y');
// Generar código de barras como CSS barcode (sin librería externa)
// Usaremos simplemente el código textual + formato visual
$codigoBarras = $orden['codigo'];
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Etiqueta <?= htmlspecialchars($codigoBarras) ?></title>
<style>
/* ── Reset e impresión A6 ── */
* { box-sizing: border-box; margin: 0; padding: 0; }
@page {
size: A6 landscape; /* 148mm × 105mm en horizontal */
margin: 4mm;
}
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 9pt;
color: #000;
background: #fff;
width: 140mm;
}
/* Pantalla previa */
@media screen {
body {
width: 148mm;
min-height: 105mm;
margin: 10mm auto;
border: 1px dashed #ccc;
padding: 4mm;
background: #fff;
}
.no-print { display: block; }
}
@media print {
.no-print { display: none !important; }
}
/* ── Layout etiqueta ── */
.etiqueta {
width: 100%;
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 1.5pt solid #000;
padding-bottom: 2mm;
margin-bottom: 2mm;
}
.lab-nombre {
font-size: 10pt;
font-weight: bold;
text-transform: uppercase;
}
.codigo {
font-size: 13pt;
font-weight: bold;
letter-spacing: 1px;
text-align: right;
}
.barcode {
font-family: 'Libre Barcode 128', monospace;
font-size: 26pt;
letter-spacing: 0;
line-height: 1;
text-align: right;
}
.paciente-nombre {
font-size: 11pt;
font-weight: bold;
line-height: 1.2;
}
.meta-row {
display: flex;
gap: 6mm;
margin-top: 1mm;
font-size: 8.5pt;
}
.meta-item { white-space: nowrap; }
.meta-label { color: #555; font-size: 7.5pt; display: block; }
.examenes-titulo {
font-weight: bold;
font-size: 8pt;
text-transform: uppercase;
color: #444;
margin-top: 2.5mm;
border-top: .5pt solid #999;
padding-top: 1.5mm;
}
.examenes-lista {
font-size: 8pt;
line-height: 1.5;
}
.badge-prio {
display: inline-block;
font-size: 7pt;
font-weight: bold;
padding: 0.5mm 1.5mm;
border-radius: 2pt;
border: 1pt solid #000;
text-transform: uppercase;
}
.prio-stat { background: #000; color: #fff; }
.prio-urgente { background: #ff0; color: #000; }
.prio-normal { border-color: #aaa; color: #555; }
.aviso-ayuno {
margin-top: 2mm;
font-size: 8pt;
font-weight: bold;
border: 1pt solid #f90;
padding: 1mm 2mm;
border-radius: 2pt;
color: #a60;
}
.footer {
margin-top: 2mm;
font-size: 7pt;
color: #666;
border-top: .5pt solid #ccc;
padding-top: 1mm;
display: flex;
justify-content: space-between;
}
/* ── Botón de impresión (solo pantalla) ── */
.btn-imprimir {
display: block;
margin: 8mm auto;
padding: 6px 20px;
font-size: 12px;
cursor: pointer;
background: #0d6efd;
color: #fff;
border: none;
border-radius: 4px;
}
</style>
<?php
// Intentar cargar Libre Barcode si hay CDN (solo para pantalla; en impresión el texto es suficiente)
?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Libre+Barcode+128+Text&display=swap" rel="stylesheet">
</head>
<body>
<!-- Botón solo en pantalla -->
<div class="no-print" style="text-align:center;margin-bottom:4mm">
<button class="btn-imprimir" onclick="window.print()">🖨 Imprimir etiqueta</button>
<a href="<?= BASE_URL ?>/erp.php?m=registro_exams&v=orden&id=<?= $ordenId ?>"
style="display:inline-block;margin-left:8px;font-size:12px;color:#666">← Volver a orden</a>
</div>
<div class="etiqueta">
<!-- Encabezado: nombre lab + código -->
<div class="header">
<div>
<div class="lab-nombre"><?= defined('APP_NAME') ? htmlspecialchars(APP_NAME) : 'Laboratorio Clínico' ?></div>
<div style="font-size:7.5pt;color:#555">Exámenes de laboratorio en sede</div>
</div>
<div style="text-align:right">
<div class="codigo"><?= htmlspecialchars($codigoBarras) ?></div>
<div class="barcode"><?= htmlspecialchars($codigoBarras) ?></div>
</div>
</div>
<!-- Paciente -->
<div class="paciente-nombre"><?= htmlspecialchars($orden['nombre_completo']) ?></div>
<div class="meta-row">
<div class="meta-item">
<span class="meta-label">Documento</span>
<?= htmlspecialchars($orden['tipo_documento'] . ' ' . $orden['numero_documento']) ?>
</div>
<?php if ($edad !== null): ?>
<div class="meta-item">
<span class="meta-label">Edad</span>
<?= $edad ?> años
</div>
<?php endif; ?>
<?php if ($orden['eps']): ?>
<div class="meta-item">
<span class="meta-label">EPS</span>
<?= htmlspecialchars($orden['eps']) ?>
</div>
<?php endif; ?>
<div class="meta-item" style="margin-left:auto">
<span class="meta-label">Prioridad</span>
<span class="badge-prio prio-<?= $orden['prioridad'] ?>">
<?= strtoupper($orden['prioridad']) ?>
</span>
</div>
</div>
<!-- Exámenes -->
<div class="examenes-titulo">
<i>Exámenes solicitados (<?= count($items) ?>)</i>
</div>
<div class="examenes-lista">
<?php foreach ($items as $i => $item): ?>
<?= ($i + 1) ?>. <?= htmlspecialchars($item['tipo_nombre']) ?>
<span style="color:#888;font-size:7.5pt">(<?= htmlspecialchars($item['tipo_codigo']) ?>)</span><?php
if (!$item['requiere_ayuno']): ?><?php endif; ?>
<?php if ($i < count($items) - 1): ?> ·
<?php endif; ?>
<?php endforeach; ?>
</div>
<!-- Aviso ayuno -->
<?php if ($ayunoReq): ?>
<div class="aviso-ayuno">
⚠ REQUIERE AYUNO<?= $horasAyuno ? " DE {$horasAyuno} HORAS" : '' ?>
</div>
<?php endif; ?>
<!-- Pie: médico + fecha -->
<div class="footer">
<span>Médico: <?= htmlspecialchars($orden['medico_nombre'] ?: '—') ?></span>
<span><?= $fecha ?></span>
</div>
</div><!-- /etiqueta -->
<script>
// Auto-print al abrir
window.addEventListener('load', function () {
// Pequeño delay para cargar la fuente barcode
setTimeout(function () { window.print(); }, 800);
});
</script>
</body>
</html>