diff --git a/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO ANEXO.pdf b/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO ANEXO.pdf new file mode 100644 index 0000000..bbfb160 Binary files /dev/null and b/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO ANEXO.pdf differ diff --git a/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO HC.pdf b/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO HC.pdf new file mode 100644 index 0000000..edcee72 Binary files /dev/null and b/CC 1016076860 -- EDISON ANDRES GOMEZ JULIO HC.pdf differ diff --git a/backend/src/__pycache__/extraer_autorizacion_pdf.cpython-313.pyc b/backend/src/__pycache__/extraer_autorizacion_pdf.cpython-313.pyc new file mode 100644 index 0000000..30420a6 Binary files /dev/null and b/backend/src/__pycache__/extraer_autorizacion_pdf.cpython-313.pyc differ diff --git a/backend/src/extraer_autorizacion_pdf.py b/backend/src/extraer_autorizacion_pdf.py index bf7f86f..8922565 100644 --- a/backend/src/extraer_autorizacion_pdf.py +++ b/backend/src/extraer_autorizacion_pdf.py @@ -27,6 +27,27 @@ def extract_digits_from_text(text, min_len=6, max_len=20): return None +def extract_date_from_text(text): + if not text: + return None + match = re.search( + r"\b(\d{1,2}[/-]\d{1,2}[/-]\d{2,4}|\d{4}[/-]\d{1,2}[/-]\d{1,2})\b", + text, + ) + if match: + return match.group(1) + return None + + +def extract_time_from_text(text): + if not text: + return None + match = re.search(r"\b(\d{1,2}:\d{2})\b", text) + if match: + return match.group(1) + return None + + def extract_text_pdfplumber(path): try: import pdfplumber # type: ignore @@ -214,7 +235,9 @@ def extract_document(lines, norm_lines): def extract_cups_code(text): if not text: return None - match = re.search(r"\b[A-Z0-9]{4,10}\b", text, re.IGNORECASE) + match = re.search( + r"\b(?=[A-Z0-9]{4,10}\b)[A-Z]*\d[A-Z0-9]*\b", text, re.IGNORECASE + ) if match: return match.group(0) digits = extract_digits_from_text(text, min_len=4, max_len=10) @@ -223,25 +246,84 @@ def extract_cups_code(text): 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 +def extract_cups_list(lines, norm_lines): + cups = [] + header_idx = -1 + for i, nline in enumerate(norm_lines): + if "CUPS" in nline and "CODIGO" in nline: + header_idx = i + break + stop_tokens = [ + "JUSTIFICACION", + "IMPRESION", + "DIAGNOSTICO", + "INFORMACION", + "NOMBRE", + "RESPONSABLE", + "SOLICITA", + "SOLICITANTE", + "FIRMA", + ] + + def add_cup(code, desc): + if not code: + return + for existing, _ in cups: + if existing == code: + return + cups.append((code, desc or None)) + + if header_idx != -1: + for j in range(header_idx + 1, min(header_idx + 20, len(lines))): + nline = norm_lines[j] + if any(token in nline for token in stop_tokens): + break + code = extract_cups_code(lines[j]) + if not code: + continue + raw = lines[j] + desc = "" + if code in raw: + desc = raw.split(code, 1)[-1].strip(" -:") + desc = re.sub(r"^\d+[.,]\d{1,2}(?:\s+|$)", "", desc) + desc = re.sub(r"^\d+(?:\s+|$)", "", desc) + if not desc and j + 1 < len(lines): + desc = lines[j + 1].strip() + add_cup(code, desc) + + if not cups: + 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 + raw = lines[j] + desc = "" + if code in raw: + desc = raw.split(code, 1)[-1].strip(" -:") + if not desc and j + 1 < len(lines): + desc = lines[j + 1].strip() + add_cup(code, desc) + if cups: + break + + return cups + + +def extract_cups(lines, norm_lines): + cups = extract_cups_list(lines, norm_lines) + if cups: + return cups[0] return None, None @@ -305,26 +387,62 @@ def extract_cups_hint(lines, norm_lines): return None -def extract_cie10(lines, norm_lines): +def extract_cie10_list(lines, norm_lines): + results = [] + seen = set() for i, nline in enumerate(norm_lines): if ( - "DIAGNOSTICO PRINCIPAL" in nline + "DIAGNOSTICO" in nline or "IMPRESION DIAGNOSTICA" in nline - or "DIAGNOSTICO" in nline + or "CIE10" 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 + for j in range(i, min(i + 6, len(lines))): + code, desc = parse_cie10_from_line(lines[j]) + if not code: + continue + if code in seen: + continue + seen.add(code) + results.append((code, desc)) + return results + + +def extract_cie10(lines, norm_lines): + cie_list = extract_cie10_list(lines, norm_lines) + if cie_list: + return cie_list[0] return None, None +def extract_fecha_ingreso_urgencias(lines, norm_lines): + keys = [ + "INGRESO A URGENCIAS", + "INGRESO URGENCIAS", + "FECHA DE INGRESO", + "FECHA INGRESO", + "INGRESA", + "INGRESO", + ] + for i, nline in enumerate(norm_lines): + if not any(key in nline for key in keys): + continue + match = re.search( + r"INGRES[AO][^0-9]{0,20}(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", + nline, + ) + if match: + return match.group(1) + date = extract_date_from_text(lines[i]) + if not date and i + 1 < len(lines): + date = extract_date_from_text(lines[i + 1]) + if date: + time = extract_time_from_text(lines[i]) or ( + extract_time_from_text(lines[i + 1]) if i + 1 < len(lines) else None + ) + return f"{date} {time}" if time else date + return None + + def clean_ips_name(value): if not value: return "" @@ -387,6 +505,76 @@ def detect_format(norm_text, norm_lines): return "DESCONOCIDO" +def merge_unique(base_list, extra_list): + result = list(base_list or []) + for item in extra_list or []: + if item not in result: + result.append(item) + return result + + +def merge_response(base, extra): + result = dict(base) + + if base.get("cups_codigos"): + result["cups_codigos"] = list(base.get("cups_codigos") or []) + result["cups_descripciones"] = list(base.get("cups_descripciones") or []) + else: + result["cups_codigos"] = merge_unique( + base.get("cups_codigos"), extra.get("cups_codigos") + ) + result["cups_descripciones"] = merge_unique( + base.get("cups_descripciones"), extra.get("cups_descripciones") + ) + result["cie10_codigos"] = merge_unique( + base.get("cie10_codigos"), extra.get("cie10_codigos") + ) + result["cie10_descripciones"] = merge_unique( + base.get("cie10_descripciones"), extra.get("cie10_descripciones") + ) + + if not result.get("cup_codigo") and extra.get("cup_codigo"): + result["cup_codigo"] = extra.get("cup_codigo") + result["cup_descripcion"] = extra.get("cup_descripcion") + + if not result.get("cie10_codigo") and extra.get("cie10_codigo"): + result["cie10_codigo"] = extra.get("cie10_codigo") + result["cie10_descripcion"] = extra.get("cie10_descripcion") + + if not result.get("ips_nombre") and extra.get("ips_nombre"): + result["ips_nombre"] = extra.get("ips_nombre") + if not result.get("ips_nit") and extra.get("ips_nit"): + result["ips_nit"] = extra.get("ips_nit") + if not result.get("fecha_ingreso_urgencias") and extra.get( + "fecha_ingreso_urgencias" + ): + result["fecha_ingreso_urgencias"] = extra.get("fecha_ingreso_urgencias") + if not result.get("cups_busqueda") and extra.get("cups_busqueda"): + result["cups_busqueda"] = extra.get("cups_busqueda") + if result.get("formato") == "DESCONOCIDO" and extra.get("formato"): + result["formato"] = extra.get("formato") + + if not result.get("cup_codigo") and result.get("cups_codigos"): + result["cup_codigo"] = result["cups_codigos"][0] + if not result.get("cie10_codigo") and result.get("cie10_codigos"): + result["cie10_codigo"] = ", ".join(result["cie10_codigos"]) + if not result.get("cie10_descripcion") and result.get("cie10_descripciones"): + result["cie10_descripcion"] = ", ".join( + [d for d in result["cie10_descripciones"] if d] + ) + + warnings = list(result.get("warnings") or []) + if result.get("cup_codigo") or result.get("cups_codigos"): + warnings = [w for w in warnings if w != "cups_not_found"] + if result.get("cie10_codigo") or result.get("cie10_codigos"): + warnings = [w for w in warnings if w != "cie10_not_found"] + if result.get("ips_nombre") or result.get("ips_nit"): + warnings = [w for w in warnings if w != "ips_not_found"] + result["warnings"] = warnings + + return result + + def build_response(text, ocr_used, ocr_available, ocr_error): lines = [line.strip() for line in (text or "").split("\n") if line.strip()] norm_lines = [normalize(line) for line in lines] @@ -394,18 +582,26 @@ 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_list = extract_cups_list(lines, norm_lines) + cup_codigo, cup_desc = (cups_list[0] if cups_list else (None, None)) + cups_codigos = [item[0] for item in cups_list] + cups_descripciones = [item[1] for item in cups_list] cups_busqueda = extract_cups_hint(lines, norm_lines) - cie_codigo, cie_desc = extract_cie10(lines, norm_lines) + cie_list = extract_cie10_list(lines, norm_lines) + cie_codigos = [item[0] for item in cie_list] + cie_descs = [item[1] for item in cie_list] + cie_codigo = ", ".join(cie_codigos) if cie_codigos else None + cie_desc = ", ".join([d for d in cie_descs if d]) if any(cie_descs) else None ips_nombre, ips_nit = extract_ips(lines, norm_lines) + fecha_ingreso = extract_fecha_ingreso_urgencias(lines, norm_lines) formato = detect_format(norm_text, norm_lines) warnings = [] if not text: warnings.append("no_text_extracted") - if not cup_codigo: + if not cup_codigo and not cups_codigos: warnings.append("cups_not_found") - if not cie_codigo: + if not cie_codigo and not cie_codigos: warnings.append("cie10_not_found") if not ips_nombre and not ips_nit: warnings.append("ips_not_found") @@ -421,11 +617,16 @@ def build_response(text, ocr_used, ocr_available, ocr_error): "numero_documento": documento, "cup_codigo": cup_codigo, "cup_descripcion": cup_desc, + "cups_codigos": cups_codigos, + "cups_descripciones": cups_descripciones, "cups_busqueda": cups_busqueda, "cie10_codigo": cie_codigo, "cie10_descripcion": cie_desc, + "cie10_codigos": cie_codigos, + "cie10_descripciones": cie_descs, "ips_nombre": ips_nombre, "ips_nit": ips_nit, + "fecha_ingreso_urgencias": fecha_ingreso, "warnings": warnings, } @@ -441,18 +642,29 @@ def main(): if not text: text = extract_text_fitz(path) - normalized_len = len(normalize(text)) ocr_used = False ocr_error = "" ocr_available = is_tesseract_available() - if normalized_len < 50 and ocr_available: + response = build_response(text, False, ocr_available, None) + needs_ocr = ocr_available and ( + not response.get("cup_codigo") or not response.get("cie10_codigo") + ) + + if needs_ocr: ocr_text, ocr_error = extract_text_ocr(path) if ocr_text: - text = ocr_text ocr_used = True + ocr_response = build_response(ocr_text, True, ocr_available, ocr_error) + response = merge_response(response, ocr_response) + if ocr_error: + response["ocr_error"] = ocr_error + + response["ocr_usado"] = bool(ocr_used) + response["ocr_disponible"] = bool(ocr_available) + if "ocr_error" not in response: + response["ocr_error"] = ocr_error or None - response = build_response(text, ocr_used, ocr_available, ocr_error) print(json.dumps(response, ensure_ascii=True)) diff --git a/backend/src/schema.sql b/backend/src/schema.sql index 9bdfa6d..51ffc85 100644 --- a/backend/src/schema.sql +++ b/backend/src/schema.sql @@ -184,7 +184,7 @@ CREATE TABLE IF NOT EXISTS autorizacion ( fecha_autorizacion date NOT NULL DEFAULT current_date, observacion text, cup_codigo varchar(20), - cie10_codigo varchar(20), + cie10_codigo text, cie10_descripcion text, tipo_autorizacion varchar(50) NOT NULL DEFAULT 'consultas_externas', tipo_servicio varchar(50), @@ -331,7 +331,7 @@ CREATE TABLE IF NOT EXISTS autorizacion_version ( fecha_autorizacion DATE, observacion TEXT, cup_codigo VARCHAR(20), - cie10_codigo VARCHAR(20), + cie10_codigo TEXT, cie10_descripcion TEXT, tipo_autorizacion VARCHAR(50), tipo_servicio VARCHAR(50), diff --git a/backend/src/server.js b/backend/src/server.js index c0745d1..5207074 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -385,6 +385,11 @@ const ensureAutorizacionExtras = async () => { ADD COLUMN IF NOT EXISTS cie10_codigo VARCHAR(20); `); + await pool.query(` + ALTER TABLE autorizacion + ALTER COLUMN cie10_codigo TYPE TEXT; + `); + await pool.query(` ALTER TABLE autorizacion ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT; @@ -474,6 +479,11 @@ const ensureAutorizacionExtras = async () => { ADD COLUMN IF NOT EXISTS cie10_codigo VARCHAR(20); `); + await pool.query(` + ALTER TABLE autorizacion_version + ALTER COLUMN cie10_codigo TYPE TEXT; + `); + await pool.query(` ALTER TABLE autorizacion_version ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT; @@ -1641,14 +1651,35 @@ async function procesarExcelIps(inputFilePath) { const codigoKeys = new Set(); const nombreKeys = new Set(); + const registerDigits = (set, value) => { + const digits = normalizeDigits(value); + if (!digits) return ''; + set.add(digits); + if (digits.length >= 10) { + set.add(digits.slice(0, -1)); + } + return digits; + }; + const client = await pool.connect(); try { await client.query('BEGIN'); for (let i = 2; i <= sheet.rowCount; i++) { const row = sheet.getRow(i); - const nit = getValue(row, 'NIT'); - const nombre = getValue(row, 'PRESTADOR'); + const nit = getValueMulti(row, [ + 'NIT', + 'NITIPS', + 'NITPRESTADOR', + 'NITPRESTADORIPS', + ]); + const nombre = getValueMulti(row, [ + 'PRESTADOR', + 'PRESTADORDELSERVICIO', + 'PRESTADORDESERVICIO', + 'NOMBREPRESTADOR', + 'NOMBREIPS', + ]); if (!nit && !nombre) { continue; @@ -1660,28 +1691,66 @@ async function procesarExcelIps(inputFilePath) { const departamento = getValue(row, 'DEPARTAMENTO'); const municipio = getValue(row, 'MUNICIPIO'); const telefono = getValue(row, 'TELEFONO'); - const codigoIps = getValue(row, 'CODIGOIPS'); + const codigoIps = getValueMulti(row, [ + 'CODIGOIPS', + 'CODIGOPRESTADOR', + 'CODIGOHABILITACION', + ]); + + const nitDigits = registerDigits(nitKeys, nit); + const codigoDigits = registerDigits(codigoKeys, codigoIps); + const lookupKeys = []; + if (nitDigits) { + lookupKeys.push(nitDigits); + if (nitDigits.length >= 10) lookupKeys.push(nitDigits.slice(0, -1)); + } + if (codigoDigits) { + lookupKeys.push(codigoDigits); + if (codigoDigits.length >= 10) lookupKeys.push(codigoDigits.slice(0, -1)); + } - const nitDigits = normalizeDigits(nit); - const codigoDigits = normalizeDigits(codigoIps); - const lookupKey = nitDigits || codigoDigits; const nombreKey = normalizeNameKey(nombre); - - if (nitDigits) nitKeys.add(nitDigits); - if (codigoDigits) codigoKeys.add(codigoDigits); if (nombreKey) nombreKeys.add(nombreKey); let existente = null; - if (lookupKey) { + if (lookupKeys.length > 0) { const res = await client.query( ` SELECT id_ips FROM ips - WHERE regexp_replace(nit, '\\D', '', 'g') = $1 - OR regexp_replace(codigo_ips, '\\D', '', 'g') = $1 + WHERE regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[]) + OR regexp_replace(codigo_ips, '\\D', '', 'g') = ANY($1::text[]) LIMIT 1 `, - [lookupKey] + [lookupKeys] + ); + existente = res.rows[0] || null; + } + + if (!existente && nitDigits && nitDigits.length >= 8) { + const res = await client.query( + ` + SELECT id_ips + FROM ips + WHERE regexp_replace(nit, '\\D', '', 'g') LIKE $1 + AND length(regexp_replace(nit, '\\D', '', 'g')) = $2 + LIMIT 1 + `, + [`${nitDigits}%`, nitDigits.length + 1] + ); + existente = res.rows[0] || null; + } + + if (!existente && codigoDigits && codigoDigits.length >= 8) { + const res = await client.query( + ` + SELECT id_ips + FROM ips + WHERE regexp_replace(codigo_ips, '\\D', '', 'g') LIKE $1 + AND length(regexp_replace(codigo_ips, '\\D', '', 'g')) = $2 + LIMIT 1 + `, + [`${codigoDigits}%`, codigoDigits.length + 1] ); existente = res.rows[0] || null; } @@ -1753,6 +1822,26 @@ async function procesarExcelIps(inputFilePath) { const nitList = Array.from(nitKeys); const codigoList = Array.from(codigoKeys); const nombreList = Array.from(nombreKeys); + const nitPrefixList = nitList.filter((nit) => nit.length >= 8 && nit.length <= 9); + const codigoPrefixList = codigoList.filter( + (codigo) => codigo.length >= 8 && codigo.length <= 9 + ); + + if (nombreList.length > 0) { + await client.query( + ` + UPDATE ips + SET tiene_convenio = true + WHERE regexp_replace( + translate(UPPER(nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'), + '[^A-Z0-9]', + '', + 'g' + ) = ANY($1::text[]) + `, + [nombreList] + ); + } const desactRes = await client.query( ` @@ -1761,6 +1850,24 @@ async function procesarExcelIps(inputFilePath) { WHERE NOT ( (regexp_replace(nit, '\\D', '', 'g') <> '' AND regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[])) OR (regexp_replace(codigo_ips, '\\D', '', 'g') <> '' AND regexp_replace(codigo_ips, '\\D', '', 'g') = ANY($2::text[])) + OR ( + regexp_replace(nit, '\\D', '', 'g') <> '' + AND EXISTS ( + SELECT 1 + FROM unnest($3::text[]) AS k + WHERE regexp_replace(nit, '\\D', '', 'g') LIKE k || '%' + AND length(regexp_replace(nit, '\\D', '', 'g')) = length(k) + 1 + ) + ) + OR ( + regexp_replace(codigo_ips, '\\D', '', 'g') <> '' + AND EXISTS ( + SELECT 1 + FROM unnest($4::text[]) AS k + WHERE regexp_replace(codigo_ips, '\\D', '', 'g') LIKE k || '%' + AND length(regexp_replace(codigo_ips, '\\D', '', 'g')) = length(k) + 1 + ) + ) OR ( regexp_replace( translate(UPPER(nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'), @@ -1773,12 +1880,12 @@ async function procesarExcelIps(inputFilePath) { '[^A-Z0-9]', '', 'g' - ) = ANY($3::text[]) + ) = ANY($5::text[]) ) ) AND tiene_convenio IS DISTINCT FROM false `, - [nitList, codigoList, nombreList] + [nitList, codigoList, nitPrefixList, codigoPrefixList, nombreList] ); resumen.desactivados = desactRes.rowCount || 0; @@ -2143,7 +2250,12 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) { continue; } - const servicioExcel = getValue(row, 'SERVICIO'); + const servicioExcel = getValueMulti(row, [ + 'SERVICIO', + 'TIPOSERVICIO', + 'TIPODESERVICIO', + 'SERVICIOAUTORIZACION', + ]); const servicioInfo = parseServicio(servicioExcel); const ambitoRaw = getValueMulti(row, [ @@ -2248,35 +2360,63 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) { } } - const ipsKey = normalizeDigits(nitIps) || normalizeSearch(hospital); + const nitKeyRaw = normalizeDigits(nitIps); + const nitKey = nitKeyRaw.length >= 10 ? nitKeyRaw.slice(0, -1) : nitKeyRaw; + const hospitalKey = normalizeNameKey(hospital); + const ipsKey = nitKey || hospitalKey; let ipsInfo = ipsCache.get(ipsKey); if (!ipsInfo) { const nitDigits = normalizeDigits(nitIps); - let ipsRes = null; + const lookupKeys = []; if (nitDigits) { + lookupKeys.push(nitDigits); + if (nitDigits.length >= 10) lookupKeys.push(nitDigits.slice(0, -1)); + } + let ipsRes = null; + if (lookupKeys.length > 0) { ipsRes = await client.query( ` SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio FROM ips - WHERE regexp_replace(nit, '\\D', '', 'g') = $1 - OR regexp_replace(codigo_ips, '\\D', '', 'g') = $1 + WHERE regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[]) + OR regexp_replace(codigo_ips, '\\D', '', 'g') = ANY($1::text[]) LIMIT 1 `, - [nitDigits] + [lookupKeys] ); } if (!ipsRes || ipsRes.rows.length === 0) { - if (hospital) { + if (nitDigits && nitDigits.length >= 8) { ipsRes = await client.query( ` SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio FROM ips - WHERE UPPER(nombre_ips) = $1 + WHERE regexp_replace(nit, '\\D', '', 'g') LIKE $1 + AND length(regexp_replace(nit, '\\D', '', 'g')) = $2 LIMIT 1 `, - [normalizeSearch(hospital)] + [`${nitDigits}%`, nitDigits.length + 1] + ); + } + } + + if (!ipsRes || ipsRes.rows.length === 0) { + if (hospitalKey) { + ipsRes = await client.query( + ` + SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio + FROM ips + WHERE regexp_replace( + translate(UPPER(nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'), + '[^A-Z0-9]', + '', + 'g' + ) = $1 + LIMIT 1 + `, + [hospitalKey] ); } } @@ -2363,7 +2503,7 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) { continue; } - const dupRes = await client.query( + let dupRes = await client.query( ` SELECT 1 FROM autorizacion @@ -2383,6 +2523,34 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) { ] ); + if (dupRes.rows.length === 0 && hospitalKey) { + dupRes = await client.query( + ` + SELECT 1 + FROM autorizacion a + JOIN ips i ON a.id_ips = i.id_ips + WHERE a.interno = $1 + AND a.cup_codigo = $2 + AND a.tipo_autorizacion = $3 + AND COALESCE(a.tipo_servicio, '') = $4 + AND regexp_replace( + translate(UPPER(i.nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'), + '[^A-Z0-9]', + '', + 'g' + ) = $5 + LIMIT 1 + `, + [ + interno, + cupCodigo, + servicioInfo.tipo_autorizacion, + servicioInfo.tipo_servicio || '', + hospitalKey, + ] + ); + } + if (dupRes.rows.length > 0) { resumen.omitidas += 1; resumen.duplicados += 1; @@ -4709,7 +4877,7 @@ app.get('/api/autorizaciones-por-fecha', verificarToken, async (req, res) => { try { const esAdmin = req.usuario?.nombre_rol === 'administrador'; - const limit = Math.min(Number(req.query.limit) || 500, 2000); + const limit = Math.min(Number(req.query.limit) || 100000, 100000); const offset = Math.max(Number(req.query.offset) || 0, 0); const ambitosPermitidos = ['intramural', 'extramural']; if (ambito && !ambitosPermitidos.includes(ambito)) { diff --git a/ips.xlsx b/ips.xlsx new file mode 100644 index 0000000..6431cea Binary files /dev/null and b/ips.xlsx differ diff --git a/saludut-inpec/src/app/components/autorizaciones-por-fecha/autorizaciones-por-fecha.ts b/saludut-inpec/src/app/components/autorizaciones-por-fecha/autorizaciones-por-fecha.ts index 144ce67..ee76b3b 100644 --- a/saludut-inpec/src/app/components/autorizaciones-por-fecha/autorizaciones-por-fecha.ts +++ b/saludut-inpec/src/app/components/autorizaciones-por-fecha/autorizaciones-por-fecha.ts @@ -20,7 +20,7 @@ export class AutorizacionesPorFechaComponent implements OnInit { isLoading = false; errorMessage: string | null = null; successMessage: string | null = null; - limiteAutorizaciones = 500; + limiteAutorizaciones = 100000; avisoAutorizaciones = false; descargandoZip = false; descargandoPdf = false; diff --git a/saludut-inpec/src/app/components/autorizaciones/autorizaciones.ts b/saludut-inpec/src/app/components/autorizaciones/autorizaciones.ts index 8c6421d..cbc1041 100644 --- a/saludut-inpec/src/app/components/autorizaciones/autorizaciones.ts +++ b/saludut-inpec/src/app/components/autorizaciones/autorizaciones.ts @@ -429,6 +429,34 @@ export class AutorizacionesComponent { } } + const observacionExtras: string[] = []; + const cupsCodigos = Array.isArray(resp?.cups_codigos) + ? resp.cups_codigos + : []; + const cupsDescripciones = Array.isArray(resp?.cups_descripciones) + ? resp.cups_descripciones + : []; + if (cupsCodigos.length > 1) { + const extras = cupsCodigos.slice(1).map((codigo: string, idx: number) => { + const desc = cupsDescripciones[idx + 1]; + return desc ? `${codigo} ${desc}` : codigo; + }); + if (extras.length) { + observacionExtras.push(`CUPS adicionales: ${extras.join(', ')}`); + } + } + if (resp?.fecha_ingreso_urgencias) { + observacionExtras.push(`Ingreso a urgencias: ${resp.fecha_ingreso_urgencias}`); + } + if (observacionExtras.length) { + const actual = String(this.formAutorizacion.observacion || '').trim(); + const nuevos = observacionExtras.filter( + (parte) => !actual.toLowerCase().includes(parte.toLowerCase()) + ); + this.formAutorizacion.observacion = + [actual, ...nuevos].filter(Boolean).join(' | '); + } + const infoParts = []; if (resp?.nombre_paciente) { infoParts.push(`Paciente: ${resp.nombre_paciente}`); diff --git a/saludut-inpec/src/app/services/auth.ts b/saludut-inpec/src/app/services/auth.ts index db7b3a5..9172b27 100644 --- a/saludut-inpec/src/app/services/auth.ts +++ b/saludut-inpec/src/app/services/auth.ts @@ -312,7 +312,7 @@ export class AuthService { getAutorizacionesPorFecha( fechaInicio: string, fechaFin: string, - limit = 500, + limit = 100000, offset = 0, establecimiento?: string, ambito?: string, diff --git a/yopal rx.xlsx b/yopal rx.xlsx new file mode 100644 index 0000000..1646877 Binary files /dev/null and b/yopal rx.xlsx differ