cambios
This commit is contained in:
parent
e7d3ab34ed
commit
a4cf0f84c3
BIN
backend/src/ANEXO 002 JUAN DAVID GUERRERO DE LA PAVA.pdf
Normal file
BIN
backend/src/ANEXO 002 JUAN DAVID GUERRERO DE LA PAVA.pdf
Normal file
Binary file not shown.
@ -13,6 +13,20 @@ def normalize(text):
|
||||
return text.upper()
|
||||
|
||||
|
||||
def extract_digits_from_text(text, min_len=6, max_len=20):
|
||||
if not text:
|
||||
return None
|
||||
match = re.search(rf"\b\d{{{min_len},{max_len}}}\b", text)
|
||||
if match:
|
||||
return match.group(0)
|
||||
groups = re.findall(rf"(?:\d[\s\-]*){{{min_len},{max_len}}}", text)
|
||||
for group in groups:
|
||||
digits = re.sub(r"\D", "", group)
|
||||
if min_len <= len(digits) <= max_len:
|
||||
return digits
|
||||
return None
|
||||
|
||||
|
||||
def extract_text_pdfplumber(path):
|
||||
try:
|
||||
import pdfplumber # type: ignore
|
||||
@ -133,6 +147,13 @@ def extract_name(lines, norm_lines):
|
||||
break
|
||||
|
||||
if idx == -1:
|
||||
for line in lines:
|
||||
if normalize(line).startswith("PACIENTE:"):
|
||||
parts = line.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
candidate = parts[1].split("-")[0].strip()
|
||||
if candidate:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
skip_tokens = [
|
||||
@ -154,67 +175,207 @@ def extract_name(lines, norm_lines):
|
||||
continue
|
||||
if len(lines[j].split()) >= 2:
|
||||
return lines[j]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_document(lines, norm_lines):
|
||||
idx = -1
|
||||
for i, line in enumerate(norm_lines):
|
||||
if "TIPO DOCUMENTO" in line or "TIPO DE DOCUMENTO" in line:
|
||||
if (
|
||||
"TIPO DOCUMENTO" in line
|
||||
or "TIPO DE DOCUMENTO" in line
|
||||
or "NUMERO DOCUMENTO" in line
|
||||
or "DOCUMENTO DE IDENTIFICACION" in line
|
||||
):
|
||||
idx = i
|
||||
break
|
||||
|
||||
if idx != -1:
|
||||
for j in range(idx, min(idx + 8, len(lines))):
|
||||
digits = extract_digits_from_text(lines[j])
|
||||
if digits:
|
||||
return digits
|
||||
|
||||
for i, line in enumerate(norm_lines):
|
||||
if (
|
||||
"CEDULA" in line
|
||||
or "DOCUMENTO" in line
|
||||
or "NUIP" in line
|
||||
or "PASAPORTE" in line
|
||||
):
|
||||
digits = extract_digits_from_text(lines[i])
|
||||
if digits:
|
||||
return digits
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_cups_code(text):
|
||||
if not text:
|
||||
return None
|
||||
match = re.search(r"\b[A-Z0-9]{4,10}\b", text, re.IGNORECASE)
|
||||
if match:
|
||||
return match.group(0)
|
||||
digits = extract_digits_from_text(text, min_len=4, max_len=10)
|
||||
if digits:
|
||||
return digits
|
||||
return None
|
||||
|
||||
|
||||
def extract_cups(lines, norm_lines):
|
||||
for i, line in enumerate(norm_lines):
|
||||
if (
|
||||
"CUPS" in line
|
||||
or re.search(r"C\s*[\.\-]?\s*U\s*[\.\-]?\s*P\s*[\.\-]?\s*S", line, re.IGNORECASE)
|
||||
or re.search(r"\bCUP\b", line, re.IGNORECASE)
|
||||
):
|
||||
for j in range(i, min(i + 8, len(lines))):
|
||||
code = extract_cups_code(lines[j])
|
||||
if not code:
|
||||
continue
|
||||
desc = ""
|
||||
raw = lines[j]
|
||||
if code in raw:
|
||||
desc = raw.split(code, 1)[-1].strip(" -:")
|
||||
if not desc and j + 1 < len(lines):
|
||||
desc = lines[j + 1].strip()
|
||||
return code, desc or None
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def parse_cie10_from_line(line):
|
||||
if not line:
|
||||
return None, None
|
||||
match = re.search(r"\(([A-Z][0-9]{2,4}[A-Z0-9]?)\)", line, re.IGNORECASE)
|
||||
if match:
|
||||
code = match.group(1).upper()
|
||||
desc = line[: match.start()].strip(" -:")
|
||||
desc = re.sub(r"(?i)diagnostico principal", "", desc).strip()
|
||||
desc = re.sub(r"(?i)impresion diagnostica", "", desc).strip()
|
||||
return code, desc.title() if desc else None
|
||||
match = re.search(r"\b([A-Z])(?:\s*\d){2,4}[A-Z0-9]?\b", line, re.IGNORECASE)
|
||||
if match:
|
||||
code = re.sub(r"\s+", "", match.group(0)).upper()
|
||||
desc = line[match.end():].strip(" -:")
|
||||
if not desc:
|
||||
desc = line[: match.start()].strip(" -:")
|
||||
desc = re.sub(r"(?i)diagnostico principal", "", desc).strip()
|
||||
desc = re.sub(r"(?i)impresion diagnostica", "", desc).strip()
|
||||
return code, desc.title() if desc else None
|
||||
match = re.search(r"\b([A-Z][0-9]{2,4}[A-Z0-9]?)\b", line, re.IGNORECASE)
|
||||
if match:
|
||||
code = match.group(1).upper()
|
||||
desc = line[match.end():].strip(" -:")
|
||||
return code, desc.title() if desc else None
|
||||
return None, None
|
||||
|
||||
|
||||
def extract_cups_hint(lines, norm_lines):
|
||||
keys = [
|
||||
"EXAMENES Y PROCEDIMIENTOS ORDENADOS",
|
||||
"EXAMENES Y PROCEDIMIENTOS",
|
||||
"PROCEDIMIENTOS ORDENADOS",
|
||||
"PROCEDIMIENTOS",
|
||||
]
|
||||
idx = -1
|
||||
for key in keys:
|
||||
for i, line in enumerate(norm_lines):
|
||||
if key in line:
|
||||
idx = i
|
||||
break
|
||||
if idx != -1:
|
||||
break
|
||||
|
||||
if idx == -1:
|
||||
return None
|
||||
|
||||
for j in range(idx + 1, min(idx + 10, len(lines))):
|
||||
raw = lines[j].strip(" -*")
|
||||
nline = norm_lines[j]
|
||||
if not raw or "ORDEN NRO" in nline or "ORDEN NO" in nline:
|
||||
continue
|
||||
if len(raw.split()) < 2:
|
||||
continue
|
||||
candidate = raw.split("(")[0].strip(" -")
|
||||
candidate = re.sub(r"(?i)AUTOMATIZADO", "", candidate).strip(" -")
|
||||
if candidate:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def extract_cie10(lines, norm_lines):
|
||||
for i, nline in enumerate(norm_lines):
|
||||
if (
|
||||
"DIAGNOSTICO PRINCIPAL" in nline
|
||||
or "IMPRESION DIAGNOSTICA" in nline
|
||||
or "DIAGNOSTICO" in nline
|
||||
):
|
||||
candidate = lines[i]
|
||||
if i + 1 < len(lines) and len(lines[i + 1].split()) > 1:
|
||||
candidate = candidate + " " + lines[i + 1]
|
||||
code, desc = parse_cie10_from_line(candidate)
|
||||
if code:
|
||||
return code, desc or None
|
||||
for line in lines:
|
||||
code, desc = parse_cie10_from_line(line)
|
||||
if code:
|
||||
return code, desc or None
|
||||
return None, None
|
||||
|
||||
|
||||
def clean_ips_name(value):
|
||||
if not value:
|
||||
return ""
|
||||
text = value.strip()
|
||||
text = re.split(r"\bCC\b", text, maxsplit=1, flags=re.IGNORECASE)[0]
|
||||
text = re.split(r"\bNUMERO\b", text, maxsplit=1, flags=re.IGNORECASE)[0]
|
||||
text = re.split(r"\bNIT\b", text, maxsplit=1, flags=re.IGNORECASE)[0]
|
||||
text = re.split(r"\bCODIGO\b", text, maxsplit=1, flags=re.IGNORECASE)[0]
|
||||
return text.strip(" -:")
|
||||
|
||||
|
||||
def extract_ips(lines, norm_lines):
|
||||
nombre = None
|
||||
nit = None
|
||||
idx = -1
|
||||
for i, line in enumerate(norm_lines):
|
||||
if "INFORMACION DEL PRESTADOR" in line:
|
||||
idx = i
|
||||
break
|
||||
|
||||
if idx != -1:
|
||||
for j in range(idx + 1, min(idx + 8, len(lines))):
|
||||
digits = re.findall(r"\d{6,}", lines[j])
|
||||
if digits:
|
||||
return digits[-1]
|
||||
|
||||
for i, line in enumerate(norm_lines):
|
||||
if "CEDULA" in line or "DOCUMENTO" in line or "NUIP" in line:
|
||||
digits = re.findall(r"\d{6,}", lines[i])
|
||||
if digits:
|
||||
return digits[-1]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def extract_cups(lines, norm_lines):
|
||||
idx = -1
|
||||
for i, line in enumerate(norm_lines):
|
||||
if "CODIGO CUPS" in line:
|
||||
idx = i
|
||||
if not nit and "NIT" in norm_lines[j]:
|
||||
nit = extract_digits_from_text(lines[j], min_len=6, max_len=15)
|
||||
if not nombre:
|
||||
if "NOMBRE" in norm_lines[j] and ":" in lines[j] and "NIT" not in norm_lines[j]:
|
||||
candidate = lines[j].split(":", 1)[1].strip()
|
||||
candidate = clean_ips_name(candidate)
|
||||
if len(candidate.split()) >= 2:
|
||||
nombre = candidate
|
||||
continue
|
||||
if (
|
||||
"NOMBRE" not in norm_lines[j]
|
||||
and "NIT" not in norm_lines[j]
|
||||
and "CODIGO" not in norm_lines[j]
|
||||
):
|
||||
candidate = clean_ips_name(lines[j])
|
||||
if len(candidate.split()) >= 2:
|
||||
nombre = candidate
|
||||
if nombre and nit:
|
||||
break
|
||||
|
||||
if idx == -1:
|
||||
return None, None
|
||||
if not nombre:
|
||||
for line in lines[:10]:
|
||||
if re.search(r"\b(HOSPITAL|CLINICA|ESE|IPS|CENTRO MEDICO)\b", line, re.IGNORECASE):
|
||||
candidate = clean_ips_name(line)
|
||||
if len(candidate.split()) >= 2:
|
||||
nombre = candidate
|
||||
break
|
||||
|
||||
for j in range(idx + 1, min(idx + 6, len(lines))):
|
||||
tokens = lines[j].split()
|
||||
if not tokens:
|
||||
continue
|
||||
code = tokens[0]
|
||||
if not re.match(r"^[A-Z0-9]{4,10}$", code, re.IGNORECASE):
|
||||
continue
|
||||
desc = ""
|
||||
if len(tokens) >= 2 and re.match(r"^\d+[,.]\d+$", tokens[1]):
|
||||
desc = " ".join(tokens[2:])
|
||||
else:
|
||||
desc = " ".join(tokens[1:])
|
||||
return code, desc.strip() or None
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def extract_cie10(lines, norm_lines):
|
||||
for i, nline in enumerate(norm_lines):
|
||||
if "DIAGNOSTICO PRINCIPAL" in nline:
|
||||
match = re.search(r"DIAGNOSTICO PRINCIPAL\s+([A-Z0-9]{3,6})\s*(.*)", nline)
|
||||
if match:
|
||||
code = match.group(1)
|
||||
desc = match.group(2).strip().title()
|
||||
return code, desc or None
|
||||
return None, None
|
||||
return nombre or None, nit or None
|
||||
|
||||
|
||||
def detect_format(norm_text, norm_lines):
|
||||
@ -234,7 +395,9 @@ def build_response(text, ocr_used, ocr_available, ocr_error):
|
||||
nombre = extract_name(lines, norm_lines)
|
||||
documento = extract_document(lines, norm_lines)
|
||||
cup_codigo, cup_desc = extract_cups(lines, norm_lines)
|
||||
cups_busqueda = extract_cups_hint(lines, norm_lines)
|
||||
cie_codigo, cie_desc = extract_cie10(lines, norm_lines)
|
||||
ips_nombre, ips_nit = extract_ips(lines, norm_lines)
|
||||
formato = detect_format(norm_text, norm_lines)
|
||||
|
||||
warnings = []
|
||||
@ -244,6 +407,8 @@ def build_response(text, ocr_used, ocr_available, ocr_error):
|
||||
warnings.append("cups_not_found")
|
||||
if not cie_codigo:
|
||||
warnings.append("cie10_not_found")
|
||||
if not ips_nombre and not ips_nit:
|
||||
warnings.append("ips_not_found")
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
@ -256,8 +421,11 @@ def build_response(text, ocr_used, ocr_available, ocr_error):
|
||||
"numero_documento": documento,
|
||||
"cup_codigo": cup_codigo,
|
||||
"cup_descripcion": cup_desc,
|
||||
"cups_busqueda": cups_busqueda,
|
||||
"cie10_codigo": cie_codigo,
|
||||
"cie10_descripcion": cie_desc,
|
||||
"ips_nombre": ips_nombre,
|
||||
"ips_nit": ips_nit,
|
||||
"warnings": warnings,
|
||||
}
|
||||
|
||||
|
||||
BIN
backend/src/hc juan david guerrero de la pava.pdf
Normal file
BIN
backend/src/hc juan david guerrero de la pava.pdf
Normal file
Binary file not shown.
@ -2697,6 +2697,11 @@ async function crearLibroAutorizacion(a) {
|
||||
sheet.getCell(celda).value = nitIps;
|
||||
});
|
||||
|
||||
const telefonoIps = a.telefono_ips || '';
|
||||
['D14', 'E14', 'F14', 'G14', 'D15', 'E15', 'F15', 'G15', 'D16', 'E16', 'F16', 'G16'].forEach(celda => {
|
||||
sheet.getCell(celda).value = telefonoIps;
|
||||
});
|
||||
|
||||
sheet.getCell('M21').value = a.cie10_codigo || '';
|
||||
sheet.getCell('N21').value = a.cie10_descripcion || '';
|
||||
|
||||
@ -2753,22 +2758,29 @@ async function crearLibroAutorizacion(a) {
|
||||
if (cupDescripcion) cupInfoParts.push(cupDescripcion);
|
||||
if (nivelTexto) cupInfoParts.push(nivelTexto);
|
||||
const cupInfo = cupInfoParts.join(' - ');
|
||||
const observacionBase = a.observacion || '';
|
||||
const limpiarObservacion = (value) => {
|
||||
const partes = String(value || '')
|
||||
.split('|')
|
||||
.map((parte) => parte.trim())
|
||||
.filter(Boolean)
|
||||
.filter((parte) => {
|
||||
const lower = parte.toLowerCase();
|
||||
return (
|
||||
!lower.includes('ips sin convenio') &&
|
||||
!lower.includes('cups no cubierto') &&
|
||||
!lower.includes('cargado por')
|
||||
);
|
||||
});
|
||||
return partes.join(' | ');
|
||||
};
|
||||
const observacionBase = limpiarObservacion(a.observacion || '');
|
||||
const solicitanteNombre = String(a.nombre_solicitante || '').trim();
|
||||
const observacionLower = observacionBase.toLowerCase();
|
||||
const solicitanteInfo =
|
||||
solicitanteNombre && !observacionLower.includes('solicitante')
|
||||
? `Solicitante: ${solicitanteNombre}`
|
||||
: '';
|
||||
const tipoAutorizacion = (a.tipo_autorizacion || 'consultas_externas').toLowerCase();
|
||||
const tipoServicioRaw = String(a.tipo_servicio || '').trim();
|
||||
let tipoServicioTexto = '';
|
||||
if (tipoServicioRaw) {
|
||||
tipoServicioTexto = `Tipo servicio: ${tipoServicioRaw}`;
|
||||
} else if (tipoAutorizacion === 'consultas_externas') {
|
||||
tipoServicioTexto = 'Tipo servicio: Consulta externa';
|
||||
}
|
||||
const observacion = [cupInfo, tipoServicioTexto, observacionBase, solicitanteInfo]
|
||||
const observacion = [cupInfo, observacionBase, solicitanteInfo]
|
||||
.filter(Boolean)
|
||||
.join(' | ');
|
||||
['H31','R31'].forEach(celda => {
|
||||
|
||||
@ -2373,6 +2373,11 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
sheet.getCell(celda).value = nitIps;
|
||||
});
|
||||
|
||||
const telefonoIps = a.telefono_ips || '';
|
||||
['D14', 'E14', 'F14', 'G14', 'D15', 'E15', 'F15', 'G15', 'D16', 'E16', 'F16', 'G16'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = telefonoIps;
|
||||
});
|
||||
|
||||
sheet.getCell('M21').value = a.cie10_codigo || '';
|
||||
sheet.getCell('N21').value = a.cie10_descripcion || '';
|
||||
|
||||
@ -2423,7 +2428,22 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
if (cupDescripcion) cupInfoParts.push(cupDescripcion);
|
||||
if (nivelTexto) cupInfoParts.push(nivelTexto);
|
||||
const cupInfo = cupInfoParts.join(' - ');
|
||||
const observacionBase = a.observacion || '';
|
||||
const limpiarObservacion = (value) => {
|
||||
const partes = String(value || '')
|
||||
.split('|')
|
||||
.map((parte) => parte.trim())
|
||||
.filter(Boolean)
|
||||
.filter((parte) => {
|
||||
const lower = parte.toLowerCase();
|
||||
return (
|
||||
!lower.includes('ips sin convenio') &&
|
||||
!lower.includes('cups no cubierto') &&
|
||||
!lower.includes('cargado por')
|
||||
);
|
||||
});
|
||||
return partes.join(' | ');
|
||||
};
|
||||
const observacionBase = limpiarObservacion(a.observacion || '');
|
||||
const solicitanteNombre = String(a.nombre_solicitante || '').trim();
|
||||
const observacionLower = observacionBase.toLowerCase();
|
||||
const solicitanteInfo =
|
||||
|
||||
@ -152,7 +152,7 @@ const extraerDatosPdfAutorizacion = (filePath) => {
|
||||
pythonPath,
|
||||
[scriptPath, filePath],
|
||||
{ cwd: __dirname },
|
||||
(error, stdout, stderr) => {
|
||||
async (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
return reject(
|
||||
new Error(stderr?.trim() || error.message || 'Error ejecutando extractor')
|
||||
@ -160,6 +160,67 @@ const extraerDatosPdfAutorizacion = (filePath) => {
|
||||
}
|
||||
try {
|
||||
const data = parseJsonOutput(stdout);
|
||||
const nit = String(data?.ips_nit || '').replace(/\D/g, '');
|
||||
const nombre = String(data?.ips_nombre || '').trim();
|
||||
let ipsRow = null;
|
||||
|
||||
if (nit) {
|
||||
const { rows } = await pool.query(
|
||||
'SELECT id_ips, nombre_ips, nit FROM ips WHERE nit = $1 LIMIT 1',
|
||||
[nit]
|
||||
);
|
||||
ipsRow = rows[0] || null;
|
||||
}
|
||||
|
||||
if (!ipsRow && nombre) {
|
||||
const nombreClean = nombre.replace(/\s+/g, ' ').trim();
|
||||
const nombreLike = `%${nombreClean}%`;
|
||||
const { rows } = await pool.query(
|
||||
`
|
||||
SELECT id_ips, nombre_ips, nit
|
||||
FROM ips
|
||||
WHERE UPPER(nombre_ips) = UPPER($1)
|
||||
OR nombre_ips ILIKE $2
|
||||
LIMIT 1
|
||||
`,
|
||||
[nombreClean, nombreLike]
|
||||
);
|
||||
ipsRow = rows[0] || null;
|
||||
}
|
||||
|
||||
if (ipsRow) {
|
||||
data.id_ips = ipsRow.id_ips;
|
||||
data.ips_nombre = ipsRow.nombre_ips;
|
||||
data.ips_nit = ipsRow.nit;
|
||||
}
|
||||
|
||||
const cupCodigoActual = String(data?.cup_codigo || '').trim();
|
||||
const cupBusqueda = String(data?.cups_busqueda || '').trim();
|
||||
if (!cupCodigoActual && cupBusqueda.length >= 4) {
|
||||
const term = cupBusqueda.replace(/\s+/g, ' ').trim();
|
||||
const { rows } = await pool.query(
|
||||
`
|
||||
SELECT codigo, descripcion
|
||||
FROM cups_cubiertos
|
||||
WHERE descripcion ILIKE $1
|
||||
ORDER BY
|
||||
CASE WHEN UPPER(descripcion) = UPPER($2) THEN 0 ELSE 1 END,
|
||||
LENGTH(descripcion) ASC
|
||||
LIMIT 2
|
||||
`,
|
||||
[`%${term}%`, term]
|
||||
);
|
||||
if (rows.length >= 1) {
|
||||
data.cup_codigo = rows[0].codigo;
|
||||
data.cup_descripcion = rows[0].descripcion;
|
||||
if (Array.isArray(data.warnings)) {
|
||||
data.warnings = data.warnings.filter((w) => w !== 'cups_not_found');
|
||||
}
|
||||
if (rows.length > 1 && Array.isArray(data.warnings)) {
|
||||
data.warnings.push('cups_multiple_matches');
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(data);
|
||||
} catch (parseError) {
|
||||
reject(parseError);
|
||||
@ -1067,6 +1128,12 @@ app.post(
|
||||
upload.single('archivo'),
|
||||
async (req, res) => {
|
||||
try {
|
||||
if (req.usuario.nombre_rol !== 'administrador') {
|
||||
return res.status(403).json({
|
||||
error: 'Solo administradores pueden autorrellenar autorizaciones',
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ error: 'No se recibio archivo PDF' });
|
||||
}
|
||||
@ -1108,6 +1175,12 @@ app.post(
|
||||
return res.status(400).json({ error: 'numero_autorizacion es obligatorio' });
|
||||
}
|
||||
|
||||
if (req.usuario.nombre_rol !== 'administrador') {
|
||||
return res.status(403).json({
|
||||
error: 'Solo administradores pueden subir archivos hospitalarios',
|
||||
});
|
||||
}
|
||||
|
||||
const historialFile = req.files?.historial_clinico?.[0] || null;
|
||||
const anexoFile = req.files?.anexo?.[0] || null;
|
||||
|
||||
@ -2070,6 +2143,9 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const servicioExcel = getValue(row, 'SERVICIO');
|
||||
const servicioInfo = parseServicio(servicioExcel);
|
||||
|
||||
const ambitoRaw = getValueMulti(row, [
|
||||
'AMBITO',
|
||||
'AMBITOATENCION',
|
||||
@ -2078,7 +2154,10 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) {
|
||||
'TIPOATENCION',
|
||||
'TIPODEORDEN',
|
||||
]);
|
||||
const ambitoAtencion = parseAmbito(ambitoRaw);
|
||||
let ambitoAtencion = parseAmbito(ambitoRaw);
|
||||
if (servicioInfo.tipo_servicio === 'hospitalarios') {
|
||||
ambitoAtencion = 'extramural';
|
||||
}
|
||||
if (!ambitoAtencion) {
|
||||
resumen.omitidas += 1;
|
||||
resumen.sin_ambito += 1;
|
||||
@ -2270,8 +2349,19 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) {
|
||||
resumen.ips_sin_convenio += 1;
|
||||
}
|
||||
|
||||
const servicioExcel = getValue(row, 'SERVICIO');
|
||||
const servicioInfo = parseServicio(servicioExcel);
|
||||
if (
|
||||
servicioInfo.tipo_servicio === 'hospitalarios' &&
|
||||
usuario?.nombre_rol !== 'administrador'
|
||||
) {
|
||||
resumen.omitidas += 1;
|
||||
if (resumen.errores.length < 50) {
|
||||
resumen.errores.push({
|
||||
fila: i,
|
||||
error: 'Hospitalarios solo permitido para administradores',
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const dupRes = await client.query(
|
||||
`
|
||||
@ -2313,15 +2403,6 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) {
|
||||
if (obs1) observaciones.push(obs1);
|
||||
if (obs2) observaciones.push(obs2);
|
||||
if (solicitante) observaciones.push(`Solicitante: ${solicitante}`);
|
||||
if (!ipsInfo.tiene_convenio) {
|
||||
observaciones.push(`IPS sin convenio${hospital ? `: ${hospital}` : ''}`);
|
||||
}
|
||||
if (!cupCubierto) {
|
||||
observaciones.push(`CUPS no cubierto: ${cupCodigo}`);
|
||||
}
|
||||
if (usuario?.nombre_completo) {
|
||||
observaciones.push(`Cargado por: ${usuario.nombre_completo}`);
|
||||
}
|
||||
|
||||
const observacionFinal = observaciones.filter(Boolean).join(' | ') || null;
|
||||
|
||||
@ -2431,6 +2512,7 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
ips.nombre_ips,
|
||||
ips.nit,
|
||||
ips.direccion,
|
||||
ips.telefono AS telefono_ips,
|
||||
ips.municipio,
|
||||
ips.departamento,
|
||||
aut.nombre AS nombre_autorizante,
|
||||
@ -2439,7 +2521,7 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
aut.telefono AS tel_autorizante,
|
||||
e.nombre_establecimiento,
|
||||
e.epc_departamento,
|
||||
cc.descripcion AS cup_descripcion,
|
||||
COALESCE(cc.descripcion, cr.descripcion) AS cup_descripcion,
|
||||
cc.nivel AS cup_nivel,
|
||||
cc.especialidad AS cup_especialidad
|
||||
FROM autorizacion a
|
||||
@ -2457,6 +2539,8 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
ON i.codigo_establecimiento = e.codigo_establecimiento
|
||||
LEFT JOIN cups_cubiertos cc
|
||||
ON a.cup_codigo = cc.codigo
|
||||
LEFT JOIN cups_referencia cr
|
||||
ON a.cup_codigo = cr.codigo
|
||||
WHERE a.numero_autorizacion = $1
|
||||
LIMIT 1;
|
||||
`;
|
||||
@ -2481,6 +2565,7 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
ips.nombre_ips,
|
||||
ips.nit,
|
||||
ips.direccion,
|
||||
ips.telefono AS telefono_ips,
|
||||
ips.municipio,
|
||||
ips.departamento,
|
||||
aut.nombre AS nombre_autorizante,
|
||||
@ -2489,7 +2574,7 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
aut.telefono AS tel_autorizante,
|
||||
e.nombre_establecimiento,
|
||||
e.epc_departamento,
|
||||
cc.descripcion AS cup_descripcion,
|
||||
COALESCE(cc.descripcion, cr.descripcion) AS cup_descripcion,
|
||||
cc.nivel AS cup_nivel,
|
||||
cc.especialidad AS cup_especialidad
|
||||
FROM autorizacion a
|
||||
@ -2509,6 +2594,8 @@ async function generarPdfAutorizacionYObtenerPath(
|
||||
ON i.codigo_establecimiento = e.codigo_establecimiento
|
||||
LEFT JOIN cups_cubiertos cc
|
||||
ON av.cup_codigo = cc.codigo
|
||||
LEFT JOIN cups_referencia cr
|
||||
ON av.cup_codigo = cr.codigo
|
||||
WHERE a.numero_autorizacion = $1
|
||||
AND av.version = $2
|
||||
LIMIT 1;
|
||||
@ -2685,7 +2772,7 @@ async function generarZipAutorizacionesPorFecha(
|
||||
aut.telefono AS tel_autorizante,
|
||||
e.nombre_establecimiento,
|
||||
e.epc_departamento,
|
||||
cc.descripcion AS cup_descripcion,
|
||||
COALESCE(cc.descripcion, cr.descripcion) AS cup_descripcion,
|
||||
cc.nivel AS cup_nivel,
|
||||
cc.especialidad AS cup_especialidad
|
||||
FROM autorizacion a
|
||||
@ -2700,6 +2787,8 @@ async function generarZipAutorizacionesPorFecha(
|
||||
ON i.codigo_establecimiento = e.codigo_establecimiento
|
||||
LEFT JOIN cups_cubiertos cc
|
||||
ON a.cup_codigo = cc.codigo
|
||||
LEFT JOIN cups_referencia cr
|
||||
ON a.cup_codigo = cr.codigo
|
||||
WHERE a.numero_autorizacion = $1
|
||||
LIMIT 1;
|
||||
`;
|
||||
@ -3384,21 +3473,6 @@ app.post('/api/autorizaciones', verificarToken, puedeGenerarAutorizaciones, asyn
|
||||
return res.status(400).json({ error: 'cie10_descripcion es obligatorio' });
|
||||
}
|
||||
|
||||
const ambitoAtencion = String(ambito_atencion || '').trim().toLowerCase();
|
||||
const ambitosPermitidos = ['intramural', 'extramural'];
|
||||
if (!ambitoAtencion) {
|
||||
return res.status(400).json({ error: 'ambito_atencion es obligatorio' });
|
||||
}
|
||||
if (!ambitosPermitidos.includes(ambitoAtencion)) {
|
||||
return res.status(400).json({ error: 'ambito_atencion invalido' });
|
||||
}
|
||||
|
||||
const numeroOrdenInput = String(numero_orden || '').trim();
|
||||
const numeroOrdenFinal =
|
||||
ambitoAtencion === 'intramural' && numeroOrdenInput
|
||||
? numeroOrdenInput
|
||||
: null;
|
||||
|
||||
const estadoEntregaInput =
|
||||
estado_entrega === undefined ? '' : String(estado_entrega);
|
||||
const estadoEntregaParsed = estadoEntregaInput
|
||||
@ -3434,6 +3508,30 @@ app.post('/api/autorizaciones', verificarToken, puedeGenerarAutorizaciones, asyn
|
||||
|
||||
// ... aquí va tu lógica de permisos por rol / sede ...
|
||||
|
||||
if (tipoServicio === 'hospitalarios' && req.usuario.nombre_rol !== 'administrador') {
|
||||
return res.status(403).json({
|
||||
error: 'Solo administradores pueden generar autorizaciones hospitalarias',
|
||||
});
|
||||
}
|
||||
|
||||
let ambitoAtencion = String(ambito_atencion || '').trim().toLowerCase();
|
||||
const ambitosPermitidos = ['intramural', 'extramural'];
|
||||
if (tipoServicio === 'hospitalarios') {
|
||||
ambitoAtencion = 'extramural';
|
||||
}
|
||||
if (!ambitoAtencion) {
|
||||
return res.status(400).json({ error: 'ambito_atencion es obligatorio' });
|
||||
}
|
||||
if (!ambitosPermitidos.includes(ambitoAtencion)) {
|
||||
return res.status(400).json({ error: 'ambito_atencion invalido' });
|
||||
}
|
||||
|
||||
const numeroOrdenInput = String(numero_orden || '').trim();
|
||||
let numeroOrdenFinal = null;
|
||||
if (ambitoAtencion === 'intramural' && numeroOrdenInput) {
|
||||
numeroOrdenFinal = numeroOrdenInput;
|
||||
}
|
||||
|
||||
const solicitanteId = req.usuario?.id_usuario ? Number(req.usuario.id_usuario) : null;
|
||||
const client = await pool.connect();
|
||||
|
||||
@ -3621,10 +3719,6 @@ app.put('/api/autorizaciones/:numero_autorizacion', verificarToken, puedeGenerar
|
||||
const cie10CodigoInput = String(cie10_codigo || '').trim().toUpperCase();
|
||||
const cie10DescripcionInput = String(cie10_descripcion || '').trim();
|
||||
const ambitoInput = String(ambito_atencion || '').trim().toLowerCase();
|
||||
const ambitosPermitidos = ['intramural', 'extramural'];
|
||||
if (ambitoInput && !ambitosPermitidos.includes(ambitoInput)) {
|
||||
return res.status(400).json({ error: 'ambito_atencion invalido' });
|
||||
}
|
||||
|
||||
const tipoAutorizacion = String(tipo_autorizacion || 'consultas_externas')
|
||||
.trim()
|
||||
@ -3649,6 +3743,12 @@ app.put('/api/autorizaciones/:numero_autorizacion', verificarToken, puedeGenerar
|
||||
tipoServicio = servicio;
|
||||
}
|
||||
|
||||
if (tipoServicio === 'hospitalarios' && req.usuario.nombre_rol !== 'administrador') {
|
||||
return res.status(403).json({
|
||||
error: 'Solo administradores pueden actualizar autorizaciones hospitalarias',
|
||||
});
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
@ -3668,7 +3768,11 @@ app.put('/api/autorizaciones/:numero_autorizacion', verificarToken, puedeGenerar
|
||||
const versionActual = Number(actual.version) || 1;
|
||||
const cie10Codigo = cie10CodigoInput || actual.cie10_codigo || '';
|
||||
const cie10Descripcion = cie10DescripcionInput || actual.cie10_descripcion || '';
|
||||
const ambitoAtencion = ambitoInput || actual.ambito_atencion || '';
|
||||
const ambitosPermitidos = ['intramural', 'extramural'];
|
||||
let ambitoAtencion = ambitoInput || actual.ambito_atencion || '';
|
||||
if (tipoServicio === 'hospitalarios') {
|
||||
ambitoAtencion = 'extramural';
|
||||
}
|
||||
|
||||
if (!cie10Codigo) {
|
||||
await client.query('ROLLBACK');
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -172,13 +172,13 @@
|
||||
<option value="">-- Seleccione --</option>
|
||||
<option value="brigadas">Brigadas</option>
|
||||
<option value="ambulancias">Ambulancias</option>
|
||||
<option value="hospitalarios">Hospitalarios</option>
|
||||
<option value="hospitalarios" *ngIf="isAdministrador()">Hospitalarios</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios' && isAdministrador()"
|
||||
>
|
||||
<label for="historialClinico">Historial clinico (HC):</label>
|
||||
<div class="file-field">
|
||||
@ -196,7 +196,7 @@
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios' && isAdministrador()"
|
||||
>
|
||||
<label for="anexoHospitalario">Anexo:</label>
|
||||
<div class="file-field">
|
||||
@ -214,7 +214,7 @@
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios' && isAdministrador()"
|
||||
>
|
||||
<label></label>
|
||||
<div class="file-actions">
|
||||
@ -243,6 +243,7 @@
|
||||
id="ambitoAtencion"
|
||||
[(ngModel)]="formAutorizacion.ambito_atencion"
|
||||
(change)="onAmbitoChange()"
|
||||
[disabled]="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
>
|
||||
<option value="">-- Seleccione --</option>
|
||||
<option value="intramural">Intramural</option>
|
||||
|
||||
@ -345,12 +345,30 @@ export class AutorizacionesComponent {
|
||||
const servicio = String(this.formAutorizacion.tipo_servicio || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (servicio === 'hospitalarios' && !this.isAdministrador()) {
|
||||
this.formAutorizacion.tipo_servicio = '';
|
||||
this.limpiarArchivosHospitalarios();
|
||||
this.errorAutorizacion = 'Solo administradores pueden seleccionar hospitalarios.';
|
||||
return;
|
||||
}
|
||||
if (servicio === 'hospitalarios') {
|
||||
this.formAutorizacion.ambito_atencion = 'extramural';
|
||||
this.formAutorizacion.numero_orden = '';
|
||||
}
|
||||
if (servicio !== 'hospitalarios') {
|
||||
this.limpiarArchivosHospitalarios();
|
||||
}
|
||||
}
|
||||
|
||||
onAmbitoChange(): void {
|
||||
const servicio = String(this.formAutorizacion.tipo_servicio || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (servicio === 'hospitalarios') {
|
||||
this.formAutorizacion.ambito_atencion = 'extramural';
|
||||
this.formAutorizacion.numero_orden = '';
|
||||
return;
|
||||
}
|
||||
const ambito = String(this.formAutorizacion.ambito_atencion || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
@ -398,6 +416,18 @@ export class AutorizacionesComponent {
|
||||
if (resp?.cie10_descripcion) {
|
||||
this.formAutorizacion.cie10_descripcion = resp.cie10_descripcion;
|
||||
}
|
||||
if (resp?.id_ips) {
|
||||
this.formAutorizacion.id_ips = String(resp.id_ips);
|
||||
const existe = this.ipsDisponibles.some(
|
||||
(ips) => String(ips.id_ips) === String(resp.id_ips)
|
||||
);
|
||||
if (!existe && this.pacienteSeleccionado) {
|
||||
this.verMasIps = true;
|
||||
this.cargarIps(this.pacienteSeleccionado.interno, true);
|
||||
} else {
|
||||
this.onIpsChange();
|
||||
}
|
||||
}
|
||||
|
||||
const infoParts = [];
|
||||
if (resp?.nombre_paciente) {
|
||||
@ -406,6 +436,9 @@ export class AutorizacionesComponent {
|
||||
if (resp?.numero_documento) {
|
||||
infoParts.push(`Documento: ${resp.numero_documento}`);
|
||||
}
|
||||
if (resp?.ips_nombre) {
|
||||
infoParts.push(`IPS: ${resp.ips_nombre}`);
|
||||
}
|
||||
if (resp?.formato) {
|
||||
infoParts.push(`Formato: ${this.getFormatoPdfLabel(resp.formato)}`);
|
||||
}
|
||||
@ -657,6 +690,7 @@ export class AutorizacionesComponent {
|
||||
this.cupSeleccionado = null;
|
||||
this.limpiarArchivosHospitalarios();
|
||||
|
||||
this.onTipoServicioChange();
|
||||
this.onTipoAutorizacionChange();
|
||||
this.onIpsChange();
|
||||
if (this.pacienteSeleccionado) {
|
||||
@ -692,6 +726,14 @@ export class AutorizacionesComponent {
|
||||
this.errorAutorizacion = null;
|
||||
this.mensajeAutorizacion = null;
|
||||
|
||||
const servicio = String(this.formAutorizacion.tipo_servicio || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (servicio === 'hospitalarios' && !this.isAdministrador()) {
|
||||
this.errorAutorizacion = 'Solo administradores pueden generar hospitalarios.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.formAutorizacion.id_ips) {
|
||||
this.errorAutorizacion = 'Debe seleccionar una IPS.';
|
||||
return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user