feat(recepcion): motor de precios + selector empresa/convenio
- Nueva sección empresa/convenio en recepcion.php: búsqueda live por nombre/NIT, selector de subgrupo, campo autorización (cuando req_autoriza), diagnóstico CIE-10 - Tabla de precios calculados en tiempo real: resolución por tarifa de la empresa o subgrupo, descuento empresa aplicado, botón "Aplicar al cobro" - Al agregar/quitar exámenes o cambiar empresa/subgrupo se recalcula automáticamente - Nuevo API get_precios_examenes.php: resuelve precios desde lab_tarifas con soporte de tarifas derivadas por porcentaje - create_solicitud.php guarda nit_empresa, subgrupo_id, autorizacion, diag_ppal, items_precio (snapshot JSON al momento de la recepción) - Migración 10: ALTER turnero_solicitudes añade los 5 campos nuevos Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b9bb4fc628
commit
a7aac706ee
@@ -0,0 +1,16 @@
|
|||||||
|
-- =============================================================
|
||||||
|
-- LIS 10 — Extiende turnero_solicitudes con campos de empresa/convenio
|
||||||
|
-- Todos DEFAULT NULL para no romper datos existentes.
|
||||||
|
-- =============================================================
|
||||||
|
|
||||||
|
ALTER TABLE turnero_solicitudes
|
||||||
|
ADD COLUMN IF NOT EXISTS nit_empresa VARCHAR(20) DEFAULT NULL
|
||||||
|
COMMENT 'FK lab_empresas.nit — empresa/EPS del paciente',
|
||||||
|
ADD COLUMN IF NOT EXISTS subgrupo_id INT UNSIGNED DEFAULT NULL
|
||||||
|
COMMENT 'FK lab_empresa_subgrupos.id',
|
||||||
|
ADD COLUMN IF NOT EXISTS autorizacion VARCHAR(100) DEFAULT NULL
|
||||||
|
COMMENT 'Número de autorización EPS',
|
||||||
|
ADD COLUMN IF NOT EXISTS diag_ppal VARCHAR(20) DEFAULT NULL
|
||||||
|
COMMENT 'Diagnóstico principal CIE-10',
|
||||||
|
ADD COLUMN IF NOT EXISTS items_precio JSON DEFAULT NULL
|
||||||
|
COMMENT 'Snapshot de precios al momento de la recepción';
|
||||||
@@ -32,7 +32,13 @@ $obs = isset($datos['observaciones']) ? trim((string) $datos['observac
|
|||||||
$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;
|
||||||
$soloMuestras = !empty($datos['solo_muestras']) ? 1 : 0;
|
$soloMuestras = !empty($datos['solo_muestras']) ? 1 : 0;
|
||||||
$medicoId = !empty($datos['medico_id']) ? (int)$datos['medico_id'] : null;
|
$medicoId = !empty($datos['medico_id']) ? (int)$datos['medico_id'] : null;
|
||||||
|
$nitEmpresa = !empty($datos['nit_empresa']) ? trim((string)$datos['nit_empresa']) : null;
|
||||||
|
$subgrupoId = !empty($datos['subgrupo_id']) ? (int)$datos['subgrupo_id'] : null;
|
||||||
|
$autorizacion = !empty($datos['autorizacion']) ? trim((string)$datos['autorizacion']) : null;
|
||||||
|
$diagPpal = !empty($datos['diag_ppal']) ? trim((string)$datos['diag_ppal']) : null;
|
||||||
|
$itemsPrecio = isset($datos['items_precio']) && is_array($datos['items_precio'])
|
||||||
|
? json_encode($datos['items_precio']) : null;
|
||||||
$pagosDetalle = null;
|
$pagosDetalle = null;
|
||||||
if ($metodoPago === 'combinado' && isset($datos['pagos_detalle']) && is_array($datos['pagos_detalle'])) {
|
if ($metodoPago === 'combinado' && isset($datos['pagos_detalle']) && is_array($datos['pagos_detalle'])) {
|
||||||
$pd = $datos['pagos_detalle'];
|
$pd = $datos['pagos_detalle'];
|
||||||
@@ -117,12 +123,15 @@ 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, pagos_detalle, observaciones, embarazada, solo_muestras, medico_id, creado_por)
|
(turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, pagos_detalle,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
observaciones, embarazada, solo_muestras, medico_id,
|
||||||
|
nit_empresa, subgrupo_id, autorizacion, diag_ppal, items_precio, creado_por)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
);
|
);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$turnoId, $pacienteId, $lugarId, $numOrden,
|
$turnoId, $pacienteId, $lugarId, $numOrden,
|
||||||
$total, $metodoPago ?: null, $pagosDetalle, $obs ?: null, $embarazada, $soloMuestras, $medicoId, adminId(),
|
$total, $metodoPago ?: null, $pagosDetalle, $obs ?: null, $embarazada, $soloMuestras, $medicoId,
|
||||||
|
$nitEmpresa, $subgrupoId, $autorizacion, $diagPpal, $itemsPrecio, adminId(),
|
||||||
]);
|
]);
|
||||||
$solicitudId = (int) $pdo->lastInsertId();
|
$solicitudId = (int) $pdo->lastInsertId();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GET /modules/turnero/api/get_precios_examenes.php
|
||||||
|
*
|
||||||
|
* Calcula precios de exámenes según empresa/tarifa.
|
||||||
|
*
|
||||||
|
* Query params:
|
||||||
|
* exam_ids[] int[] IDs de exam_tipos
|
||||||
|
* nit_empresa string opcional
|
||||||
|
* subgrupo_id int opcional
|
||||||
|
*
|
||||||
|
* Response:
|
||||||
|
* items[] { exam_tipo_id, codigo, nombre, valor_base, valor_final, tiene_precio }
|
||||||
|
* subtotal precio antes de descuento empresa
|
||||||
|
* descuento_pct porcentaje de descuento empresa
|
||||||
|
* descuento_val valor del descuento
|
||||||
|
* total total a cobrar
|
||||||
|
* tarifa_id tarifa aplicada
|
||||||
|
* tarifa_nombre nombre de la tarifa
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/_helpers.php';
|
||||||
|
requireMethod('GET');
|
||||||
|
|
||||||
|
$examIds = array_filter(array_map('intval', (array)($_GET['exam_ids'] ?? [])));
|
||||||
|
$nitEmpresa = trim($_GET['nit_empresa'] ?? '');
|
||||||
|
$subgrupoId = (int)($_GET['subgrupo_id'] ?? 0);
|
||||||
|
|
||||||
|
if (empty($examIds)) jsonOk([
|
||||||
|
'items' => [],
|
||||||
|
'subtotal' => 0,
|
||||||
|
'descuento_pct' => 0,
|
||||||
|
'descuento_val' => 0,
|
||||||
|
'total' => 0,
|
||||||
|
'tarifa_id' => null,
|
||||||
|
'tarifa_nombre' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// ── Resolver tarifa efectiva ───────────────────────────────────
|
||||||
|
$tarifaId = null;
|
||||||
|
$tarifaNombre = null;
|
||||||
|
$descuentoPct = 0;
|
||||||
|
|
||||||
|
if ($nitEmpresa !== '') {
|
||||||
|
$stmtE = $pdo->prepare(
|
||||||
|
"SELECT e.tarifa_id, e.descuento_pct, ti.nombre AS tarifa_nombre
|
||||||
|
FROM lab_empresas e
|
||||||
|
LEFT JOIN lab_tarifas_id ti ON ti.id = e.tarifa_id
|
||||||
|
WHERE e.nit = ?"
|
||||||
|
);
|
||||||
|
$stmtE->execute([$nitEmpresa]);
|
||||||
|
$empresa = $stmtE->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($empresa) {
|
||||||
|
$tarifaId = $empresa['tarifa_id'];
|
||||||
|
$tarifaNombre = $empresa['tarifa_nombre'];
|
||||||
|
$descuentoPct = (float)$empresa['descuento_pct'];
|
||||||
|
|
||||||
|
// Si el subgrupo tiene tarifa propia, override
|
||||||
|
if ($subgrupoId > 0) {
|
||||||
|
$stmtS = $pdo->prepare(
|
||||||
|
"SELECT es.tarifa_id, ti.nombre AS tarifa_nombre
|
||||||
|
FROM lab_empresa_subgrupos es
|
||||||
|
LEFT JOIN lab_tarifas_id ti ON ti.id = es.tarifa_id
|
||||||
|
WHERE es.id = ? AND es.nit_empresa = ? AND es.tarifa_id IS NOT NULL"
|
||||||
|
);
|
||||||
|
$stmtS->execute([$subgrupoId, $nitEmpresa]);
|
||||||
|
$sub = $stmtS->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($sub) {
|
||||||
|
$tarifaId = $sub['tarifa_id'];
|
||||||
|
$tarifaNombre = $sub['tarifa_nombre'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Obtener nombre/codigo de los exámenes ─────────────────────
|
||||||
|
$ph = implode(',', array_fill(0, count($examIds), '?'));
|
||||||
|
$stmtX = $pdo->prepare(
|
||||||
|
"SELECT id AS exam_tipo_id, codigo, nombre FROM exam_tipos WHERE id IN ($ph)"
|
||||||
|
);
|
||||||
|
$stmtX->execute($examIds);
|
||||||
|
$examMap = [];
|
||||||
|
foreach ($stmtX->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
$examMap[(int)$r['exam_tipo_id']] = $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Obtener precios desde lab_tarifas (con derivación por %) ──
|
||||||
|
$items = [];
|
||||||
|
$subtotal = 0;
|
||||||
|
|
||||||
|
foreach ($examIds as $examId) {
|
||||||
|
$examen = $examMap[$examId] ?? ['codigo' => '?', 'nombre' => 'Desconocido'];
|
||||||
|
$valorFinal = null;
|
||||||
|
$valorBase = null;
|
||||||
|
$tienePrecio = false;
|
||||||
|
|
||||||
|
if ($tarifaId !== null) {
|
||||||
|
// Intentar precio en la tarifa exacta
|
||||||
|
$stmtP = $pdo->prepare(
|
||||||
|
"SELECT lt.valor, ti.porcentaje, ti.tarifa_origen,
|
||||||
|
lt_base.valor AS valor_origen
|
||||||
|
FROM lab_tarifas lt
|
||||||
|
JOIN lab_tarifas_id ti ON ti.id = lt.tarifa_id
|
||||||
|
LEFT JOIN lab_tarifas lt_base ON lt_base.exam_tipo_id = lt.exam_tipo_id
|
||||||
|
AND lt_base.tarifa_id = ti.tarifa_origen
|
||||||
|
WHERE lt.exam_tipo_id = ? AND lt.tarifa_id = ?"
|
||||||
|
);
|
||||||
|
$stmtP->execute([$examId, $tarifaId]);
|
||||||
|
$precio = $stmtP->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($precio) {
|
||||||
|
$tienePrecio = true;
|
||||||
|
$valorBase = (float)$precio['valor'];
|
||||||
|
if ((float)$precio['porcentaje'] > 0 && $precio['tarifa_origen'] && $precio['valor_origen'] !== null) {
|
||||||
|
$valorFinal = round((float)$precio['valor_origen'] * (1 + (float)$precio['porcentaje'] / 100), 0);
|
||||||
|
} else {
|
||||||
|
$valorFinal = $valorBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si no hay precio, dejar null (sin tarifa o sin mapeo)
|
||||||
|
$subtotal += $valorFinal ?? 0;
|
||||||
|
|
||||||
|
$items[] = [
|
||||||
|
'exam_tipo_id' => $examId,
|
||||||
|
'codigo' => $examen['codigo'],
|
||||||
|
'nombre' => $examen['nombre'],
|
||||||
|
'valor_base' => $valorBase,
|
||||||
|
'valor_final' => $valorFinal,
|
||||||
|
'tiene_precio' => $tienePrecio,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Aplicar descuento empresa ─────────────────────────────────
|
||||||
|
$descuentoVal = round($subtotal * $descuentoPct / 100, 0);
|
||||||
|
$total = $subtotal - $descuentoVal;
|
||||||
|
|
||||||
|
jsonOk([
|
||||||
|
'items' => $items,
|
||||||
|
'subtotal' => $subtotal,
|
||||||
|
'descuento_pct' => $descuentoPct,
|
||||||
|
'descuento_val' => $descuentoVal,
|
||||||
|
'total' => $total,
|
||||||
|
'tarifa_id' => $tarifaId,
|
||||||
|
'tarifa_nombre' => $tarifaNombre,
|
||||||
|
]);
|
||||||
@@ -608,6 +608,80 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Sección 3.5: Empresa / Convenio ── -->
|
||||||
|
<div class="ficha-section" id="sec-empresa">
|
||||||
|
<h6><i class="fas fa-building me-1"></i>Empresa / Convenio <span class="text-muted fw-normal">(opcional)</span></h6>
|
||||||
|
<!-- Buscador empresa -->
|
||||||
|
<div id="empresa-seleccionada" class="d-none mb-2">
|
||||||
|
<div class="d-flex align-items-center gap-2 p-2 rounded border" style="background:#f0f9ff">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="fw-semibold small" id="empresa-badge-nombre"></div>
|
||||||
|
<div class="text-muted" style="font-size:.75rem" id="empresa-badge-meta"></div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-link btn-sm p-0 text-danger" onclick="quitarEmpresa()" title="Quitar empresa">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="empresa-buscador">
|
||||||
|
<input type="text" id="inp-buscar-empresa" class="form-control form-control-sm"
|
||||||
|
placeholder="Buscar empresa por nombre o NIT…"
|
||||||
|
autocomplete="off" oninput="buscarEmpresa()">
|
||||||
|
<div class="position-relative">
|
||||||
|
<ul id="empresa-resultados" class="list-unstyled mb-0 border rounded bg-white shadow-sm position-absolute w-100 d-none"
|
||||||
|
style="z-index:200;max-height:200px;overflow-y:auto;top:2px"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="inp-empresa-nit">
|
||||||
|
|
||||||
|
<!-- Subgrupo (se muestra cuando hay subgrupos) -->
|
||||||
|
<div id="wrap-subgrupo" class="d-none mt-2">
|
||||||
|
<label class="form-label mb-1 small fw-semibold">Subgrupo</label>
|
||||||
|
<select id="sel-subgrupo" class="form-select form-select-sm" onchange="recalcularPrecios()">
|
||||||
|
<option value="">— Sin subgrupo —</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Autorización -->
|
||||||
|
<div id="wrap-autorizacion" class="d-none mt-2">
|
||||||
|
<label class="form-label mb-1 small fw-semibold text-warning">
|
||||||
|
<i class="fas fa-key me-1"></i>N.° Autorización <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="inp-autorizacion" class="form-control form-control-sm"
|
||||||
|
placeholder="Número de autorización EPS" maxlength="100">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Diagnóstico -->
|
||||||
|
<div id="wrap-diag" class="d-none mt-2">
|
||||||
|
<label class="form-label mb-1 small fw-semibold">Diagnóstico (CIE-10)</label>
|
||||||
|
<input type="text" id="inp-diag" class="form-control form-control-sm"
|
||||||
|
placeholder="Código CIE-10 diagnóstico principal" maxlength="20">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla de precios calculados -->
|
||||||
|
<div id="panel-precios" class="d-none mt-3">
|
||||||
|
<div class="d-flex align-items-center justify-content-between mb-1">
|
||||||
|
<span class="small fw-semibold text-muted text-uppercase" style="font-size:.7rem;letter-spacing:.06em">
|
||||||
|
Precios
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-light text-dark border small" id="precio-tarifa-label"></span>
|
||||||
|
</div>
|
||||||
|
<div id="tabla-precios" class="mb-1" style="font-size:.82rem"></div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center pt-1 border-top">
|
||||||
|
<span class="fw-bold small">Total calculado</span>
|
||||||
|
<span class="fw-bold text-success" id="precio-total-calc">$0</span>
|
||||||
|
</div>
|
||||||
|
<div id="precio-dcto-row" class="d-none d-flex justify-content-between text-muted" style="font-size:.78rem">
|
||||||
|
<span>Descuento empresa (<span id="precio-dcto-pct"></span>%)</span>
|
||||||
|
<span id="precio-dcto-val" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm w-100 mt-2"
|
||||||
|
onclick="aplicarPrecioCalculado()" id="btn-aplicar-precio">
|
||||||
|
<i class="fas fa-check me-1"></i>Aplicar al cobro
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ── Sección 4: Pago ── -->
|
<!-- ── Sección 4: Pago ── -->
|
||||||
<div class="ficha-section">
|
<div class="ficha-section">
|
||||||
<h6><i class="fas fa-dollar-sign me-1"></i>Cobro (opcional)</h6>
|
<h6><i class="fas fa-dollar-sign me-1"></i>Cobro (opcional)</h6>
|
||||||
@@ -904,6 +978,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
examTS = new TomSelect('#sel-examenes', {
|
examTS = new TomSelect('#sel-examenes', {
|
||||||
plugins: ['remove_button'],
|
plugins: ['remove_button'],
|
||||||
maxOptions: null,
|
maxOptions: null,
|
||||||
|
onItemAdd: () => recalcularPrecios(),
|
||||||
|
onItemRemove: () => recalcularPrecios(),
|
||||||
render: {
|
render: {
|
||||||
option(data, escape) {
|
option(data, escape) {
|
||||||
const consent = data['data-consent'] == '1' || data.consent == '1';
|
const consent = data['data-consent'] == '1' || data.consent == '1';
|
||||||
@@ -1483,6 +1559,11 @@ async function guardarSolicitud() {
|
|||||||
embarazada: document.getElementById('chk-embarazada').checked ? 1 : 0,
|
embarazada: document.getElementById('chk-embarazada').checked ? 1 : 0,
|
||||||
solo_muestras: soloMuestras ? 1 : 0,
|
solo_muestras: soloMuestras ? 1 : 0,
|
||||||
medico_id: parseInt(document.getElementById('inp-medico-id').value) || null,
|
medico_id: parseInt(document.getElementById('inp-medico-id').value) || null,
|
||||||
|
nit_empresa: document.getElementById('inp-empresa-nit').value.trim() || null,
|
||||||
|
subgrupo_id: parseInt(document.getElementById('sel-subgrupo').value) || null,
|
||||||
|
autorizacion: document.getElementById('inp-autorizacion').value.trim() || null,
|
||||||
|
diag_ppal: document.getElementById('inp-diag').value.trim() || null,
|
||||||
|
items_precio: _preciosActivos.length ? _preciosActivos : null,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
@@ -1890,8 +1971,172 @@ function resetCheckboxes() {
|
|||||||
document.getElementById('inp-obs').value = '';
|
document.getElementById('inp-obs').value = '';
|
||||||
resetPagoCombinado();
|
resetPagoCombinado();
|
||||||
resetMedico();
|
resetMedico();
|
||||||
|
quitarEmpresa();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Empresa / Convenio ────────────────────────────────────────
|
||||||
|
let _empresaActiva = null;
|
||||||
|
let _empresaBusqTimer;
|
||||||
|
|
||||||
|
function buscarEmpresa() {
|
||||||
|
clearTimeout(_empresaBusqTimer);
|
||||||
|
_empresaBusqTimer = setTimeout(_doEmpresaSearch, 280);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _doEmpresaSearch() {
|
||||||
|
const q = document.getElementById('inp-buscar-empresa').value.trim();
|
||||||
|
const ul = document.getElementById('empresa-resultados');
|
||||||
|
if (q.length < 2) { ul.classList.add('d-none'); return; }
|
||||||
|
const res = await fetch(`${BASE_URL_API_LAB}empresas.php?action=list&search=${encodeURIComponent(q)}&activa=1&limit=10`);
|
||||||
|
const j = await res.json();
|
||||||
|
const list = j.empresas || [];
|
||||||
|
if (!list.length) { ul.innerHTML = '<li class="px-3 py-2 text-muted small">Sin resultados</li>'; ul.classList.remove('d-none'); return; }
|
||||||
|
ul.innerHTML = list.map(e => `
|
||||||
|
<li class="px-3 py-2 cursor-pointer hover-bg"
|
||||||
|
style="cursor:pointer;border-bottom:1px solid #f1f5f9"
|
||||||
|
onmousedown="seleccionarEmpresa(${JSON.stringify(e)})">
|
||||||
|
<div class="fw-semibold small">${escHtml(e.nombre)}</div>
|
||||||
|
<div class="text-muted" style="font-size:.75rem">NIT ${escHtml(e.nit)}${e.tarifa_nombre ? ' · ' + escHtml(e.tarifa_nombre) : ''}</div>
|
||||||
|
</li>
|
||||||
|
`).join('');
|
||||||
|
ul.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function seleccionarEmpresa(e) {
|
||||||
|
document.getElementById('empresa-resultados').classList.add('d-none');
|
||||||
|
document.getElementById('empresa-buscador').classList.add('d-none');
|
||||||
|
document.getElementById('empresa-seleccionada').classList.remove('d-none');
|
||||||
|
document.getElementById('empresa-badge-nombre').textContent = e.nombre;
|
||||||
|
document.getElementById('empresa-badge-meta').textContent =
|
||||||
|
`NIT ${e.nit}${e.tarifa_nombre ? ' · ' + e.tarifa_nombre : ''}${parseFloat(e.descuento_pct) > 0 ? ' · Dcto: ' + e.descuento_pct + '%' : ''}`;
|
||||||
|
document.getElementById('inp-empresa-nit').value = e.nit;
|
||||||
|
_empresaActiva = e;
|
||||||
|
|
||||||
|
// Mostrar autorización y diagnóstico si la empresa los exige
|
||||||
|
document.getElementById('wrap-autorizacion').classList.toggle('d-none', !parseInt(e.req_autoriza));
|
||||||
|
document.getElementById('wrap-diag').classList.remove('d-none');
|
||||||
|
|
||||||
|
// Cargar subgrupos
|
||||||
|
const res = await fetch(`${BASE_URL_API_LAB}empresa_subgrupos.php?nit_empresa=${encodeURIComponent(e.nit)}`);
|
||||||
|
const j = await res.json();
|
||||||
|
const subs = j.subgrupos || [];
|
||||||
|
const wrapSub = document.getElementById('wrap-subgrupo');
|
||||||
|
const selSub = document.getElementById('sel-subgrupo');
|
||||||
|
if (subs.length) {
|
||||||
|
selSub.innerHTML = '<option value="">— Sin subgrupo —</option>' +
|
||||||
|
subs.map(s => `<option value="${s.id}">${escHtml(s.subgrupo)}${s.tarifa_nombre ? ' · ' + escHtml(s.tarifa_nombre) : ''}</option>`).join('');
|
||||||
|
wrapSub.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
selSub.innerHTML = '<option value="">— Sin subgrupo —</option>';
|
||||||
|
wrapSub.classList.add('d-none');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si es EPS, preseleccionar forma de pago
|
||||||
|
if (document.getElementById('sel-pago').value === '') {
|
||||||
|
document.getElementById('sel-pago').value = 'eps';
|
||||||
|
togglePagoCombinado();
|
||||||
|
}
|
||||||
|
|
||||||
|
recalcularPrecios();
|
||||||
|
}
|
||||||
|
|
||||||
|
function quitarEmpresa() {
|
||||||
|
_empresaActiva = null;
|
||||||
|
document.getElementById('inp-empresa-nit').value = '';
|
||||||
|
document.getElementById('inp-buscar-empresa').value = '';
|
||||||
|
document.getElementById('empresa-seleccionada').classList.add('d-none');
|
||||||
|
document.getElementById('empresa-buscador').classList.remove('d-none');
|
||||||
|
document.getElementById('empresa-resultados').classList.add('d-none');
|
||||||
|
document.getElementById('wrap-subgrupo').classList.add('d-none');
|
||||||
|
document.getElementById('wrap-autorizacion').classList.add('d-none');
|
||||||
|
document.getElementById('wrap-diag').classList.add('d-none');
|
||||||
|
document.getElementById('panel-precios').classList.add('d-none');
|
||||||
|
document.getElementById('sel-subgrupo').innerHTML = '<option value="">— Sin subgrupo —</option>';
|
||||||
|
document.getElementById('inp-autorizacion').value = '';
|
||||||
|
document.getElementById('inp-diag').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Motor de precios ──────────────────────────────────────────
|
||||||
|
let _recalcTimer;
|
||||||
|
let _preciosActivos = [];
|
||||||
|
|
||||||
|
function recalcularPrecios() {
|
||||||
|
clearTimeout(_recalcTimer);
|
||||||
|
_recalcTimer = setTimeout(_doRecalcular, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _doRecalcular() {
|
||||||
|
const examIds = examTS ? examTS.getValue() : [];
|
||||||
|
const nit = document.getElementById('inp-empresa-nit').value.trim();
|
||||||
|
const subId = document.getElementById('sel-subgrupo').value;
|
||||||
|
|
||||||
|
if (!examIds.length || !nit) {
|
||||||
|
document.getElementById('panel-precios').classList.add('d-none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams({ nit_empresa: nit });
|
||||||
|
examIds.forEach(id => params.append('exam_ids[]', id));
|
||||||
|
if (subId) params.append('subgrupo_id', subId);
|
||||||
|
|
||||||
|
const res = await fetch(`${API}get_precios_examenes.php?${params}`);
|
||||||
|
const j = await res.json();
|
||||||
|
if (!j.ok) return;
|
||||||
|
|
||||||
|
_preciosActivos = j.items || [];
|
||||||
|
|
||||||
|
// Tabla
|
||||||
|
const tabla = document.getElementById('tabla-precios');
|
||||||
|
const tienePrecios = j.items.some(i => i.tiene_precio);
|
||||||
|
if (!tienePrecios) {
|
||||||
|
tabla.innerHTML = '<div class="text-muted small text-center py-1">Sin precios configurados para esta tarifa</div>';
|
||||||
|
} else {
|
||||||
|
tabla.innerHTML = j.items.map(i => `
|
||||||
|
<div class="d-flex justify-content-between py-1" style="border-bottom:1px solid #f1f5f9">
|
||||||
|
<span class="text-truncate me-2" style="max-width:200px" title="${escHtml(i.nombre)}">${escHtml(i.nombre)}</span>
|
||||||
|
<span class="${i.tiene_precio ? 'fw-semibold' : 'text-muted'}">
|
||||||
|
${i.tiene_precio ? '$' + fmt(i.valor_final) : 'Sin precio'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Totales
|
||||||
|
document.getElementById('precio-tarifa-label').textContent = j.tarifa_nombre || '';
|
||||||
|
document.getElementById('precio-total-calc').textContent = '$' + fmt(j.subtotal);
|
||||||
|
const dctoRow = document.getElementById('precio-dcto-row');
|
||||||
|
if (j.descuento_pct > 0) {
|
||||||
|
document.getElementById('precio-dcto-pct').textContent = j.descuento_pct;
|
||||||
|
document.getElementById('precio-dcto-val').textContent = '-$' + fmt(j.descuento_val);
|
||||||
|
document.getElementById('precio-total-calc').textContent = '$' + fmt(j.total);
|
||||||
|
dctoRow.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
dctoRow.classList.add('d-none');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('panel-precios').classList.remove('d-none');
|
||||||
|
}
|
||||||
|
|
||||||
|
function aplicarPrecioCalculado() {
|
||||||
|
const total = _preciosActivos.reduce((sum, i) => sum + (i.valor_final || 0), 0);
|
||||||
|
const nit = document.getElementById('inp-empresa-nit').value.trim();
|
||||||
|
const emp = _empresaActiva;
|
||||||
|
const dcto = parseFloat(emp?.descuento_pct || 0);
|
||||||
|
const final = Math.round(total * (1 - dcto / 100));
|
||||||
|
document.getElementById('inp-total').value = final || '';
|
||||||
|
if (emp && !document.getElementById('sel-pago').value) {
|
||||||
|
document.getElementById('sel-pago').value = 'eps';
|
||||||
|
togglePagoCombinado();
|
||||||
|
}
|
||||||
|
mostrarToast('Total aplicado: $' + fmt(final), 'ok', 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt(n) {
|
||||||
|
return Math.round(n || 0).toLocaleString('es-CO');
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_URL_API_LAB = '<?= BASE_URL ?>/api/lab/';
|
||||||
|
|
||||||
function escHtml(str) {
|
function escHtml(str) {
|
||||||
const d = document.createElement('div');
|
const d = document.createElement('div');
|
||||||
d.appendChild(document.createTextNode(String(str)));
|
d.appendChild(document.createTextNode(String(str)));
|
||||||
|
|||||||
Reference in New Issue
Block a user