mas cambios

This commit is contained in:
Jhonathan Guevara 2026-01-05 12:04:41 -05:00
parent a4cf0f84c3
commit b8c258a963
Signed by: jhonathan_guevara
GPG Key ID: 619239F12DCBE55B
11 changed files with 478 additions and 70 deletions

Binary file not shown.

Binary file not shown.

View File

@ -27,6 +27,27 @@ def extract_digits_from_text(text, min_len=6, max_len=20):
return None 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): def extract_text_pdfplumber(path):
try: try:
import pdfplumber # type: ignore import pdfplumber # type: ignore
@ -214,7 +235,9 @@ def extract_document(lines, norm_lines):
def extract_cups_code(text): def extract_cups_code(text):
if not text: if not text:
return None 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: if match:
return match.group(0) return match.group(0)
digits = extract_digits_from_text(text, min_len=4, max_len=10) digits = extract_digits_from_text(text, min_len=4, max_len=10)
@ -223,25 +246,84 @@ def extract_cups_code(text):
return None return None
def extract_cups(lines, norm_lines): 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): for i, line in enumerate(norm_lines):
if ( if (
"CUPS" in line "CUPS" in line
or re.search(r"C\s*[\.\-]?\s*U\s*[\.\-]?\s*P\s*[\.\-]?\s*S", line, re.IGNORECASE) 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) or re.search(r"\bCUP\b", line, re.IGNORECASE)
): ):
for j in range(i, min(i + 8, len(lines))): for j in range(i, min(i + 8, len(lines))):
code = extract_cups_code(lines[j]) code = extract_cups_code(lines[j])
if not code: if not code:
continue continue
desc = ""
raw = lines[j] raw = lines[j]
desc = ""
if code in raw: if code in raw:
desc = raw.split(code, 1)[-1].strip(" -:") desc = raw.split(code, 1)[-1].strip(" -:")
if not desc and j + 1 < len(lines): if not desc and j + 1 < len(lines):
desc = lines[j + 1].strip() desc = lines[j + 1].strip()
return code, desc or None 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 return None, None
@ -305,26 +387,62 @@ def extract_cups_hint(lines, norm_lines):
return None 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): for i, nline in enumerate(norm_lines):
if ( if (
"DIAGNOSTICO PRINCIPAL" in nline "DIAGNOSTICO" in nline
or "IMPRESION DIAGNOSTICA" in nline or "IMPRESION DIAGNOSTICA" in nline
or "DIAGNOSTICO" in nline or "CIE10" in nline
): ):
candidate = lines[i] for j in range(i, min(i + 6, len(lines))):
if i + 1 < len(lines) and len(lines[i + 1].split()) > 1: code, desc = parse_cie10_from_line(lines[j])
candidate = candidate + " " + lines[i + 1] if not code:
code, desc = parse_cie10_from_line(candidate) continue
if code: if code in seen:
return code, desc or None continue
for line in lines: seen.add(code)
code, desc = parse_cie10_from_line(line) results.append((code, desc))
if code: return results
return code, desc or None
def extract_cie10(lines, norm_lines):
cie_list = extract_cie10_list(lines, norm_lines)
if cie_list:
return cie_list[0]
return None, None 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): def clean_ips_name(value):
if not value: if not value:
return "" return ""
@ -387,6 +505,76 @@ def detect_format(norm_text, norm_lines):
return "DESCONOCIDO" 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): def build_response(text, ocr_used, ocr_available, ocr_error):
lines = [line.strip() for line in (text or "").split("\n") if line.strip()] lines = [line.strip() for line in (text or "").split("\n") if line.strip()]
norm_lines = [normalize(line) for line in lines] 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) nombre = extract_name(lines, norm_lines)
documento = extract_document(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) 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) 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) formato = detect_format(norm_text, norm_lines)
warnings = [] warnings = []
if not text: if not text:
warnings.append("no_text_extracted") warnings.append("no_text_extracted")
if not cup_codigo: if not cup_codigo and not cups_codigos:
warnings.append("cups_not_found") warnings.append("cups_not_found")
if not cie_codigo: if not cie_codigo and not cie_codigos:
warnings.append("cie10_not_found") warnings.append("cie10_not_found")
if not ips_nombre and not ips_nit: if not ips_nombre and not ips_nit:
warnings.append("ips_not_found") warnings.append("ips_not_found")
@ -421,11 +617,16 @@ def build_response(text, ocr_used, ocr_available, ocr_error):
"numero_documento": documento, "numero_documento": documento,
"cup_codigo": cup_codigo, "cup_codigo": cup_codigo,
"cup_descripcion": cup_desc, "cup_descripcion": cup_desc,
"cups_codigos": cups_codigos,
"cups_descripciones": cups_descripciones,
"cups_busqueda": cups_busqueda, "cups_busqueda": cups_busqueda,
"cie10_codigo": cie_codigo, "cie10_codigo": cie_codigo,
"cie10_descripcion": cie_desc, "cie10_descripcion": cie_desc,
"cie10_codigos": cie_codigos,
"cie10_descripciones": cie_descs,
"ips_nombre": ips_nombre, "ips_nombre": ips_nombre,
"ips_nit": ips_nit, "ips_nit": ips_nit,
"fecha_ingreso_urgencias": fecha_ingreso,
"warnings": warnings, "warnings": warnings,
} }
@ -441,18 +642,29 @@ def main():
if not text: if not text:
text = extract_text_fitz(path) text = extract_text_fitz(path)
normalized_len = len(normalize(text))
ocr_used = False ocr_used = False
ocr_error = "" ocr_error = ""
ocr_available = is_tesseract_available() 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) ocr_text, ocr_error = extract_text_ocr(path)
if ocr_text: if ocr_text:
text = ocr_text
ocr_used = True 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)) print(json.dumps(response, ensure_ascii=True))

