From 5e58b14eba1fcb886b8bb2ed7e6a7efdf20af87a Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:38:03 -0500 Subject: [PATCH] reportes --- api/lab/get_reporte_pagos.php | 127 ++++++++++++++ lab_reportes.php | 311 ++++++++++++++++++++++++++++++++++ 2 files changed, 438 insertions(+) create mode 100644 api/lab/get_reporte_pagos.php diff --git a/api/lab/get_reporte_pagos.php b/api/lab/get_reporte_pagos.php new file mode 100644 index 0000000..ad2a141 --- /dev/null +++ b/api/lab/get_reporte_pagos.php @@ -0,0 +1,127 @@ + 12) $mes = (int)date('n'); + $wheres[] = 'YEAR(d.fecha_programada) = ?'; + $params[] = $anio; + $wheres[] = 'MONTH(d.fecha_programada) = ?'; + $params[] = $mes; + break; + + case 'rango': + $desde = $_GET['desde'] ?? date('Y-m-01'); + $hasta = $_GET['hasta'] ?? date('Y-m-d'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $desde)) $desde = date('Y-m-01'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $hasta)) $hasta = date('Y-m-d'); + $wheres[] = 'DATE(d.fecha_programada) BETWEEN ? AND ?'; + $params[] = $desde; + $params[] = $hasta; + break; + + default: // dia + $fecha = $_GET['fecha'] ?? date('Y-m-d'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) $fecha = date('Y-m-d'); + $wheres[] = 'DATE(d.fecha_programada) = ?'; + $params[] = $fecha; + break; +} + +// Filtro por enfermera +$enfermeraId = null; +if (!empty($_GET['enfermera_id']) && ctype_digit($_GET['enfermera_id'])) { + $enfermeraId = (int)$_GET['enfermera_id']; + $wheres[] = 'a.enfermera_id = ?'; + $params[] = $enfermeraId; +} + +$where = $wheres ? 'WHERE ' . implode(' AND ', $wheres) : ''; + +$filas = $db->fetchAll( + "SELECT + d.id, + d.fecha_programada, + d.pago_fecha, + p.nombre_completo AS paciente, + p.numero_documento, + COALESCE(e.nombre_completo,'Sin asignar') AS enfermera, + d.tipo_cliente, + COALESCE(d.seguro_nombre,'') AS seguro_nombre, + d.estado AS dom_estado, + COALESCE(d.valor_domicilio, 0) AS valor_domicilio, + d.pago_estado, + COALESCE(d.pago_modo,'') AS pago_modo, + COALESCE(d.pago_monto, 0) AS pago_monto, + COALESCE(d.pago_notas,'') AS pago_notas + FROM lab_domicilios d + JOIN lab_pacientes p ON p.id = d.paciente_id + LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id AND a.estado NOT IN ('liberada') + LEFT JOIN lab_enfermeras e ON e.id = a.enfermera_id + $where + ORDER BY d.fecha_programada ASC, d.id ASC", + $params +); + +// Resumen financiero +$resumen = [ + 'total' => count($filas), + 'pagados' => 0, + 'pendientes' => 0, + 'exentos' => 0, + 'monto_total' => 0.0, + 'por_modo' => [], +]; + +foreach ($filas as $f) { + $est = $f['pago_estado'] ?? 'pendiente'; + if ($est === 'pagado') { + $resumen['pagados']++; + $resumen['monto_total'] += (float)$f['pago_monto']; + $key = $f['pago_modo'] ?: 'sin_registrar'; + $resumen['por_modo'][$key] = ($resumen['por_modo'][$key] ?? 0.0) + (float)$f['pago_monto']; + } elseif ($est === 'exento') { + $resumen['exentos']++; + } else { + $resumen['pendientes']++; + } +} + +// Lista de enfermeras para poblar el selector +$enfermeras = $db->fetchAll( + "SELECT id, nombre_completo FROM lab_enfermeras WHERE is_active = 1 ORDER BY nombre_completo" +); + +echo json_encode([ + 'success' => true, + 'filas' => $filas, + 'resumen' => $resumen, + 'enfermeras' => $enfermeras, +], JSON_UNESCAPED_UNICODE); diff --git a/lab_reportes.php b/lab_reportes.php index ddacea2..3010497 100644 --- a/lab_reportes.php +++ b/lab_reportes.php @@ -136,6 +136,67 @@ if ($tipo) { $headers = ['ID','Nombre','Tipo Doc.','Documento','Teléfono','Email','EPS','Ciudad','Barrio','Activo','Total Órdenes','Registrado']; break; + case 'pagos': + $pModo = in_array($_GET['modo'] ?? '', ['dia','mes','rango']) ? $_GET['modo'] : 'dia'; + $pWheres = []; + $pParams = []; + switch ($pModo) { + case 'mes': + $pAnio = isset($_GET['anio']) && ctype_digit($_GET['anio']) ? (int)$_GET['anio'] : (int)date('Y'); + $pMes = isset($_GET['mes']) && ctype_digit($_GET['mes']) ? (int)$_GET['mes'] : (int)date('n'); + if ($pMes < 1 || $pMes > 12) $pMes = (int)date('n'); + $pWheres[] = 'YEAR(d.fecha_programada) = ?'; $pParams[] = $pAnio; + $pWheres[] = 'MONTH(d.fecha_programada) = ?'; $pParams[] = $pMes; + $rangoNombre = sprintf('%04d-%02d', $pAnio, $pMes); + break; + case 'rango': + $pDesde = $_GET['desde'] ?? date('Y-m-01'); + $pHasta = $_GET['hasta'] ?? date('Y-m-d'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $pDesde)) $pDesde = date('Y-m-01'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $pHasta)) $pHasta = date('Y-m-d'); + $pWheres[] = 'DATE(d.fecha_programada) BETWEEN ? AND ?'; + $pParams[] = $pDesde; $pParams[] = $pHasta; + $rangoNombre = "{$pDesde}_{$pHasta}"; + break; + default: // dia + $pFecha = $_GET['fecha'] ?? date('Y-m-d'); + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $pFecha)) $pFecha = date('Y-m-d'); + $pWheres[] = 'DATE(d.fecha_programada) = ?'; + $pParams[] = $pFecha; + $rangoNombre = $pFecha; + } + if (!empty($_GET['enfermera_id']) && ctype_digit($_GET['enfermera_id'])) { + $pWheres[] = 'a.enfermera_id = ?'; + $pParams[] = (int)$_GET['enfermera_id']; + } + $pWhere = $pWheres ? 'WHERE ' . implode(' AND ', $pWheres) : ''; + $rows = $db->fetchAll( + "SELECT d.id, d.fecha_programada, d.pago_fecha, + p.nombre_completo AS paciente, p.numero_documento, + COALESCE(e.nombre_completo,'Sin asignar') AS enfermera, + d.tipo_cliente, COALESCE(d.seguro_nombre,'') AS seguro, + d.estado AS dom_estado, + COALESCE(d.valor_domicilio,0) AS valor_domicilio, + COALESCE(d.pago_estado,'pendiente') AS pago_estado, + COALESCE(d.pago_modo,'') AS pago_modo, + COALESCE(d.pago_monto,0) AS pago_monto, + COALESCE(d.pago_notas,'') AS pago_notas + FROM lab_domicilios d + JOIN lab_pacientes p ON p.id = d.paciente_id + LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id AND a.estado NOT IN ('liberada') + LEFT JOIN lab_enfermeras e ON e.id = a.enfermera_id + $pWhere + ORDER BY d.fecha_programada ASC, d.id ASC", + $pParams + ); + $filename = "reporte_pagos_{$rangoNombre}.csv"; + $headers = [ + 'ID','Fecha programada','Fecha pago','Paciente','Documento', + 'Enfermero/a','Tipo cliente','Seguro','Estado domicilio', + 'Valor domicilio','Estado pago','Modo pago','Monto recibido','Notas pago', + ]; + break; + default: http_response_code(400); exit('Tipo de exportación no válido'); @@ -244,6 +305,100 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user'][' + + + +
+
+
+ Informe de pagos recibidos en domicilios +
+
+
+ + +
+ +
+ + +
+ + + + + + + +
+ + +
+ + +
+
+ + + +
+
+
+ Selecciona los filtros y haz clic en Ver informe +
+
+
+
+ @@ -563,6 +718,161 @@ function exportarReporteEnfermero() { window.open(`lab_reportes.php?${params}`, '_blank'); } +// ── Reporte de Pagos ─────────────────────────────────────────────────────── + +function pagoToggleModo() { + const modo = document.getElementById('pago-modo').value; + document.getElementById('pago-wrap-dia').style.display = modo === 'dia' ? '' : 'none'; + document.getElementById('pago-wrap-anio').style.display = modo === 'mes' ? '' : 'none'; + document.getElementById('pago-wrap-mes').style.display = modo === 'mes' ? '' : 'none'; + document.getElementById('pago-wrap-desde').style.display = modo === 'rango' ? '' : 'none'; + document.getElementById('pago-wrap-hasta').style.display = modo === 'rango' ? '' : 'none'; +} + +// Seleccionar el mes actual al cargar +(function initPagoMes() { + const mes = new Date().getMonth() + 1; + const sel = document.getElementById('pago-mes'); + if (sel) sel.value = mes; +})(); + +async function cargarReportePagos() { + const wrap = document.getElementById('pago-tabla-wrap'); + wrap.innerHTML = '
Cargando…
'; + document.getElementById('pago-resumen').style.display = 'none'; + + const params = _pagoParams(); + + try { + const r = await fetch(`api/lab/get_reporte_pagos.php?${params}`); + const d = await r.json(); + if (!d.success) throw new Error(d.error || 'Error al cargar'); + + // Poblar selector de enfermeras (solo la primera vez) + const sel = document.getElementById('pago-enfermera'); + if (sel.options.length === 1 && d.enfermeras?.length) { + d.enfermeras.forEach(e => { + const o = document.createElement('option'); + o.value = e.id; + o.textContent = e.nombre_completo; + sel.appendChild(o); + }); + } + + // Resumen KPIs + const rs = d.resumen || {}; + document.getElementById('pr-total').textContent = rs.total ?? 0; + document.getElementById('pr-pagados').textContent = rs.pagados ?? 0; + document.getElementById('pr-pendientes').textContent = rs.pendientes ?? 0; + document.getElementById('pr-monto').textContent = fmtMoney(rs.monto_total); + + const modosEl = document.getElementById('pr-modos'); + const MODO_LABEL = { efectivo:'Efectivo', transferencia:'Transferencia', otro:'Otro', sin_registrar:'Sin modo' }; + const modos = Object.entries(rs.por_modo || {}); + modosEl.innerHTML = modos.length + ? modos.map(([k, v]) => + `${esc(MODO_LABEL[k] || k)}: ${fmtMoney(v)}` + ).join('') + : ''; + document.getElementById('pago-resumen').style.display = ''; + + // Tabla de detalle + if (!d.filas?.length) { + wrap.innerHTML = '

