326 lines
9.7 KiB
PHP
326 lines
9.7 KiB
PHP
<?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>
|