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;
|
||||
$embarazada = !empty($datos['embarazada']) ? 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;
|
||||
if ($metodoPago === 'combinado' && isset($datos['pagos_detalle']) && is_array($datos['pagos_detalle'])) {
|
||||
$pd = $datos['pagos_detalle'];
|
||||
@@ -117,12 +123,15 @@ try {
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"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)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
(turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, pagos_detalle,
|
||||
observaciones, embarazada, solo_muestras, medico_id,
|
||||
nit_empresa, subgrupo_id, autorizacion, diag_ppal, items_precio, creado_por)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([
|
||||
$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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!-- ── 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 ── -->
|
||||
<div class="ficha-section">
|
||||
<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', {
|
||||
plugins: ['remove_button'],
|
||||
maxOptions: null,
|
||||
onItemAdd: () => recalcularPrecios(),
|
||||
onItemRemove: () => recalcularPrecios(),
|
||||
render: {
|
||||
option(data, escape) {
|
||||
const consent = data['data-consent'] == '1' || data.consent == '1';
|
||||
@@ -1483,6 +1559,11 @@ async function guardarSolicitud() {
|
||||
embarazada: document.getElementById('chk-embarazada').checked ? 1 : 0,
|
||||
solo_muestras: soloMuestras ? 1 : 0,
|
||||
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();
|
||||
@@ -1890,8 +1971,172 @@ function resetCheckboxes() {
|
||||
document.getElementById('inp-obs').value = '';
|
||||
resetPagoCombinado();
|
||||
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) {
|
||||
const d = document.createElement('div');
|
||||
d.appendChild(document.createTextNode(String(str)));
|
||||
|
||||
Reference in New Issue
Block a user