fix(turnero): corregir 6 hallazgos de auditoría en tomas previas

- cancelar_toma_pendiente: añade JOIN por paciente_id para prevenir IDOR
- cancelar_toma_pendiente: requiere turno_id para validar propiedad
- get_tomas_pendientes: porta filtros condicionales y _tomas_config de
  guardar_toma.php para mostrar conteo de firmas correcto
- lugar.php: refresca sección tomas previas al completar via turneroFirmado
- lugar.php: muestra error en catch de cargarTomasPrevias en vez de silenciar
- lugar.php: limpia _tomaAlertados al cancelar toma previa
- lugar.php: pasa turno_id en POST de cancelar para ownership check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-21 20:58:21 -05:00
co-authored by Claude Sonnet 4.6
parent 814bd2ce5c
commit 49a622e05f
3 changed files with 64 additions and 20 deletions
@@ -8,18 +8,26 @@ require_once __DIR__ . '/_helpers.php';
requireMethod('POST');
requireTurnero();
$body = inputJson();
$body = inputJson();
$consentId = (int)($body['consentimiento_id'] ?? 0);
$turnoId = (int)($body['turno_id'] ?? 0);
if (!$consentId) jsonError('consentimiento_id requerido.');
if (!$turnoId) jsonError('turno_id requerido.');
$pdo = db();
$stmt = $pdo->prepare(
"UPDATE turnero_consentimientos
SET estado = 'rechazado'
WHERE id = ? AND estado = 'en_progreso'"
);
$stmt->execute([$consentId]);
if ($stmt->rowCount() === 0) jsonError('No se encontró toma en progreso con ese id.', 404);
// Verificar que el consentimiento pertenece al mismo paciente que el turno activo.
// Previene que un turnero cancele tomas de pacientes que no tiene abiertos (IDOR).
$stmt = $pdo->prepare(
"UPDATE turnero_consentimientos tc
JOIN turnero_turnos t_old ON t_old.id = tc.turno_id
JOIN turnero_turnos t_cur ON t_cur.id = ? AND t_cur.paciente_id IS NOT NULL
AND t_cur.paciente_id = t_old.paciente_id
SET tc.estado = 'rechazado'
WHERE tc.id = ? AND tc.estado = 'en_progreso'"
);
$stmt->execute([$turnoId, $consentId]);
if ($stmt->rowCount() === 0) jsonError('Toma no encontrada o el paciente no coincide.', 404);
jsonOk(['cancelado' => true]);
@@ -42,14 +42,46 @@ foreach ($rows as $r) {
$esquema = json_decode($r['esquema'] ?? '[]', true) ?: [];
$datos = json_decode($r['datos_respuestas'] ?? '[]', true) ?: [];
// Contar firmas totales y hechas
$totalFirmas = 0;
$firmasHechas = 0;
// Contar firmas activas — mismo filtro que guardar_toma.php
// (condicionales de separador + _tomas_config)
$firmasCampos = [];
$separadorLabel = null;
$curCondField = null;
$curCondVals = [];
foreach ($esquema as $c) {
if (($c['tipo'] ?? '') === 'firma_profesional') {
$totalFirmas++;
if (!empty($datos[$c['id']]) && strlen($datos[$c['id']]) > 10) $firmasHechas++;
$tipo = $c['tipo'] ?? '';
if ($tipo === 'separador') {
$separadorLabel = $c['label'] ?? '';
$cond = $c['condicion'] ?? null;
if ($cond) {
$curCondField = $cond['campo_id'] ?? null;
$curCondVals = $cond['valores'] ?? ($cond['valor'] ? [$cond['valor']] : []);
} else {
$curCondField = null;
$curCondVals = [];
}
}
if ($tipo === 'firma_profesional') {
if ($curCondField !== null && !empty($curCondVals)) {
$ctrlVal = $datos[$curCondField] ?? null;
$ctrlArr = is_array($ctrlVal) ? $ctrlVal : ($ctrlVal !== null ? [$ctrlVal] : []);
if (empty(array_intersect($curCondVals, $ctrlArr))) continue;
}
$firmasCampos[] = $c['id'];
}
}
$tcFirmas = $datos['_tomas_config'] ?? null;
if (is_array($tcFirmas)) {
$allowed = [];
foreach ($tcFirmas as $ids) {
if (is_array($ids)) foreach ($ids as $id) $allowed[$id] = true;
}
$firmasCampos = array_values(array_filter($firmasCampos, fn($id) => isset($allowed[$id])));
}
$totalFirmas = count($firmasCampos);
$firmasHechas = 0;
foreach ($firmasCampos as $fid) {
if (!empty($datos[$fid]) && strlen($datos[$fid]) > 10) $firmasHechas++;
}
$tomas[] = [