View File

@ -184,7 +184,7 @@ CREATE TABLE IF NOT EXISTS autorizacion (
fecha_autorizacion date NOT NULL DEFAULT current_date, fecha_autorizacion date NOT NULL DEFAULT current_date,
observacion text, observacion text,
cup_codigo varchar(20), cup_codigo varchar(20),
cie10_codigo varchar(20), cie10_codigo text,
cie10_descripcion text, cie10_descripcion text,
tipo_autorizacion varchar(50) NOT NULL DEFAULT 'consultas_externas', tipo_autorizacion varchar(50) NOT NULL DEFAULT 'consultas_externas',
tipo_servicio varchar(50), tipo_servicio varchar(50),
@ -331,7 +331,7 @@ CREATE TABLE IF NOT EXISTS autorizacion_version (
fecha_autorizacion DATE, fecha_autorizacion DATE,
observacion TEXT, observacion TEXT,
cup_codigo VARCHAR(20), cup_codigo VARCHAR(20),
cie10_codigo VARCHAR(20), cie10_codigo TEXT,
cie10_descripcion TEXT, cie10_descripcion TEXT,
tipo_autorizacion VARCHAR(50), tipo_autorizacion VARCHAR(50),
tipo_servicio VARCHAR(50), tipo_servicio VARCHAR(50),

View File

@ -385,6 +385,11 @@ const ensureAutorizacionExtras = async () => {
ADD COLUMN IF NOT EXISTS cie10_codigo VARCHAR(20); ADD COLUMN IF NOT EXISTS cie10_codigo VARCHAR(20);
`); `);
await pool.query(`
ALTER TABLE autorizacion
ALTER COLUMN cie10_codigo TYPE TEXT;
`);
await pool.query(` await pool.query(`
ALTER TABLE autorizacion ALTER TABLE autorizacion
ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT; ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT;
@ -474,6 +479,11 @@ const ensureAutorizacionExtras = async () => {
ADD COLUMN IF NOT EXISTS cie10_codigo VARCHAR(20); 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(` await pool.query(`
ALTER TABLE autorizacion_version ALTER TABLE autorizacion_version
ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT; ADD COLUMN IF NOT EXISTS cie10_descripcion TEXT;
@ -1641,14 +1651,35 @@ async function procesarExcelIps(inputFilePath) {
const codigoKeys = new Set(); const codigoKeys = new Set();
const nombreKeys = 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(); const client = await pool.connect();
try { try {
await client.query('BEGIN'); await client.query('BEGIN');
for (let i = 2; i <= sheet.rowCount; i++) { for (let i = 2; i <= sheet.rowCount; i++) {
const row = sheet.getRow(i); const row = sheet.getRow(i);
const nit = getValue(row, 'NIT'); const nit = getValueMulti(row, [
const nombre = getValue(row, 'PRESTADOR'); 'NIT',
'NITIPS',
'NITPRESTADOR',
'NITPRESTADORIPS',
]);
const nombre = getValueMulti(row, [
'PRESTADOR',
'PRESTADORDELSERVICIO',
'PRESTADORDESERVICIO',
'NOMBREPRESTADOR',
'NOMBREIPS',
]);
if (!nit && !nombre) { if (!nit && !nombre) {
continue; continue;
@ -1660,28 +1691,66 @@ async function procesarExcelIps(inputFilePath) {
const departamento = getValue(row, 'DEPARTAMENTO'); const departamento = getValue(row, 'DEPARTAMENTO');
const municipio = getValue(row, 'MUNICIPIO'); const municipio = getValue(row, 'MUNICIPIO');
const telefono = getValue(row, 'TELEFONO'); 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); const nombreKey = normalizeNameKey(nombre);
if (nitDigits) nitKeys.add(nitDigits);
if (codigoDigits) codigoKeys.add(codigoDigits);
if (nombreKey) nombreKeys.add(nombreKey); if (nombreKey) nombreKeys.add(nombreKey);
let existente = null; let existente = null;
if (lookupKey) { if (lookupKeys.length > 0) {
const res = await client.query( const res = await client.query(
` `
SELECT id_ips SELECT id_ips
FROM ips FROM ips
WHERE regexp_replace(nit, '\\D', '', 'g') = $1 WHERE regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[])
OR regexp_replace(codigo_ips, '\\D', '', 'g') = $1 OR regexp_replace(codigo_ips, '\\D', '', 'g') = ANY($1::text[])
LIMIT 1 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; existente = res.rows[0] || null;
} }
@ -1753,6 +1822,26 @@ async function procesarExcelIps(inputFilePath) {
const nitList = Array.from(nitKeys); const nitList = Array.from(nitKeys);
const codigoList = Array.from(codigoKeys); const codigoList = Array.from(codigoKeys);
const nombreList = Array.from(nombreKeys); 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( const desactRes = await client.query(
` `
@ -1761,6 +1850,24 @@ async function procesarExcelIps(inputFilePath) {
WHERE NOT ( WHERE NOT (
(regexp_replace(nit, '\\D', '', 'g') <> '' AND regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[])) (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(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 ( OR (
regexp_replace( regexp_replace(
translate(UPPER(nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'), translate(UPPER(nombre_ips), 'ÁÉÍÓÚÜÑ', 'AEIOUUN'),
@ -1773,12 +1880,12 @@ async function procesarExcelIps(inputFilePath) {
'[^A-Z0-9]', '[^A-Z0-9]',
'', '',
'g' 'g'
) = ANY($3::text[]) ) = ANY($5::text[])
) )
) )
AND tiene_convenio IS DISTINCT FROM false AND tiene_convenio IS DISTINCT FROM false
`, `,
[nitList, codigoList, nombreList] [nitList, codigoList, nitPrefixList, codigoPrefixList, nombreList]
); );
resumen.desactivados = desactRes.rowCount || 0; resumen.desactivados = desactRes.rowCount || 0;
@ -2143,7 +2250,12 @@ async function procesarExcelAutorizacionesMasivas(inputFilePath, usuario) {
continue; continue;
} }
const servicioExcel = getValue(row, 'SERVICIO'); const servicioExcel = getValueMulti(row, [
'SERVICIO',
'TIPOSERVICIO',
'TIPODESERVICIO',
'SERVICIOAUTORIZACION',
]);
const servicioInfo = parseServicio(servicioExcel); const servicioInfo = parseServicio(servicioExcel);
const ambitoRaw = getValueMulti(row, [ 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); let ipsInfo = ipsCache.get(ipsKey);
if (!ipsInfo) { if (!ipsInfo) {
const nitDigits = normalizeDigits(nitIps); const nitDigits = normalizeDigits(nitIps);
let ipsRes = null; const lookupKeys = [];
if (nitDigits) { 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( ipsRes = await client.query(
` `
SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio
FROM ips FROM ips
WHERE regexp_replace(nit, '\\D', '', 'g') = $1 WHERE regexp_replace(nit, '\\D', '', 'g') = ANY($1::text[])
OR regexp_replace(codigo_ips, '\\D', '', 'g') = $1 OR regexp_replace(codigo_ips, '\\D', '', 'g') = ANY($1::text[])
LIMIT 1 LIMIT 1
`, `,
[nitDigits] [lookupKeys]
); );
} }
if (!ipsRes || ipsRes.rows.length === 0) { if (!ipsRes || ipsRes.rows.length === 0) {
if (hospital) { if (nitDigits && nitDigits.length >= 8) {
ipsRes = await client.query( ipsRes = await client.query(
` `
SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio SELECT id_ips, nombre_ips, tiene_convenio, departamento, municipio
FROM ips 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 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; continue;
} }
const dupRes = await client.query( let dupRes = await client.query(
` `
SELECT 1 SELECT 1
FROM autorizacion 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) { if (dupRes.rows.length > 0) {
resumen.omitidas += 1; resumen.omitidas += 1;
resumen.duplicados += 1; resumen.duplicados += 1;
@ -4709,7 +4877,7 @@ app.get('/api/autorizaciones-por-fecha', verificarToken, async (req, res) => {
try { try {
const esAdmin = req.usuario?.nombre_rol === 'administrador'; 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 offset = Math.max(Number(req.query.offset) || 0, 0);
const ambitosPermitidos = ['intramural', 'extramural']; const ambitosPermitidos = ['intramural', 'extramural'];
if (ambito && !ambitosPermitidos.includes(ambito)) { if (ambito && !ambitosPermitidos.includes(ambito)) {

BIN
ips.xlsx Normal file

Binary file not shown.

View File

@ -20,7 +20,7 @@ export class AutorizacionesPorFechaComponent implements OnInit {
isLoading = false; isLoading = false;
errorMessage: string | null = null; errorMessage: string | null = null;
successMessage: string | null = null; successMessage: string | null = null;
limiteAutorizaciones = 500; limiteAutorizaciones = 100000;
avisoAutorizaciones = false; avisoAutorizaciones = false;
descargandoZip = false; descargandoZip = false;
descargandoPdf = false; descargandoPdf = false;

View File

@ -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 = []; const infoParts = [];
if (resp?.nombre_paciente) { if (resp?.nombre_paciente) {
infoParts.push(`Paciente: ${resp.nombre_paciente}`); infoParts.push(`Paciente: ${resp.nombre_paciente}`);

View File

@ -312,7 +312,7 @@ export class AuthService {
getAutorizacionesPorFecha( getAutorizacionesPorFecha(
fechaInicio: string, fechaInicio: string,
fechaFin: string, fechaFin: string,
limit = 500, limit = 100000,
offset = 0, offset = 0,
establecimiento?: string, establecimiento?: string,
ambito?: string, ambito?: string,

BIN
yopal rx.xlsx Normal file

Binary file not shown.