This commit is contained in:
Lizandro Guarnizo
2026-04-08 08:31:21 -05:00
parent 26d16b5d82
commit cbf7a8c296
4 changed files with 184 additions and 1 deletions
+11
View File
@@ -17,6 +17,17 @@ try {
$dom = new Domicilio();
$admin = adminId();
// ── Reprogramar ──
if (!empty($datos['id']) && !empty($datos['reprogramar'])) {
$id = (int)$datos['id'];
$nuevaFecha = trim($datos['nueva_fecha'] ?? '');
$nuevaHora = trim($datos['nueva_hora'] ?? '');
$motivo = trim($datos['motivo'] ?? '');
if (!$nuevaFecha) jsonError('nueva_fecha es obligatoria');
$nuevoId = $dom->reprogramar($id, $nuevaFecha, $nuevaHora, $motivo, $admin);
jsonOk(['id_origen' => $id, 'id_nuevo' => $nuevoId], 'Domicilio reprogramado correctamente');
}
// Solo cambio de estado
if (!empty($datos['id']) && !empty($datos['solo_estado'])) {
$id = (int)$datos['id'];
+72
View File
@@ -163,6 +163,20 @@ class Domicilio {
[$id]
);
// Vínculo: domicilio origen (este fue reprogramado desde otro)
if (!empty($dom['domicilio_origen_id'])) {
$dom['origen_info'] = $this->db->fetch(
"SELECT id, fecha_programada, hora_programada, estado FROM lab_domicilios WHERE id = ?",
[(int)$dom['domicilio_origen_id']]
);
}
// Vínculo: domicilio(s) reprogramado(s) desde este
$dom['reprogramaciones'] = $this->db->fetchAll(
"SELECT id, fecha_programada, hora_programada, estado FROM lab_domicilios WHERE domicilio_origen_id = ? ORDER BY id ASC",
[$id]
);
return $dom;
}
@@ -217,6 +231,63 @@ class Domicilio {
return $ok > 0;
}
/**
* Reprogramar un domicilio:
* 1. Marca el original como 'reprogramado' con fecha_reprogramada.
* 2. Clona el domicilio para la nueva fecha/hora.
* 3. Vincula el clon con domicilio_origen_id.
*
* @return int ID del nuevo domicilio creado.
*/
public function reprogramar(
int $idOrigen,
string $nuevaFecha,
string $nuevaHora = '',
string $motivo = '',
?int $adminId = null
): int {
// 1. Obtener datos del original
$origen = $this->db->fetch(
'SELECT * FROM lab_domicilios WHERE id = ?', [$idOrigen]
);
if (!$origen) {
throw new \RuntimeException('Domicilio no encontrado');
}
// 2. Marcar original como reprogramado
$extrasOrigen = [
'fecha_reprogramada' => $nuevaFecha,
];
if ($motivo) $extrasOrigen['motivo_cancelacion'] = $motivo;
$this->cambiarEstado($idOrigen, 'reprogramado', $adminId, $extrasOrigen);
// 3. Clonar campos relevantes para el nuevo domicilio
$campos = $this->filtrarCampos($origen);
// Resetear campos operativos del original
unset(
$campos['estado'], $campos['motivo_cancelacion'],
$campos['fecha_reprogramada'], $campos['hora_llegada'],
$campos['hora_salida'], $campos['observaciones'],
$campos['muestras_tomadas'], $campos['pago_estado'],
$campos['pago_modo'], $campos['pago_monto'],
$campos['pago_fecha'], $campos['pago_notas'],
$campos['pago_registrado_por'], $campos['temperatura_inicio'],
$campos['temperatura_salida'], $campos['domicilio_origen_id']
);
$campos['fecha_programada'] = $nuevaFecha;
$campos['hora_programada'] = $nuevaHora ?: null;
$campos['estado'] = 'programado';
$campos['domicilio_origen_id']= $idOrigen;
$campos['creado_por'] = $adminId;
$nuevoId = (int)$this->db->insert('lab_domicilios', $campos);
$this->log->registrar($adminId, 'domicilios', 'reprogramar', $nuevoId, [
'origen_id' => $idOrigen,
'nueva_fecha' => $nuevaFecha,
]);
return $nuevoId;
}
// ─────────────────────────────────────────────────────────────────────────
// Agenda del día
// ─────────────────────────────────────────────────────────────────────────
@@ -279,6 +350,7 @@ class Domicilio {
'observaciones', 'muestras_tomadas', 'notas_admin',
'seguro_nombre', 'autorizacion', 'copago_laboratorio',
'valor_domicilio', 'valor_copago',
'domicilio_origen_id',
// pago
'pago_estado', 'pago_modo', 'pago_monto', 'pago_fecha',
'pago_notas', 'pago_registrado_por',
+87 -1
View File
@@ -520,6 +520,41 @@ $domIdParam = (int)($_GET['id'] ?? 0);
</div>
</div>
<!-- ═══════════════ MODAL: Reprogramar Domicilio ═══════════════ -->
<div class="modal fade" id="modalReprogramar" tabindex="-1">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header py-2" style="background:#6f42c1;color:#fff">
<h6 class="modal-title mb-0"><i class="fas fa-calendar-alt me-1"></i>Reprogramar domicilio</h6>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="repr-dom-id">
<div class="mb-3">
<label class="form-label small fw-semibold">Nueva fecha <span class="text-danger">*</span></label>
<input type="date" class="form-control form-control-sm" id="repr-fecha">
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Nueva hora</label>
<input type="time" class="form-control form-control-sm" id="repr-hora">
</div>
<div class="mb-2">
<label class="form-label small fw-semibold">Motivo / nota</label>
<textarea class="form-control form-control-sm" id="repr-motivo" rows="2"
placeholder="Ej: Paciente solicitó cambio…"></textarea>
</div>
<div id="repr-error" class="text-danger small d-none">Debes ingresar la nueva fecha.</div>
</div>
<div class="modal-footer py-2">
<button class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
<button class="btn btn-sm text-white" style="background:#6f42c1" onclick="confirmarReprogramar()">
<i class="fas fa-calendar-check me-1"></i>Reprogramar
</button>
</div>
</div>
</div>
</div>
<!-- Modal Asignar / Reasignar Enfermero -->
<div class="modal fade" id="modalAsignarEnf" tabindex="-1">
<div class="modal-dialog">
@@ -768,6 +803,13 @@ async function verDomicilio(id) {
en_camino: [['en_domicilio','En domicilio','warning']],
en_domicilio:[['completado','Completar','success']],
};
// Reprogramar disponible en estados abiertos (excepto en camino / en domicilio)
const puedeReprogramar = ['programado','confirmado'].includes(dom.estado);
const btnRepr = puedeReprogramar
? `<button class="btn btn-sm" style="background:#6f42c1;color:#fff" onclick="abrirReprogramar(${dom.id})">
<i class="fas fa-calendar-alt me-1"></i>Reprogramar
</button>`
: '';
const btns = (SIGUIENTES[dom.estado]||[]).map(([e,lbl,col]) =>
`<button class="btn btn-sm btn-${col}" onclick="cambiarEstado(${dom.id},'${e}')">
<i class="fas fa-arrow-right me-1"></i>${lbl}
@@ -824,7 +866,23 @@ async function verDomicilio(id) {
<!-- Orden médica -->
${renderOrdenesDetail(dom)}
<!-- Botones de flujo -->
<div class="d-flex gap-2 flex-wrap">${btns}</div>
<div class="d-flex gap-2 flex-wrap">${btns}${btnRepr}</div>
<!-- Vínculos reprogramación -->
${dom.domicilio_origen_id ? `
<div class="alert alert-warning py-2 small mt-2 mb-0">
<i class="fas fa-history me-1"></i>
Reprogramado desde domicilio
<a href="#" onclick="event.preventDefault();verDomicilio(${dom.domicilio_origen_id})" class="fw-semibold">#${dom.domicilio_origen_id}</a>
${dom.origen_info ? `(${esc(dom.origen_info.fecha_programada)})` : ''}
</div>` : ''}
${dom.reprogramaciones && dom.reprogramaciones.length ? `
<div class="alert alert-info py-2 small mt-2 mb-0">
<i class="fas fa-calendar-check me-1"></i>
Reprogramado a:
${dom.reprogramaciones.map(r =>
`<a href="#" onclick="event.preventDefault();verDomicilio(${r.id})" class="fw-semibold">#${r.id}</a> (${esc(r.fecha_programada)})`
).join(', ')}
</div>` : ''}
</div>
</div>
<div class="row mt-3">
@@ -1106,6 +1164,34 @@ async function confirmarAsignacion() {
}
}
// ── Reprogramar domicilio ───────────────────────────────────
let _reprModal = null;
function abrirReprogramar(id) {
document.getElementById('repr-dom-id').value = id;
document.getElementById('repr-fecha').value = '';
document.getElementById('repr-hora').value = _domActual?.hora_programada?.slice(0,5) || '';
document.getElementById('repr-motivo').value = '';
document.getElementById('repr-error').classList.add('d-none');
if (!_reprModal) _reprModal = new bootstrap.Modal('#modalReprogramar');
_reprModal.show();
}
async function confirmarReprogramar() {
const id = +document.getElementById('repr-dom-id').value;
const fecha = document.getElementById('repr-fecha').value;
const hora = document.getElementById('repr-hora').value;
const motivo = document.getElementById('repr-motivo').value.trim();
if (!fecha) { document.getElementById('repr-error').classList.remove('d-none'); return; }
_reprModal.hide();
const r = await fetch('api/lab/save_domicilio.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, reprogramar: true, nueva_fecha: fecha, nueva_hora: hora, motivo }),
});
const d = await r.json();
mostrarToast(d.success ? d.message : (d.error || 'Error'), d.success ? 'success' : 'danger');
if (d.success) { cargarLista(paginaActual); verDomicilio(id); }
}
// ── Cambio de estado ───────────────────────────────────────────────────────
async function cambiarEstado(id, estado) {
if (!confirm(`¿Cambiar estado a "${estado}"?`)) return;
@@ -0,0 +1,14 @@
-- ============================================================
-- Migration: 20260408_lab_domicilio_origen_id
-- Vincula un domicilio reprogramado con su domicilio origen
-- ============================================================
ALTER TABLE `lab_domicilios`
ADD COLUMN `domicilio_origen_id` INT(11) DEFAULT NULL
COMMENT 'FK al domicilio original del que fue reprogramado'
AFTER `id`,
ADD KEY `idx_dom_origen` (`domicilio_origen_id`),
ADD CONSTRAINT `fk_dom_origen`
FOREIGN KEY (`domicilio_origen_id`)
REFERENCES `lab_domicilios` (`id`)
ON DELETE SET NULL;