Feature 1 — Ciclos configurables por paciente: - ver_formulario_enviado.php: pantalla de configuración aparece la primera vez que el profesional abre el formulario (antes de firmar); permite seleccionar qué tomas realizar para cada protocolo. $_mpSkippedSepIds oculta las secciones no seleccionadas en render. $_mpMap se reconstruye desde los grupos filtrados para que next_firma_id salte tomas omitidas. - configurar_tomas.php (nuevo): guarda _tomas_config en datos_respuestas del consentimiento; valida que cada grupo tenga al menos una toma. - guardar_toma.php: aplica el filtro _tomas_config al array firmasCampos para que el conteo de tomas firmadas/pendientes sea correcto. Feature 2 — Gestión de tipos de examen (admin): - lab_tomas_config.php (nuevo): página admin que muestra los exámenes actuales con sus tomas y permite añadir nuevos tipos con su esquema completo (separador + hora + resultado + firma_profesional por toma). - api/lab/save_tomas_config.php (nuevo): modifica el esquema JSON del formulario id=15; add_exam agrega opción al selector y los campos de cada toma; delete_exam elimina la opción y los separadores exclusivos. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
152 lines
5.9 KiB
PHP
152 lines
5.9 KiB
PHP
<?php
|
|
/**
|
|
* POST /api/lab/save_tomas_config.php
|
|
* Gestiona los tipos de examen del formulario de Tomas Prolongadas (lab_formularios.id=15).
|
|
*
|
|
* Body JSON:
|
|
* action string 'add_exam' | 'delete_exam'
|
|
* exam_name string Nombre del nuevo tipo de examen
|
|
* tomas array [{label, tipo:'minutos'|'hora_fija', valor}] (solo add_exam)
|
|
*/
|
|
require_once __DIR__ . '/_helpers.php';
|
|
requireAdmin();
|
|
requireMethod('POST');
|
|
|
|
$body = inputJson();
|
|
$action = trim($body['action'] ?? '');
|
|
|
|
if (!in_array($action, ['add_exam', 'delete_exam'], true)) jsonError('action inválida.');
|
|
|
|
$db = Database::getInstance();
|
|
$row = $db->fetch("SELECT id, esquema FROM lab_formularios WHERE id = 15 LIMIT 1");
|
|
if (!$row) jsonError('Formulario de tomas no encontrado.', 404);
|
|
|
|
$esquema = json_decode($row['esquema'], true);
|
|
if (!is_array($esquema)) jsonError('Esquema del formulario no válido.', 500);
|
|
|
|
// ── Localizar el campo selector de tipo de examen ────────────
|
|
$idxSelector = null;
|
|
foreach ($esquema as $i => $c) {
|
|
if (($c['id'] ?? '') === '_c8j2g16') { $idxSelector = $i; break; }
|
|
}
|
|
if ($idxSelector === null) jsonError('Campo selector de examen (_c8j2g16) no encontrado.', 500);
|
|
|
|
// ── ADD EXAM ─────────────────────────────────────────────────
|
|
if ($action === 'add_exam') {
|
|
$examName = trim($body['exam_name'] ?? '');
|
|
$tomas = $body['tomas'] ?? [];
|
|
|
|
if (!$examName) jsonError('exam_name requerido.');
|
|
if (strlen($examName) > 80) jsonError('exam_name demasiado largo (máx 80 chars).');
|
|
if (!is_array($tomas) || empty($tomas)) jsonError('tomas requeridas.');
|
|
if (count($tomas) > 20) jsonError('Máximo 20 tomas por examen.');
|
|
|
|
// Verificar que el examen no exista ya
|
|
$currentOptions = $esquema[$idxSelector]['options'] ?? [];
|
|
if (in_array($examName, $currentOptions, true)) {
|
|
jsonError("El tipo de examen '$examName' ya existe.");
|
|
}
|
|
|
|
// Validar tomas
|
|
foreach ($tomas as $i => $t) {
|
|
$tipo = $t['tipo'] ?? '';
|
|
$valor = $t['valor'] ?? '';
|
|
$label = trim($t['label'] ?? '');
|
|
if (!in_array($tipo, ['minutos', 'hora_fija'], true)) jsonError("Toma $i: tipo inválido.");
|
|
if (!$label) jsonError("Toma $i: label requerido.");
|
|
if ($tipo === 'minutos' && (!is_numeric($valor) || (int)$valor < 0))
|
|
jsonError("Toma $i: valor de minutos inválido.");
|
|
if ($tipo === 'hora_fija' && !preg_match('/^\d{1,2}:\d{2}$/', $valor))
|
|
jsonError("Toma $i: hora_fija debe ser HH:MM.");
|
|
}
|
|
|
|
// Prefijo corto para IDs (basado en nombre del examen, sanitizado)
|
|
$prefix = '_' . substr(preg_replace('/[^a-z0-9]/i', '', strtolower($examName)), 0, 8) . '_';
|
|
$uid = substr(md5($examName . microtime()), 0, 4);
|
|
|
|
// 1. Agregar opción al campo selector
|
|
$esquema[$idxSelector]['options'][] = $examName;
|
|
|
|
// 2. Agregar campos al final del esquema
|
|
$condCampoId = '_c8j2g16';
|
|
foreach ($tomas as $idx => $t) {
|
|
$label = trim($t['label']);
|
|
$tipo = $t['tipo'];
|
|
$valor = $t['valor'];
|
|
|
|
// Construir label del separador
|
|
if ($tipo === 'minutos') {
|
|
$sepLabel = "$examName · Minuto $valor";
|
|
} else {
|
|
// hora_fija: convertir HH:MM a "H:MM a.m./p.m."
|
|
[$hh, $mm] = explode(':', $valor);
|
|
$h = (int)$hh; $ampm = $h >= 12 ? 'p.m.' : 'a.m.';
|
|
$h12 = $h > 12 ? $h - 12 : ($h === 0 ? 12 : $h);
|
|
$sepLabel = "$examName · {$h12}:{$mm} {$ampm}";
|
|
}
|
|
|
|
$sepId = $prefix . 's' . $idx . $uid;
|
|
$horaId = $prefix . 'h' . $idx . $uid;
|
|
$resId = $prefix . 'r' . $idx . $uid;
|
|
$firmaId = $prefix . 'f' . $idx . $uid;
|
|
|
|
$esquema[] = [
|
|
'id' => $sepId,
|
|
'tipo' => 'separador',
|
|
'label' => $sepLabel,
|
|
'condicion'=> ['campo_id' => $condCampoId, 'valores' => [$examName]],
|
|
];
|
|
$esquema[] = [
|
|
'id' => $horaId,
|
|
'tipo' => 'hora',
|
|
'label' => 'Hora de toma',
|
|
];
|
|
$esquema[] = [
|
|
'id' => $resId,
|
|
'tipo' => 'numero',
|
|
'label' => 'Resultado',
|
|
];
|
|
$esquema[] = [
|
|
'id' => $firmaId,
|
|
'tipo' => 'firma_profesional',
|
|
'label' => 'Firma del profesional',
|
|
];
|
|
}
|
|
|
|
$db->getConnection()->prepare(
|
|
"UPDATE lab_formularios SET esquema = ? WHERE id = 15"
|
|
)->execute([json_encode($esquema, JSON_UNESCAPED_UNICODE)]);
|
|
|
|
jsonOk([
|
|
'exam_name' => $examName,
|
|
'tomas_count' => count($tomas),
|
|
'options_count'=> count($esquema[$idxSelector]['options']),
|
|
], "Examen '$examName' agregado con " . count($tomas) . " tomas.");
|
|
}
|
|
|
|
// ── DELETE EXAM ──────────────────────────────────────────────
|
|
if ($action === 'delete_exam') {
|
|
$examName = trim($body['exam_name'] ?? '');
|
|
if (!$examName) jsonError('exam_name requerido.');
|
|
|
|
$options = $esquema[$idxSelector]['options'] ?? [];
|
|
if (!in_array($examName, $options, true)) jsonError("El examen '$examName' no existe.");
|
|
|
|
// Eliminar opción del selector
|
|
$esquema[$idxSelector]['options'] = array_values(array_filter($options, fn($o) => $o !== $examName));
|
|
|
|
// Eliminar separadores condicionados únicamente a este examen
|
|
$esquema = array_values(array_filter($esquema, function($c) use ($examName) {
|
|
$cond = $c['condicion'] ?? null;
|
|
if (!$cond) return true;
|
|
$vals = $cond['valores'] ?? [];
|
|
return !(count($vals) === 1 && $vals[0] === $examName);
|
|
}));
|
|
|
|
$db->getConnection()->prepare(
|
|
"UPDATE lab_formularios SET esquema = ? WHERE id = 15"
|
|
)->execute([json_encode($esquema, JSON_UNESCAPED_UNICODE)]);
|
|
|
|
jsonOk(['exam_name' => $examName], "Examen '$examName' eliminado.");
|
|
}
|