feat(turnero): pago combinado con desglose por efectivo, transferencia y tarjeta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
4626ca3f28
commit
76dda4722a
@@ -0,0 +1,7 @@
|
|||||||
|
ALTER TABLE turnero_solicitudes
|
||||||
|
MODIFY COLUMN metodo_pago ENUM(
|
||||||
|
'efectivo','transferencia','tarjeta','eps','cortesia','combinado'
|
||||||
|
) DEFAULT NULL,
|
||||||
|
ADD COLUMN pagos_detalle JSON DEFAULT NULL
|
||||||
|
COMMENT 'Desglose de pago combinado {efectivo, transferencia, tarjeta}'
|
||||||
|
AFTER metodo_pago;
|
||||||
@@ -27,10 +27,19 @@ $examIds = isset($datos['exam_tipo_ids']) && is_array($datos['exam_tipo_ids'
|
|||||||
? array_filter(array_map('intval', $datos['exam_tipo_ids'])) : [];
|
? array_filter(array_map('intval', $datos['exam_tipo_ids'])) : [];
|
||||||
$total = isset($datos['total_cobrado']) && $datos['total_cobrado'] !== null
|
$total = isset($datos['total_cobrado']) && $datos['total_cobrado'] !== null
|
||||||
? (float) $datos['total_cobrado'] : null;
|
? (float) $datos['total_cobrado'] : null;
|
||||||
$metodoPago = isset($datos['metodo_pago']) ? trim((string) $datos['metodo_pago']) : null;
|
$metodoPago = isset($datos['metodo_pago']) ? trim((string) $datos['metodo_pago']) : null;
|
||||||
$obs = isset($datos['observaciones']) ? trim((string) $datos['observaciones']) : null;
|
$obs = isset($datos['observaciones']) ? trim((string) $datos['observaciones']) : null;
|
||||||
$numOrden = isset($datos['numero_orden']) ? trim((string) $datos['numero_orden']) : null;
|
$numOrden = isset($datos['numero_orden']) ? trim((string) $datos['numero_orden']) : null;
|
||||||
$embarazada = !empty($datos['embarazada']) ? 1 : 0;
|
$embarazada = !empty($datos['embarazada']) ? 1 : 0;
|
||||||
|
$pagosDetalle = null;
|
||||||
|
if ($metodoPago === 'combinado' && isset($datos['pagos_detalle']) && is_array($datos['pagos_detalle'])) {
|
||||||
|
$pd = $datos['pagos_detalle'];
|
||||||
|
$pagosDetalle = json_encode([
|
||||||
|
'efectivo' => max(0, (float)($pd['efectivo'] ?? 0)),
|
||||||
|
'transferencia' => max(0, (float)($pd['transferencia'] ?? 0)),
|
||||||
|
'tarjeta' => max(0, (float)($pd['tarjeta'] ?? 0)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
if ($numOrden === '') $numOrden = null;
|
if ($numOrden === '') $numOrden = null;
|
||||||
|
|
||||||
// ── Validaciones ──────────────────────────────────────────────
|
// ── Validaciones ──────────────────────────────────────────────
|
||||||
@@ -39,7 +48,7 @@ if ($pacienteId <= 0) jsonError('paciente_id inválido.');
|
|||||||
if ($lugarId <= 0) jsonError('lugar_id inválido.');
|
if ($lugarId <= 0) jsonError('lugar_id inválido.');
|
||||||
if (empty($examIds)) jsonError('Debe seleccionar al menos un examen.');
|
if (empty($examIds)) jsonError('Debe seleccionar al menos un examen.');
|
||||||
|
|
||||||
$metodosValidos = ['efectivo', 'transferencia', 'tarjeta', 'eps', 'cortesia', ''];
|
$metodosValidos = ['efectivo', 'transferencia', 'tarjeta', 'eps', 'cortesia', 'combinado', ''];
|
||||||
if ($metodoPago && !in_array($metodoPago, $metodosValidos, true)) {
|
if ($metodoPago && !in_array($metodoPago, $metodosValidos, true)) {
|
||||||
jsonError('metodo_pago inválido.');
|
jsonError('metodo_pago inválido.');
|
||||||
}
|
}
|
||||||
@@ -104,12 +113,12 @@ try {
|
|||||||
|
|
||||||
$stmt = $pdo->prepare(
|
$stmt = $pdo->prepare(
|
||||||
"INSERT INTO turnero_solicitudes
|
"INSERT INTO turnero_solicitudes
|
||||||
(turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, observaciones, embarazada, creado_por)
|
(turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, pagos_detalle, observaciones, embarazada, creado_por)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
);
|
);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$turnoId, $pacienteId, $lugarId, $numOrden,
|
$turnoId, $pacienteId, $lugarId, $numOrden,
|
||||||
$total, $metodoPago ?: null, $obs ?: null, $embarazada, adminId(),
|
$total, $metodoPago ?: null, $pagosDetalle, $obs ?: null, $embarazada, adminId(),
|
||||||
]);
|
]);
|
||||||
$solicitudId = (int) $pdo->lastInsertId();
|
$solicitudId = (int) $pdo->lastInsertId();
|
||||||
|
|
||||||
|
|||||||
@@ -501,16 +501,32 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
placeholder="Total $" step="100" min="0">
|
placeholder="Total $" step="100" min="0">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<select id="sel-pago" class="form-select form-select-sm">
|
<select id="sel-pago" class="form-select form-select-sm" onchange="togglePagoCombinado()">
|
||||||
<option value="">— Forma de pago —</option>
|
<option value="">— Forma de pago —</option>
|
||||||
<option value="efectivo">Efectivo</option>
|
<option value="efectivo">Efectivo</option>
|
||||||
<option value="transferencia">Transferencia</option>
|
<option value="transferencia">Transferencia</option>
|
||||||
<option value="tarjeta">Tarjeta</option>
|
<option value="tarjeta">Tarjeta</option>
|
||||||
<option value="eps">EPS / Convenio</option>
|
<option value="eps">EPS / Convenio</option>
|
||||||
<option value="cortesia">Cortesía</option>
|
<option value="cortesia">Cortesía</option>
|
||||||
|
<option value="combinado">Pago combinado</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Panel pago combinado -->
|
||||||
|
<div id="panel-combinado" class="d-none mt-2 p-2 border rounded" style="background:#f8fafc">
|
||||||
|
<div class="row g-2 align-items-center mb-1">
|
||||||
|
<div class="col-5"><label class="form-label mb-0 small fw-semibold"><i class="fas fa-money-bill-wave me-1 text-success"></i>Efectivo</label></div>
|
||||||
|
<div class="col-7"><input type="number" id="comb-efectivo" class="form-control form-control-sm" placeholder="$0" min="0" step="100" oninput="sumarCombinado()"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-2 align-items-center mb-1">
|
||||||
|
<div class="col-5"><label class="form-label mb-0 small fw-semibold"><i class="fas fa-university me-1 text-primary"></i>Transferencia</label></div>
|
||||||
|
<div class="col-7"><input type="number" id="comb-transferencia" class="form-control form-control-sm" placeholder="$0" min="0" step="100" oninput="sumarCombinado()"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col-5"><label class="form-label mb-0 small fw-semibold"><i class="fas fa-credit-card me-1 text-warning"></i>Tarjeta</label></div>
|
||||||
|
<div class="col-7"><input type="number" id="comb-tarjeta" class="form-control form-control-sm" placeholder="$0" min="0" step="100" oninput="sumarCombinado()"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<textarea id="inp-obs" class="form-control form-control-sm mt-2"
|
<textarea id="inp-obs" class="form-control form-control-sm mt-2"
|
||||||
rows="2" placeholder="Observaciones…" maxlength="500"></textarea>
|
rows="2" placeholder="Observaciones…" maxlength="500"></textarea>
|
||||||
</div>
|
</div>
|
||||||
@@ -1120,6 +1136,27 @@ function seleccionarPaciente(pac) {
|
|||||||
_historialPacienteId = pac.id;
|
_historialPacienteId = pac.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function togglePagoCombinado() {
|
||||||
|
const combinado = document.getElementById('sel-pago').value === 'combinado';
|
||||||
|
document.getElementById('panel-combinado').classList.toggle('d-none', !combinado);
|
||||||
|
const inp = document.getElementById('inp-total');
|
||||||
|
inp.readOnly = combinado;
|
||||||
|
inp.style.background = combinado ? '#e9ecef' : '';
|
||||||
|
if (combinado) sumarCombinado();
|
||||||
|
}
|
||||||
|
function sumarCombinado() {
|
||||||
|
const v = id => parseFloat(document.getElementById(id).value) || 0;
|
||||||
|
const total = v('comb-efectivo') + v('comb-transferencia') + v('comb-tarjeta');
|
||||||
|
document.getElementById('inp-total').value = total || '';
|
||||||
|
}
|
||||||
|
function resetPagoCombinado() {
|
||||||
|
['comb-efectivo','comb-transferencia','comb-tarjeta'].forEach(id => document.getElementById(id).value = '');
|
||||||
|
document.getElementById('panel-combinado').classList.add('d-none');
|
||||||
|
const inp = document.getElementById('inp-total');
|
||||||
|
inp.readOnly = false;
|
||||||
|
inp.style.background = '';
|
||||||
|
}
|
||||||
|
|
||||||
function desvincularPaciente() {
|
function desvincularPaciente() {
|
||||||
pacienteActivo = null;
|
pacienteActivo = null;
|
||||||
_historialPacienteId = null;
|
_historialPacienteId = null;
|
||||||
@@ -1264,8 +1301,13 @@ async function guardarSolicitud() {
|
|||||||
lugar_id: lugarId,
|
lugar_id: lugarId,
|
||||||
exam_tipo_ids: examIds,
|
exam_tipo_ids: examIds,
|
||||||
numero_orden: document.getElementById('inp-consecutivo').value.trim() || null,
|
numero_orden: document.getElementById('inp-consecutivo').value.trim() || null,
|
||||||
total_cobrado: parseFloat(document.getElementById('inp-total').value) || null,
|
total_cobrado: parseFloat(document.getElementById('inp-total').value) || null,
|
||||||
metodo_pago: document.getElementById('sel-pago').value || null,
|
metodo_pago: document.getElementById('sel-pago').value || null,
|
||||||
|
pagos_detalle: document.getElementById('sel-pago').value === 'combinado' ? {
|
||||||
|
efectivo: parseFloat(document.getElementById('comb-efectivo').value) || 0,
|
||||||
|
transferencia: parseFloat(document.getElementById('comb-transferencia').value) || 0,
|
||||||
|
tarjeta: parseFloat(document.getElementById('comb-tarjeta').value) || 0,
|
||||||
|
} : null,
|
||||||
observaciones: document.getElementById('inp-obs').value.trim() || null,
|
observaciones: document.getElementById('inp-obs').value.trim() || null,
|
||||||
embarazada: document.getElementById('chk-embarazada').checked ? 1 : 0,
|
embarazada: document.getElementById('chk-embarazada').checked ? 1 : 0,
|
||||||
}),
|
}),
|
||||||
@@ -1612,6 +1654,10 @@ async function soltarTurno() {
|
|||||||
// ── Helpers ───────────────────────────────────────────────────
|
// ── Helpers ───────────────────────────────────────────────────
|
||||||
function resetCheckboxes() {
|
function resetCheckboxes() {
|
||||||
document.querySelectorAll('.exam-chk').forEach(c => c.checked = false);
|
document.querySelectorAll('.exam-chk').forEach(c => c.checked = false);
|
||||||
|
document.getElementById('sel-pago').value = '';
|
||||||
|
document.getElementById('inp-total').value = '';
|
||||||
|
document.getElementById('inp-obs').value = '';
|
||||||
|
resetPagoCombinado();
|
||||||
}
|
}
|
||||||
function toggleTodosExamenes(val) {
|
function toggleTodosExamenes(val) {
|
||||||
document.querySelectorAll('.exam-chk').forEach(c => c.checked = val);
|
document.querySelectorAll('.exam-chk').forEach(c => c.checked = val);
|
||||||
|
|||||||
Reference in New Issue
Block a user