Sin domicilios para los filtros seleccionados.

'; + return; + } + + const PAGO_BADGE = { pagado:'success', pendiente:'warning', exento:'secondary' }; + const DOM_BADGE = { completado:'success', cancelado:'danger', programado:'secondary', + confirmado:'info', en_camino:'primary', reprogramado:'dark' }; + + wrap.innerHTML = ` + + + + + + + + + + + + + + + + + + + + ${d.filas.map(f => { + const pagoEst = f.pago_estado || 'pendiente'; + const domEst = f.dom_estado || ''; + const cliente = f.tipo_cliente === 'seguro' && f.seguro_nombre + ? `Seguro: ${esc(f.seguro_nombre)}` + : esc(f.tipo_cliente || 'particular'); + return ` + + + + + + + + + + + + + + `; + }).join('')} + + + + + + + + + + +
#Fecha prog.PacienteDocumentoEnfermero/aTipo clienteValor domicilioEstado pagoModo pagoMonto recibidoFecha pagoEstado domicilioNotas pago
${f.id}${esc(f.fecha_programada?.slice(0,10) || '—')}${esc(f.paciente)}${esc(f.numero_documento || '—')}${esc(f.enfermera)}${cliente}${fmtMoney(f.valor_domicilio)} + ${esc(pagoEst)} + ${esc(f.pago_modo) || '—'} + ${pagoEst === 'pagado' ? fmtMoney(f.pago_monto) : '—'} + ${f.pago_fecha ? esc(f.pago_fecha.slice(0,16)) : '—'} + ${esc(domEst)} + ${esc(f.pago_notas) || ''}
Total${fmtMoney(d.filas.reduce((s,f) => s + parseFloat(f.valor_domicilio||0), 0))}${fmtMoney(rs.monto_total)}
`; + } catch (e) { + wrap.innerHTML = `
${esc(e.message)}
`; + } +} + +function exportarReportePagos() { + const p = _pagoParams(); + p.set('export', 'pagos'); + window.open(`lab_reportes.php?${p}`, '_blank'); +} + +function _pagoParams() { + const modo = document.getElementById('pago-modo').value; + const enf = document.getElementById('pago-enfermera').value; + const p = new URLSearchParams({ modo }); + if (modo === 'dia') { + p.set('fecha', document.getElementById('pago-fecha').value || ''); + } else if (modo === 'mes') { + p.set('anio', document.getElementById('pago-anio').value || ''); + p.set('mes', document.getElementById('pago-mes').value || ''); + } else { + p.set('desde', document.getElementById('pago-desde').value || ''); + p.set('hasta', document.getElementById('pago-hasta').value || ''); + } + if (enf) p.set('enfermera_id', enf); + return p; +} + // ── Carga todo en paralelo ────────────────────────────────────────────────── async function cargarTodo() { const desde = fecha('rpt-desde'); @@ -572,6 +882,7 @@ async function cargarTodo() { cargarResumenOrdenes(desde, hasta), cargarActividad(1), cargarReporteEnfermero(), + cargarReportePagos(), ]); }