V2 final
This commit is contained in:
parent
2d29bd88a1
commit
0287cd6931
42
README.md
42
README.md
@ -299,3 +299,45 @@ pip3 install --break-system-packages pdfplumber pypdf reportlab pillow
|
||||
# verificacion
|
||||
python3 -c "import pandas, openpyxl, dotenv; print('OK', pandas.__version__, openpyxl.__version__, dotenv.__version__)"
|
||||
```
|
||||
|
||||
#### 7.7) OCR (PDF real + escaneado) con Tesseract (Alpine)
|
||||
Si necesitas leer PDFs escaneados para el autorrelleno, usa este flujo.
|
||||
```bash
|
||||
# Uso:
|
||||
# sh -c '...este bloque...' archivo.pdf
|
||||
# Si no pasas argumento, usa "archivo.pdf"
|
||||
|
||||
# 1) Asegura repo community (donde esta ocrmypdf/tesseract-data)
|
||||
ALPINE_VER="$(cut -d. -f1,2 /etc/alpine-release)"
|
||||
sed -i 's/^#\(.*\/community\)/\1/' /etc/apk/repositories
|
||||
grep -q "/v${ALPINE_VER}/community" /etc/apk/repositories || \
|
||||
echo "https://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VER}/community" >> /etc/apk/repositories
|
||||
|
||||
# 2) Instala herramientas: pdftotext (poppler), ocrmypdf, tesseract + idiomas
|
||||
apk update
|
||||
apk add --no-cache \
|
||||
poppler-utils \
|
||||
ocrmypdf \
|
||||
tesseract-ocr-data-spa \
|
||||
tesseract-ocr-data-eng
|
||||
|
||||
# 3) Procesa el PDF (real/escaneado/mixto)
|
||||
IN="${1:-archivo.pdf}"
|
||||
BASE="${IN%.pdf}"
|
||||
TXT="${BASE}.txt"
|
||||
OCRPDF="${BASE}.ocr.pdf"
|
||||
|
||||
# Intenta extraer texto directo
|
||||
pdftotext -layout "$IN" - > "$TXT" 2>/dev/null || true
|
||||
|
||||
# Si el texto es muy corto, probablemente es escaneado (o casi todo imagen) -> OCR
|
||||
LEN="$(tr -d '[:space:]' < "$TXT" | wc -c | tr -d ' ')"
|
||||
if [ "${LEN:-0}" -lt 80 ]; then
|
||||
ocrmypdf -l spa+eng --skip-text --rotate-pages --deskew --optimize 3 "$IN" "$OCRPDF"
|
||||
pdftotext -layout "$OCRPDF" "$TXT"
|
||||
echo "OK (OCR aplicado cuando hacia falta): $OCRPDF | Texto: $TXT"
|
||||
else
|
||||
echo "OK (PDF con texto): Texto: $TXT"
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
BIN
backend/src/ANEXO 1 LUIS CARLOS VARGAS MAYORGA CC 1022361409.pdf
Normal file
BIN
backend/src/ANEXO 1 LUIS CARLOS VARGAS MAYORGA CC 1022361409.pdf
Normal file
Binary file not shown.
BIN
backend/src/ANEXO 3 SOL MARIA BAUTISTA DIAZ.pdf
Normal file
BIN
backend/src/ANEXO 3 SOL MARIA BAUTISTA DIAZ.pdf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/src/CC 1122730848 - LIBER IBAÑEZ BOLAÑOS.pdf
Normal file
BIN
backend/src/CC 1122730848 - LIBER IBAÑEZ BOLAÑOS.pdf
Normal file
Binary file not shown.
BIN
backend/src/CC 79607978 -- CESAR AUGUSTO GUEVARA CAMARGO.pdf
Normal file
BIN
backend/src/CC 79607978 -- CESAR AUGUSTO GUEVARA CAMARGO.pdf
Normal file
Binary file not shown.
BIN
backend/src/CONTRERAS JIMENEZ ANDRES MAURICIO.pdf
Normal file
BIN
backend/src/CONTRERAS JIMENEZ ANDRES MAURICIO.pdf
Normal file
Binary file not shown.
BIN
backend/src/GUEVARA CAMARGO CESAR AUGUSTO.pdf
Normal file
BIN
backend/src/GUEVARA CAMARGO CESAR AUGUSTO.pdf
Normal file
Binary file not shown.
BIN
backend/src/HC CC 79607978 -- CESAR AUGUSTO GUEVARA CAMARGO.pdf
Normal file
BIN
backend/src/HC CC 79607978 -- CESAR AUGUSTO GUEVARA CAMARGO.pdf
Normal file
Binary file not shown.
BIN
backend/src/IBAÑEZ BOLAÑOS LIBER NO TIENE.pdf
Normal file
BIN
backend/src/IBAÑEZ BOLAÑOS LIBER NO TIENE.pdf
Normal file
Binary file not shown.
BIN
backend/src/autorizacion_masivo.xlsx
Normal file
BIN
backend/src/autorizacion_masivo.xlsx
Normal file
Binary file not shown.
292
backend/src/extraer_autorizacion_pdf.py
Normal file
292
backend/src/extraer_autorizacion_pdf.py
Normal file
@ -0,0 +1,292 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
|
||||
def normalize(text):
|
||||
text = text or ""
|
||||
text = unicodedata.normalize("NFD", text)
|
||||
text = "".join(ch for ch in text if not unicodedata.combining(ch))
|
||||
text = re.sub(r"\s+", " ", text).strip()
|
||||
return text.upper()
|
||||
|
||||
|
||||
def extract_text_pdfplumber(path):
|
||||
try:
|
||||
import pdfplumber # type: ignore
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
parts = []
|
||||
with pdfplumber.open(path) as pdf:
|
||||
for page in pdf.pages:
|
||||
parts.append(page.extract_text() or "")
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def extract_text_fitz(path):
|
||||
try:
|
||||
import fitz # type: ignore
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
parts = []
|
||||
doc = fitz.open(path)
|
||||
try:
|
||||
for page in doc:
|
||||
parts.append(page.get_text() or "")
|
||||
finally:
|
||||
doc.close()
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def configure_tesseract():
|
||||
try:
|
||||
import pytesseract # type: ignore
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
candidates = []
|
||||
env_path = os.environ.get("TESSERACT_PATH")
|
||||
if env_path:
|
||||
candidates.append(env_path)
|
||||
|
||||
candidates.extend(
|
||||
[
|
||||
r"C:\Program Files\Tesseract-OCR\tesseract.exe",
|
||||
r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",
|
||||
]
|
||||
)
|
||||
|
||||
for path in candidates:
|
||||
if path and os.path.isfile(path):
|
||||
pytesseract.pytesseract.tesseract_cmd = path
|
||||
return path
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_tesseract_available():
|
||||
try:
|
||||
import pytesseract # type: ignore
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
configure_tesseract()
|
||||
try:
|
||||
_ = pytesseract.get_tesseract_version()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def extract_text_ocr(path, max_pages=2):
|
||||
try:
|
||||
import fitz # type: ignore
|
||||
import pytesseract # type: ignore
|
||||
from PIL import Image # type: ignore
|
||||
except Exception:
|
||||
return "", "ocr_modules_missing"
|
||||
|
||||
configure_tesseract()
|
||||
try:
|
||||
_ = pytesseract.get_tesseract_version()
|
||||
except Exception:
|
||||
return "", "tesseract_not_found"
|
||||
|
||||
text_parts = []
|
||||
ocr_error = ""
|
||||
doc = fitz.open(path)
|
||||
try:
|
||||
total_pages = min(max_pages, doc.page_count)
|
||||
for i in range(total_pages):
|
||||
page = doc.load_page(i)
|
||||
pix = page.get_pixmap(dpi=200)
|
||||
img = Image.frombytes("RGB", (pix.width, pix.height), pix.samples)
|
||||
try:
|
||||
text = pytesseract.image_to_string(img, lang="spa") or ""
|
||||
except Exception:
|
||||
try:
|
||||
text = pytesseract.image_to_string(img, lang="eng") or ""
|
||||
if not ocr_error:
|
||||
ocr_error = "tesseract_lang_missing_spa"
|
||||
except Exception:
|
||||
return "", "tesseract_lang_missing"
|
||||
text_parts.append(text)
|
||||
finally:
|
||||
doc.close()
|
||||
|
||||
return "\n".join(text_parts), ocr_error
|
||||
|
||||
|
||||
def extract_name(lines, norm_lines):
|
||||
keys = ["DATOS DEL USUARIO", "DATOS DEL PACIENTE"]
|
||||
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
|
||||
|
||||
skip_tokens = [
|
||||
"1ER APELLIDO",
|
||||
"2DO APELLIDO",
|
||||
"1ER NOMBRE",
|
||||
"2DO NOMBRE",
|
||||
"TIPO DOCUMENTO",
|
||||
"DOCUMENTO DE IDENTIFICACION",
|
||||
"REGISTRO CIVIL",
|
||||
"TARJETA",
|
||||
"CEDULA",
|
||||
"NUIP",
|
||||
]
|
||||
|
||||
for j in range(idx + 1, min(idx + 6, len(lines))):
|
||||
nline = norm_lines[j]
|
||||
if any(token in nline for token in skip_tokens):
|
||||
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:
|
||||
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
|
||||
break
|
||||
|
||||
if idx == -1:
|
||||
return None, None
|
||||
|
||||
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
|
||||
|
||||
|
||||
def detect_format(norm_text, norm_lines):
|
||||
if "ANEXO TECNICO" in norm_text or "SOLICITUD DE AUTORIZACION" in norm_text:
|
||||
return "ANEXO_TECNICO"
|
||||
for line in norm_lines:
|
||||
if "ATENCION INICIAL DE URGENCIAS" in line:
|
||||
return "ANEXO_URGENCIAS"
|
||||
return "DESCONOCIDO"
|
||||
|
||||
|
||||
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]
|
||||
norm_text = normalize(text)
|
||||
|
||||
nombre = extract_name(lines, norm_lines)
|
||||
documento = extract_document(lines, norm_lines)
|
||||
cup_codigo, cup_desc = extract_cups(lines, norm_lines)
|
||||
cie_codigo, cie_desc = extract_cie10(lines, norm_lines)
|
||||
formato = detect_format(norm_text, norm_lines)
|
||||
|
||||
warnings = []
|
||||
if not text:
|
||||
warnings.append("no_text_extracted")
|
||||
if not cup_codigo:
|
||||
warnings.append("cups_not_found")
|
||||
if not cie_codigo:
|
||||
warnings.append("cie10_not_found")
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"text_length": len(norm_text),
|
||||
"ocr_usado": ocr_used,
|
||||
"ocr_disponible": ocr_available,
|
||||
"ocr_error": ocr_error or None,
|
||||
"formato": formato,
|
||||
"nombre_paciente": nombre,
|
||||
"numero_documento": documento,
|
||||
"cup_codigo": cup_codigo,
|
||||
"cup_descripcion": cup_desc,
|
||||
"cie10_codigo": cie_codigo,
|
||||
"cie10_descripcion": cie_desc,
|
||||
"warnings": warnings,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(json.dumps({"ok": False, "error": "missing_file"}, ensure_ascii=True))
|
||||
return
|
||||
|
||||
path = sys.argv[1]
|
||||
|
||||
text = extract_text_pdfplumber(path)
|
||||
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:
|
||||
ocr_text, ocr_error = extract_text_ocr(path)
|
||||
if ocr_text:
|
||||
text = ocr_text
|
||||
ocr_used = True
|
||||
|
||||
response = build_response(text, ocr_used, ocr_available, ocr_error)
|
||||
print(json.dumps(response, ensure_ascii=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1436,27 +1436,27 @@ async function crearLibroAutorizacion(a) {
|
||||
sheet.getCell('L21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('L21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('L21').alignment = {"horizontal":"center","vertical":"middle"};
|
||||
sheet.getCell('M21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('M21').value = "CODIGO CIE-10";
|
||||
sheet.getCell('M21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('M21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('M21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('N21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('N21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('N21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('N21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('N21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('O21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('O21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('O21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('O21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('O21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('P21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('P21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('P21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('P21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('P21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('Q21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('Q21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('Q21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('Q21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('Q21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('R21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('R21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('R21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('R21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"double"},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('R21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
@ -1510,27 +1510,27 @@ async function crearLibroAutorizacion(a) {
|
||||
sheet.getCell('L22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('L22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('L22').alignment = {"horizontal":"center","vertical":"middle"};
|
||||
sheet.getCell('M22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('M22').value = "CODIGO CIE-10";
|
||||
sheet.getCell('M22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('M22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('M22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('N22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('N22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('N22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('N22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('N22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('O22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('O22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('O22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('O22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('O22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('P22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('P22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('P22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('P22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('P22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('Q22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('Q22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('Q22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('Q22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('Q22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('R22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('R22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('R22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('R22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"double"},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('R22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
@ -2697,9 +2697,8 @@ async function crearLibroAutorizacion(a) {
|
||||
sheet.getCell(celda).value = nitIps;
|
||||
});
|
||||
|
||||
['M21', 'N21', 'R21'].forEach(celda => {
|
||||
sheet.getCell(celda).value = a.interno || '';
|
||||
});
|
||||
sheet.getCell('M21').value = a.cie10_codigo || '';
|
||||
sheet.getCell('N21').value = a.cie10_descripcion || '';
|
||||
|
||||
['N20', 'R20'].forEach(celda => {
|
||||
sheet.getCell(celda).value = a.sexo || '';
|
||||
@ -2715,12 +2714,12 @@ async function crearLibroAutorizacion(a) {
|
||||
});
|
||||
|
||||
['M16', 'N16'].forEach(celda => {
|
||||
sheet.getCell(celda).value = a.departamento || '';
|
||||
sheet.getCell(celda).value = a.municipio || '';
|
||||
});
|
||||
|
||||
// 6) Municipio IPS (Q16:R16)
|
||||
// 6) Departamento IPS (Q16:R16)
|
||||
['Q16', 'R16'].forEach(celda => {
|
||||
sheet.getCell(celda).value = a.municipio || '';
|
||||
sheet.getCell(celda).value = a.departamento || '';
|
||||
});
|
||||
|
||||
// 7) Nombre completo paciente (H18:R18)
|
||||
@ -2755,6 +2754,12 @@ async function crearLibroAutorizacion(a) {
|
||||
if (nivelTexto) cupInfoParts.push(nivelTexto);
|
||||
const cupInfo = cupInfoParts.join(' - ');
|
||||
const observacionBase = 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 = '';
|
||||
@ -2763,7 +2768,7 @@ async function crearLibroAutorizacion(a) {
|
||||
} else if (tipoAutorizacion === 'consultas_externas') {
|
||||
tipoServicioTexto = 'Tipo servicio: Consulta externa';
|
||||
}
|
||||
const observacion = [cupInfo, tipoServicioTexto, observacionBase]
|
||||
const observacion = [cupInfo, tipoServicioTexto, observacionBase, solicitanteInfo]
|
||||
.filter(Boolean)
|
||||
.join(' | ');
|
||||
['H31','R31'].forEach(celda => {
|
||||
|
||||
@ -1279,27 +1279,27 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
sheet.getCell('L21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('L21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('L21').alignment = {"horizontal":"center","vertical":"middle"};
|
||||
sheet.getCell('M21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('M21').value = "CODIGO CIE-10";
|
||||
sheet.getCell('M21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('M21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('M21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('N21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('N21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('N21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('N21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('N21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('O21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('O21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('O21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('O21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('O21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('P21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('P21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('P21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('P21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('P21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('Q21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('Q21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('Q21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('Q21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('Q21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('R21').value = "CODIGO INTERNO";
|
||||
sheet.getCell('R21').value = "DIAGNOSTICO";
|
||||
sheet.getCell('R21').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('R21').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"double"},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('R21').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
@ -1347,27 +1347,27 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
sheet.getCell('L22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('L22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('L22').alignment = {"horizontal":"center","vertical":"middle"};
|
||||
sheet.getCell('M22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('M22').value = "CODIGO CIE-10";
|
||||
sheet.getCell('M22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('M22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('M22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('N22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('N22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('N22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('N22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('N22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('O22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('O22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('O22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('O22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('O22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('P22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('P22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('P22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('P22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('P22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('Q22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('Q22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('Q22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('Q22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"medium","color":{"argb":"FF000000"}},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('Q22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
sheet.getCell('R22').value = "CODIGO INTERNO";
|
||||
sheet.getCell('R22').value = "DIAGNOSTICO";
|
||||
sheet.getCell('R22').fill = {"type":"pattern","pattern":"none"};
|
||||
sheet.getCell('R22').border = {"left":{"style":"medium","color":{"argb":"FF000000"}},"right":{"style":"double"},"top":{"style":"medium","color":{"argb":"FF000000"}},"bottom":{"style":"medium","color":{"argb":"FF000000"}}};
|
||||
sheet.getCell('R22').alignment = {"horizontal":"center","vertical":"middle","wrapText":true};
|
||||
@ -2373,9 +2373,8 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
sheet.getCell(celda).value = nitIps;
|
||||
});
|
||||
|
||||
['M21', 'N21', 'R21'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = a.interno || '';
|
||||
});
|
||||
sheet.getCell('M21').value = a.cie10_codigo || '';
|
||||
sheet.getCell('N21').value = a.cie10_descripcion || '';
|
||||
|
||||
['N20', 'R20'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = a.sexo || '';
|
||||
@ -2390,11 +2389,11 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
});
|
||||
|
||||
['M16', 'N16'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = a.departamento || '';
|
||||
sheet.getCell(celda).value = a.municipio || '';
|
||||
});
|
||||
|
||||
['Q16', 'R16'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = a.municipio || '';
|
||||
sheet.getCell(celda).value = a.departamento || '';
|
||||
});
|
||||
|
||||
['H18', 'I18', 'J18', 'K18', 'L18', 'M18', 'N18', 'O18', 'P18', 'Q18', 'R18'].forEach((celda) => {
|
||||
@ -2425,7 +2424,13 @@ async function crearLibroAutorizacionBrigadasAmbulanciasHospitalarios(a) {
|
||||
if (nivelTexto) cupInfoParts.push(nivelTexto);
|
||||
const cupInfo = cupInfoParts.join(' - ');
|
||||
const observacionBase = a.observacion || '';
|
||||
const observacion = [cupInfo, observacionBase].filter(Boolean).join(' | ');
|
||||
const solicitanteNombre = String(a.nombre_solicitante || '').trim();
|
||||
const observacionLower = observacionBase.toLowerCase();
|
||||
const solicitanteInfo =
|
||||
solicitanteNombre && !observacionLower.includes('solicitante')
|
||||
? 'Solicitante: ' + solicitanteNombre
|
||||
: '';
|
||||
const observacion = [cupInfo, observacionBase, solicitanteInfo].filter(Boolean).join(' | ');
|
||||
['H31', 'R31'].forEach((celda) => {
|
||||
sheet.getCell(celda).value = observacion;
|
||||
});
|
||||
|
||||
@ -180,13 +180,26 @@ CREATE TABLE IF NOT EXISTS autorizacion (
|
||||
interno text NOT NULL REFERENCES paciente(interno),
|
||||
id_ips integer NOT NULL REFERENCES ips(id_ips),
|
||||
numero_documento_autorizante bigint NOT NULL REFERENCES autorizante(numero_documento),
|
||||
id_usuario_solicitante integer REFERENCES usuario(id_usuario),
|
||||
fecha_autorizacion date NOT NULL DEFAULT current_date,
|
||||
observacion text,
|
||||
cup_codigo varchar(20),
|
||||
cie10_codigo varchar(20),
|
||||
cie10_descripcion text,
|
||||
tipo_autorizacion varchar(50) NOT NULL DEFAULT 'consultas_externas',
|
||||
tipo_servicio varchar(50),
|
||||
ambito_atencion varchar(20),
|
||||
numero_orden varchar(50),
|
||||
archivo_historial_clinico text,
|
||||
archivo_historial_clinico_nombre text,
|
||||
archivo_anexo text,
|
||||
archivo_anexo_nombre text,
|
||||
estado_entrega varchar(20) NOT NULL DEFAULT 'pendiente_entrega',
|
||||
estado_autorizacion varchar(20) NOT NULL DEFAULT 'pendiente',
|
||||
version integer NOT NULL DEFAULT 1
|
||||
version integer NOT NULL DEFAULT 1,
|
||||
correo_inpec_pendiente boolean NOT NULL DEFAULT false,
|
||||
correo_inpec_enviado boolean NOT NULL DEFAULT false,
|
||||
correo_inpec_respuesta boolean NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
DO $$
|
||||
@ -202,6 +215,32 @@ BEGIN
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'autorizacion_estado_entrega_chk'
|
||||
) THEN
|
||||
ALTER TABLE autorizacion
|
||||
ADD CONSTRAINT autorizacion_estado_entrega_chk
|
||||
CHECK (estado_entrega IN ('pendiente_entrega', 'entregado', 'programado', 'atendido', 'cancelado'));
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'autorizacion_ambito_atencion_chk'
|
||||
) THEN
|
||||
ALTER TABLE autorizacion
|
||||
ADD CONSTRAINT autorizacion_ambito_atencion_chk
|
||||
CHECK (ambito_atencion IN ('intramural', 'extramural'));
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
INSERT INTO consecutivo_autorizacion (id, codigo)
|
||||
VALUES (1, COALESCE((SELECT MAX(numero_autorizacion) FROM autorizacion), 'UTUSCPGB00'))
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
@ -229,11 +268,17 @@ CREATE TABLE IF NOT EXISTS autorizacion_version (
|
||||
version INTEGER NOT NULL,
|
||||
id_ips INTEGER NOT NULL,
|
||||
numero_documento_autorizante BIGINT NOT NULL,
|
||||
id_usuario_solicitante INTEGER,
|
||||
fecha_autorizacion DATE,
|
||||
observacion TEXT,
|
||||
cup_codigo VARCHAR(20),
|
||||
cie10_codigo VARCHAR(20),
|
||||
cie10_descripcion TEXT,
|
||||
tipo_autorizacion VARCHAR(50),
|
||||
tipo_servicio VARCHAR(50),
|
||||
ambito_atencion VARCHAR(20),
|
||||
numero_orden VARCHAR(50),
|
||||
estado_entrega VARCHAR(20),
|
||||
fecha_version TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -87,7 +87,8 @@
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid var(--color-input-border);
|
||||
@ -98,7 +99,8 @@
|
||||
color: var(--color-text-main);
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
.form-group input:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #1976d2;
|
||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
|
||||
@ -284,6 +286,12 @@
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
.numero-orden {
|
||||
font-weight: 600;
|
||||
font-family: "Courier New", monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cup-codigo {
|
||||
font-weight: 600;
|
||||
font-family: "Courier New", monospace;
|
||||
@ -321,8 +329,10 @@
|
||||
.nombre-paciente,
|
||||
.ips,
|
||||
.autorizante,
|
||||
.solicitante,
|
||||
.establecimiento,
|
||||
.estado {
|
||||
.estado,
|
||||
.estado-entrega {
|
||||
max-width: 180px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
@ -340,7 +350,8 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.estado select {
|
||||
.estado select,
|
||||
.estado-entrega select {
|
||||
width: 100%;
|
||||
min-width: 140px;
|
||||
padding: 10px 12px;
|
||||
@ -500,8 +511,10 @@
|
||||
.nombre-paciente,
|
||||
.ips,
|
||||
.autorizante,
|
||||
.solicitante,
|
||||
.establecimiento,
|
||||
.estado {
|
||||
.estado,
|
||||
.estado-entrega {
|
||||
max-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +74,37 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="establecimiento">Centro penitenciario:</label>
|
||||
<input
|
||||
id="establecimiento"
|
||||
type="text"
|
||||
formControlName="establecimiento"
|
||||
placeholder="Codigo o nombre"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ips">IPS:</label>
|
||||
<input
|
||||
id="ips"
|
||||
type="text"
|
||||
formControlName="ips"
|
||||
placeholder="Nombre o NIT"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ambito">Ambito:</label>
|
||||
<select id="ambito" formControlName="ambito">
|
||||
<option value="">Todos</option>
|
||||
<option value="intramural">Intramural</option>
|
||||
<option value="extramural">Extramural</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -97,8 +128,8 @@
|
||||
placeholder="Numero de autorizacion"
|
||||
/>
|
||||
</div>
|
||||
<div class="resultados-actions" *ngIf="esAdmin">
|
||||
<div class="estado-masivo">
|
||||
<div class="resultados-actions" *ngIf="esAdmin || puedeDescargarMasivo">
|
||||
<div class="estado-masivo" *ngIf="esAdmin">
|
||||
<label for="estadoMasivo">Estado masivo:</label>
|
||||
<select
|
||||
id="estadoMasivo"
|
||||
@ -120,6 +151,7 @@
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-secondary btn-exportar"
|
||||
*ngIf="puedeDescargarMasivo"
|
||||
(click)="exportarAExcel()"
|
||||
title="Exportar a Excel"
|
||||
[disabled]="descargandoZip || descargandoPdf"
|
||||
@ -128,6 +160,7 @@
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary btn-descargar-todos"
|
||||
*ngIf="puedeDescargarMasivo"
|
||||
(click)="descargarTodosLosPdfs()"
|
||||
title="Descargar todos los PDFs"
|
||||
[disabled]="descargandoZip"
|
||||
@ -149,6 +182,8 @@
|
||||
<th>Version</th>
|
||||
<th>Tipo autorizacion</th>
|
||||
<th>Tipo servicio</th>
|
||||
<th>Ambito</th>
|
||||
<th>Numero orden</th>
|
||||
<th>CUPS</th>
|
||||
<th>Cubre</th>
|
||||
<th>Nivel</th>
|
||||
@ -156,14 +191,16 @@
|
||||
<th>Municipio</th>
|
||||
<th>Departamento</th>
|
||||
<th>Autorizante</th>
|
||||
<th>Solicitante</th>
|
||||
<th>Establecimiento</th>
|
||||
<th>Estado</th>
|
||||
<th>Estado entrega</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngIf="autorizacionesFiltradas.length === 0">
|
||||
<td class="no-result" colspan="17">
|
||||
<td class="no-result" colspan="21">
|
||||
No hay autorizaciones para ese numero.
|
||||
</td>
|
||||
</tr>
|
||||
@ -184,6 +221,10 @@
|
||||
<td class="tipo-servicio">
|
||||
{{ getTipoServicioLabel(aut.tipo_servicio) }}
|
||||
</td>
|
||||
<td class="ambito">{{ getAmbitoLabel(aut.ambito_atencion) }}</td>
|
||||
<td class="numero-orden">
|
||||
{{ aut.ambito_atencion === 'intramural' ? (aut.numero_orden || '-') : '-' }}
|
||||
</td>
|
||||
<td class="cup-codigo">{{ aut.cup_codigo }}</td>
|
||||
<td class="cup-cobertura">
|
||||
<span
|
||||
@ -209,6 +250,7 @@
|
||||
<td class="municipio">{{ aut.municipio || '-' }}</td>
|
||||
<td class="departamento">{{ aut.departamento || '-' }}</td>
|
||||
<td class="autorizante">{{ aut.nombre_autorizante }}</td>
|
||||
<td class="solicitante">{{ aut.nombre_solicitante || '-' }}</td>
|
||||
<td class="establecimiento">{{ aut.nombre_establecimiento }}</td>
|
||||
<td class="estado">
|
||||
<ng-container *ngIf="esAdmin; else estadoTexto">
|
||||
@ -234,6 +276,32 @@
|
||||
</span>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td class="estado-entrega">
|
||||
<ng-container *ngIf="puedeGestionarEntrega; else estadoEntregaTexto">
|
||||
<select
|
||||
[ngModel]="aut.estado_entrega || 'pendiente_entrega'"
|
||||
(ngModelChange)="actualizarEstadoEntrega(aut, $event)"
|
||||
[disabled]="actualizandoEntrega[aut.numero_autorizacion]"
|
||||
>
|
||||
<option value="pendiente_entrega">Pendiente por entrega</option>
|
||||
<option value="entregado">Entregado</option>
|
||||
<option value="programado">Programado</option>
|
||||
<option value="atendido">Atendido</option>
|
||||
<option value="cancelado">Cancelado</option>
|
||||
</select>
|
||||
<span
|
||||
class="mini-status"
|
||||
*ngIf="actualizandoEntrega[aut.numero_autorizacion]"
|
||||
>
|
||||
Guardando...
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-template #estadoEntregaTexto>
|
||||
<span class="estado-label">
|
||||
{{ getEstadoEntregaLabel(aut.estado_entrega) }}
|
||||
</span>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td class="acciones">
|
||||
<button
|
||||
class="btn btn-success btn-sm btn-descargar"
|
||||
|
||||
@ -28,8 +28,14 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
actualizandoMasivo = false;
|
||||
estadoMasivo: 'pendiente' | 'autorizado' | 'no_autorizado' = 'autorizado';
|
||||
esAdmin = false;
|
||||
puedeGestionarEntrega = false;
|
||||
puedeDescargarMasivo = false;
|
||||
filtroNumero = '';
|
||||
establecimientoFiltro = '';
|
||||
ambitoFiltro = '';
|
||||
ipsFiltro = '';
|
||||
autorizacionesFiltradas: any[] = [];
|
||||
actualizandoEntrega: Record<string, boolean> = {};
|
||||
|
||||
// Para saber si ya buscamos algo
|
||||
hayResultados = false;
|
||||
@ -48,12 +54,18 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
) {
|
||||
this.filtroForm = this.fb.group({
|
||||
fecha_inicio: ['', Validators.required],
|
||||
fecha_fin: ['', Validators.required]
|
||||
fecha_fin: ['', Validators.required],
|
||||
establecimiento: [''],
|
||||
ambito: [''],
|
||||
ips: [''],
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.esAdmin = this.authService.isAdministrador();
|
||||
this.puedeGestionarEntrega =
|
||||
this.esAdmin || this.authService.isAdministrativoSede();
|
||||
this.puedeDescargarMasivo = this.authService.puedeDescargarPdfs();
|
||||
|
||||
// Rango por defecto: últimos 30 días
|
||||
const hoy = new Date();
|
||||
@ -85,6 +97,9 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
// Guardamos las fechas ya formateadas para reutilizarlas
|
||||
this.fechaInicioApi = this.formatDateForInput(inicio);
|
||||
this.fechaFinApi = this.formatDateForInput(fin);
|
||||
this.establecimientoFiltro = String(this.filtroForm.get('establecimiento')?.value || '').trim();
|
||||
this.ambitoFiltro = String(this.filtroForm.get('ambito')?.value || '').trim().toLowerCase();
|
||||
this.ipsFiltro = String(this.filtroForm.get('ips')?.value || '').trim();
|
||||
|
||||
this.isLoading = true;
|
||||
this.autorizaciones = [];
|
||||
@ -96,7 +111,10 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
this.fechaInicioApi!,
|
||||
this.fechaFinApi!,
|
||||
this.limiteAutorizaciones,
|
||||
0
|
||||
0,
|
||||
this.establecimientoFiltro || undefined,
|
||||
this.ambitoFiltro || undefined,
|
||||
this.ipsFiltro || undefined
|
||||
)
|
||||
.pipe(finalize(() => {
|
||||
this.isLoading = false;
|
||||
@ -284,6 +302,45 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
actualizarEstadoEntrega(autorizacion: any, estado: string): void {
|
||||
if (!this.puedeGestionarEntrega || !autorizacion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numero = String(autorizacion.numero_autorizacion || '');
|
||||
if (!numero) return;
|
||||
|
||||
if (this.actualizandoEntrega[numero]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const estadoPrevio = String(autorizacion.estado_entrega || 'pendiente_entrega');
|
||||
autorizacion.estado_entrega = estado;
|
||||
this.actualizandoEntrega[numero] = true;
|
||||
|
||||
this.pacienteService
|
||||
.actualizarEstadoEntrega(numero, estado)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.actualizandoEntrega[numero] = false;
|
||||
this.cdr.detectChanges();
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
next: (resp) => {
|
||||
const data = resp?.autorizacion;
|
||||
if (data) {
|
||||
autorizacion.estado_entrega = data.estado_entrega;
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
autorizacion.estado_entrega = estadoPrevio;
|
||||
this.errorMessage = err?.error?.error || 'Error actualizando estado de entrega.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ========= USUARIO =========
|
||||
logout(): void {
|
||||
this.authService.logout();
|
||||
@ -309,8 +366,8 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
descargarTodosLosPdfs(): void {
|
||||
this.limpiarMensajes();
|
||||
|
||||
if (!this.esAdmin) {
|
||||
this.errorMessage = 'No tienes permisos para descargar todos los PDFs.';
|
||||
if (!this.puedeDescargarMasivo) {
|
||||
this.errorMessage = 'No tienes permisos para descargar los PDFs.';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,7 +384,13 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
this.descargandoZip = true;
|
||||
|
||||
this.authService
|
||||
.crearJobZipAutorizaciones(this.fechaInicioApi, this.fechaFinApi)
|
||||
.crearJobZipAutorizaciones(
|
||||
this.fechaInicioApi,
|
||||
this.fechaFinApi,
|
||||
this.establecimientoFiltro || undefined,
|
||||
this.ambitoFiltro || undefined,
|
||||
this.ipsFiltro || undefined
|
||||
)
|
||||
.subscribe({
|
||||
next: (job) => {
|
||||
this.jobsService.pollJob(job.id).subscribe({
|
||||
@ -439,6 +502,17 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
return tipo ? String(tipo) : '';
|
||||
}
|
||||
|
||||
getAmbitoLabel(ambito: string | null | undefined): string {
|
||||
const normalizado = String(ambito || '').toLowerCase();
|
||||
if (normalizado === 'intramural') {
|
||||
return 'Intramural';
|
||||
}
|
||||
if (normalizado === 'extramural') {
|
||||
return 'Extramural';
|
||||
}
|
||||
return ambito ? String(ambito) : '';
|
||||
}
|
||||
|
||||
getEstadoAutorizacionLabel(estado: string | null | undefined): string {
|
||||
const normalizado = String(estado || 'pendiente').toLowerCase();
|
||||
if (normalizado === 'autorizado') {
|
||||
@ -450,6 +524,23 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
return 'Pendiente';
|
||||
}
|
||||
|
||||
getEstadoEntregaLabel(estado: string | null | undefined): string {
|
||||
const normalizado = String(estado || 'pendiente_entrega').toLowerCase();
|
||||
if (normalizado === 'entregado') {
|
||||
return 'Entregado';
|
||||
}
|
||||
if (normalizado === 'programado') {
|
||||
return 'Programado';
|
||||
}
|
||||
if (normalizado === 'atendido') {
|
||||
return 'Atendido';
|
||||
}
|
||||
if (normalizado === 'cancelado') {
|
||||
return 'Cancelado';
|
||||
}
|
||||
return 'Pendiente por entrega';
|
||||
}
|
||||
|
||||
getCoberturaLabel(autorizacion: any): string {
|
||||
if (autorizacion?.cup_cubierto === true) {
|
||||
return 'Cubre';
|
||||
@ -528,7 +619,7 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
|
||||
// ========= EXPORTAR A CSV SIMPLE =========
|
||||
exportarAExcel(): void {
|
||||
if (!this.esAdmin) {
|
||||
if (!this.puedeDescargarMasivo) {
|
||||
this.errorMessage = 'No tienes permisos para exportar.';
|
||||
return;
|
||||
}
|
||||
@ -549,13 +640,19 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
'Tipo autorizacion',
|
||||
'Tipo servicio',
|
||||
'CUPS',
|
||||
'CIE-10',
|
||||
'Diagnostico',
|
||||
'Ambito',
|
||||
'Numero orden',
|
||||
'Nivel',
|
||||
'IPS',
|
||||
'Municipio',
|
||||
'Departamento',
|
||||
'Autorizante',
|
||||
'Solicitante',
|
||||
'Establecimiento',
|
||||
'Estado'
|
||||
'Estado',
|
||||
'Estado entrega'
|
||||
];
|
||||
|
||||
const separator = ';';
|
||||
@ -571,13 +668,19 @@ export class AutorizacionesPorFechaComponent implements OnInit {
|
||||
this.csvValue(this.getTipoAutorizacionLabel(aut.tipo_autorizacion)),
|
||||
this.csvValue(this.getTipoServicioLabel(aut.tipo_servicio)),
|
||||
this.csvValue(aut.cup_codigo || ''),
|
||||
this.csvValue(aut.cie10_codigo || ''),
|
||||
this.csvValue(aut.cie10_descripcion || ''),
|
||||
this.csvValue(this.getAmbitoLabel(aut.ambito_atencion)),
|
||||
this.csvValue(aut.ambito_atencion === 'intramural' ? (aut.numero_orden || '') : ''),
|
||||
this.csvValue(aut.cup_nivel || ''),
|
||||
this.csvValue(aut.nombre_ips),
|
||||
this.csvValue(aut.municipio),
|
||||
this.csvValue(aut.departamento),
|
||||
this.csvValue(aut.nombre_autorizante),
|
||||
this.csvValue(aut.nombre_solicitante || ''),
|
||||
this.csvValue(aut.nombre_establecimiento),
|
||||
this.csvValue(this.getEstadoAutorizacionLabel(aut.estado_autorizacion))
|
||||
this.csvValue(this.getEstadoAutorizacionLabel(aut.estado_autorizacion)),
|
||||
this.csvValue(this.getEstadoEntregaLabel(aut.estado_entrega))
|
||||
].join(separator)
|
||||
)
|
||||
].join('\n');
|
||||
|
||||
@ -30,6 +30,30 @@
|
||||
color: var(--color-text-main);
|
||||
}
|
||||
|
||||
.file-field {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.file-field input[type='file'] {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.file-actions {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.form-row input:focus,
|
||||
.form-row select:focus,
|
||||
.form-row textarea:focus {
|
||||
|
||||
@ -164,7 +164,11 @@
|
||||
"
|
||||
>
|
||||
<label for="tipoServicio">Tipo de servicio:</label>
|
||||
<select id="tipoServicio" [(ngModel)]="formAutorizacion.tipo_servicio">
|
||||
<select
|
||||
id="tipoServicio"
|
||||
[(ngModel)]="formAutorizacion.tipo_servicio"
|
||||
(change)="onTipoServicioChange()"
|
||||
>
|
||||
<option value="">-- Seleccione --</option>
|
||||
<option value="brigadas">Brigadas</option>
|
||||
<option value="ambulancias">Ambulancias</option>
|
||||
@ -172,6 +176,90 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
>
|
||||
<label for="historialClinico">Historial clinico (HC):</label>
|
||||
<div class="file-field">
|
||||
<input
|
||||
id="historialClinico"
|
||||
type="file"
|
||||
accept=".pdf,application/pdf"
|
||||
(change)="onHistorialClinicoChange($event)"
|
||||
/>
|
||||
<span class="file-name" *ngIf="archivoHistorialClinico">
|
||||
{{ archivoHistorialClinico.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
>
|
||||
<label for="anexoHospitalario">Anexo:</label>
|
||||
<div class="file-field">
|
||||
<input
|
||||
id="anexoHospitalario"
|
||||
type="file"
|
||||
accept=".pdf,application/pdf"
|
||||
(change)="onAnexoChange($event)"
|
||||
/>
|
||||
<span class="file-name" *ngIf="archivoAnexo">
|
||||
{{ archivoAnexo.name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-row"
|
||||
*ngIf="formAutorizacion.tipo_servicio === 'hospitalarios'"
|
||||
>
|
||||
<label></label>
|
||||
<div class="file-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-sm"
|
||||
(click)="autorrellenarDesdePdf()"
|
||||
[disabled]="autorrellenandoPdf || (!archivoAnexo && !archivoHistorialClinico)"
|
||||
>
|
||||
Autorrellenar desde PDF
|
||||
</button>
|
||||
<span class="mini-status" *ngIf="autorrellenandoPdf">Leyendo PDF...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status ok" *ngIf="autorrellenoInfo">
|
||||
{{ autorrellenoInfo }}
|
||||
</div>
|
||||
<div class="status error" *ngIf="autorrellenoError">
|
||||
{{ autorrellenoError }}
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="ambitoAtencion">Ambito:</label>
|
||||
<select
|
||||
id="ambitoAtencion"
|
||||
[(ngModel)]="formAutorizacion.ambito_atencion"
|
||||
(change)="onAmbitoChange()"
|
||||
>
|
||||
<option value="">-- Seleccione --</option>
|
||||
<option value="intramural">Intramural</option>
|
||||
<option value="extramural">Extramural</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row" *ngIf="formAutorizacion.ambito_atencion === 'intramural'">
|
||||
<label for="numeroOrden">Numero de orden:</label>
|
||||
<input
|
||||
id="numeroOrden"
|
||||
type="text"
|
||||
[(ngModel)]="formAutorizacion.numero_orden"
|
||||
placeholder="Ej: 12345"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="ips">IPS / Hospital:</label>
|
||||
<div class="ips-field">
|
||||
@ -285,6 +373,25 @@
|
||||
{{ errorCups }}
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="cie10Codigo">Codigo CIE-10:</label>
|
||||
<input
|
||||
id="cie10Codigo"
|
||||
type="text"
|
||||
[(ngModel)]="formAutorizacion.cie10_codigo"
|
||||
placeholder="Ej: A00"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="cie10Descripcion">Diagnostico:</label>
|
||||
<textarea
|
||||
id="cie10Descripcion"
|
||||
rows="2"
|
||||
[(ngModel)]="formAutorizacion.cie10_descripcion"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="fechaAut">Fecha autorización:</label>
|
||||
<input
|
||||
@ -335,6 +442,10 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="status" *ngIf="subiendoArchivosHospitalarios">
|
||||
Subiendo archivos hospitalarios...
|
||||
</div>
|
||||
|
||||
<div class="status ok" *ngIf="mensajeAutorizacion">
|
||||
{{ mensajeAutorizacion }}
|
||||
</div>
|
||||
@ -356,6 +467,7 @@
|
||||
<th>Nivel</th>
|
||||
<th>Tipo autorizacion</th>
|
||||
<th>Tipo servicio</th>
|
||||
<th>Ambito</th>
|
||||
<th>Version</th>
|
||||
<th>IPS</th>
|
||||
<th>Autoriza</th>
|
||||
@ -371,6 +483,7 @@
|
||||
<td>{{ a.cup_nivel }}</td>
|
||||
<td>{{ getTipoAutorizacionLabel(a.tipo_autorizacion) }}</td>
|
||||
<td>{{ getTipoServicioLabel(a.tipo_servicio) }}</td>
|
||||
<td>{{ getAmbitoLabel(a.ambito_atencion) }}</td>
|
||||
<td class="version-cell">
|
||||
<div class="version-stack">
|
||||
<span>v{{ a.version || 1 }}</span>
|
||||
|
||||
@ -4,7 +4,8 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AuthService } from '../../services/auth';
|
||||
import { PacienteService, AutorizacionVersion, CupInfo } from '../../services/paciente';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { finalize, switchMap, catchError, map } from 'rxjs/operators';
|
||||
import { of } from 'rxjs';
|
||||
import { AppHeaderComponent } from '../shared/app-header/app-header';
|
||||
import { JobsService } from '../../services/jobs';
|
||||
|
||||
@ -51,8 +52,12 @@ export class AutorizacionesComponent {
|
||||
fecha_autorizacion: '',
|
||||
observacion: '',
|
||||
cup_codigo: '',
|
||||
cie10_codigo: '',
|
||||
cie10_descripcion: '',
|
||||
tipo_autorizacion: 'consultas_externas',
|
||||
tipo_servicio: '',
|
||||
ambito_atencion: '',
|
||||
numero_orden: '',
|
||||
};
|
||||
|
||||
guardandoAutorizacion = false;
|
||||
@ -64,6 +69,12 @@ export class AutorizacionesComponent {
|
||||
errorAutLista: string | null = null;
|
||||
|
||||
descargandoPdf = false;
|
||||
archivoHistorialClinico: File | null = null;
|
||||
archivoAnexo: File | null = null;
|
||||
subiendoArchivosHospitalarios = false;
|
||||
autorrellenandoPdf = false;
|
||||
autorrellenoInfo: string | null = null;
|
||||
autorrellenoError: string | null = null;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
@ -137,8 +148,12 @@ export class AutorizacionesComponent {
|
||||
fecha_autorizacion: '',
|
||||
observacion: '',
|
||||
cup_codigo: '',
|
||||
cie10_codigo: '',
|
||||
cie10_descripcion: '',
|
||||
tipo_autorizacion: 'consultas_externas',
|
||||
tipo_servicio: '',
|
||||
ambito_atencion: '',
|
||||
numero_orden: '',
|
||||
};
|
||||
|
||||
this.autorizacionesPaciente = [];
|
||||
@ -153,6 +168,7 @@ export class AutorizacionesComponent {
|
||||
|
||||
this.cargarIps(p.interno, this.verMasIps);
|
||||
this.cargarAutorizantes();
|
||||
this.limpiarArchivosHospitalarios();
|
||||
}
|
||||
|
||||
cerrarAutorizacion(): void {
|
||||
@ -172,6 +188,7 @@ export class AutorizacionesComponent {
|
||||
this.versionActualPorAutorizacion = {};
|
||||
this.versionSeleccionada = {};
|
||||
this.cargandoVersiones = {};
|
||||
this.limpiarArchivosHospitalarios();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@ -320,9 +337,174 @@ export class AutorizacionesComponent {
|
||||
'brigadas_ambulancias_hospitalarios'
|
||||
) {
|
||||
this.formAutorizacion.tipo_servicio = '';
|
||||
this.limpiarArchivosHospitalarios();
|
||||
}
|
||||
}
|
||||
|
||||
onTipoServicioChange(): void {
|
||||
const servicio = String(this.formAutorizacion.tipo_servicio || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (servicio !== 'hospitalarios') {
|
||||
this.limpiarArchivosHospitalarios();
|
||||
}
|
||||
}
|
||||
|
||||
onAmbitoChange(): void {
|
||||
const ambito = String(this.formAutorizacion.ambito_atencion || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (ambito !== 'intramural') {
|
||||
this.formAutorizacion.numero_orden = '';
|
||||
}
|
||||
}
|
||||
|
||||
onHistorialClinicoChange(event: Event): void {
|
||||
this.setPdfFile(event, 'historial');
|
||||
}
|
||||
|
||||
onAnexoChange(event: Event): void {
|
||||
this.setPdfFile(event, 'anexo');
|
||||
}
|
||||
|
||||
autorrellenarDesdePdf(): void {
|
||||
const archivo = this.archivoAnexo || this.archivoHistorialClinico;
|
||||
if (!archivo) {
|
||||
this.autorrellenoError = 'Adjunta un PDF para autorrellenar.';
|
||||
return;
|
||||
}
|
||||
|
||||
this.autorrellenoInfo = null;
|
||||
this.autorrellenoError = null;
|
||||
this.autorrellenandoPdf = true;
|
||||
|
||||
this.pacienteService
|
||||
.autorrellenarAutorizacionPdf(archivo)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.autorrellenandoPdf = false;
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
next: (resp: any) => {
|
||||
if (resp?.cup_codigo) {
|
||||
this.formAutorizacion.cup_codigo = resp.cup_codigo;
|
||||
this.buscarCups();
|
||||
}
|
||||
if (resp?.cie10_codigo) {
|
||||
this.formAutorizacion.cie10_codigo = resp.cie10_codigo;
|
||||
}
|
||||
if (resp?.cie10_descripcion) {
|
||||
this.formAutorizacion.cie10_descripcion = resp.cie10_descripcion;
|
||||
}
|
||||
|
||||
const infoParts = [];
|
||||
if (resp?.nombre_paciente) {
|
||||
infoParts.push(`Paciente: ${resp.nombre_paciente}`);
|
||||
}
|
||||
if (resp?.numero_documento) {
|
||||
infoParts.push(`Documento: ${resp.numero_documento}`);
|
||||
}
|
||||
if (resp?.formato) {
|
||||
infoParts.push(`Formato: ${this.getFormatoPdfLabel(resp.formato)}`);
|
||||
}
|
||||
if (resp?.ocr_usado) {
|
||||
infoParts.push('OCR usado');
|
||||
}
|
||||
const warnings = Array.isArray(resp?.warnings) ? resp.warnings : [];
|
||||
if (warnings.length) {
|
||||
infoParts.push(`Avisos: ${warnings.join(', ')}`);
|
||||
}
|
||||
|
||||
this.autorrellenoInfo = infoParts.join(' | ') || 'PDF procesado.';
|
||||
|
||||
const docPdf = String(resp?.numero_documento || '').trim();
|
||||
const docPaciente = String(this.pacienteSeleccionado?.numero_documento || '').trim();
|
||||
if (docPdf && docPaciente && docPdf !== docPaciente) {
|
||||
this.autorrellenoError =
|
||||
'El documento del PDF no coincide con el paciente seleccionado.';
|
||||
}
|
||||
|
||||
if (!this.autorrellenoError && warnings.includes('no_text_extracted')) {
|
||||
if (resp?.ocr_disponible) {
|
||||
this.autorrellenoError =
|
||||
'No se pudo leer texto del PDF. Revisa que el archivo sea legible.';
|
||||
} else {
|
||||
this.autorrellenoError =
|
||||
'No se pudo leer texto del PDF. OCR no disponible en el servidor.';
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
this.autorrellenoError =
|
||||
err?.error?.error || 'Error leyendo datos del PDF.';
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private setPdfFile(event: Event, tipo: 'historial' | 'anexo'): void {
|
||||
const input = event.target as HTMLInputElement | null;
|
||||
const file = input?.files?.[0] || null;
|
||||
|
||||
if (!file) {
|
||||
if (tipo === 'historial') {
|
||||
this.archivoHistorialClinico = null;
|
||||
} else {
|
||||
this.archivoAnexo = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isPdfFile(file)) {
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
this.errorAutorizacion = 'Solo se permiten archivos PDF.';
|
||||
if (tipo === 'historial') {
|
||||
this.archivoHistorialClinico = null;
|
||||
} else {
|
||||
this.archivoAnexo = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tipo === 'historial') {
|
||||
this.archivoHistorialClinico = file;
|
||||
} else {
|
||||
this.archivoAnexo = file;
|
||||
}
|
||||
}
|
||||
|
||||
private isPdfFile(file: File): boolean {
|
||||
const nombre = String(file.name || '').toLowerCase();
|
||||
const tipo = String(file.type || '').toLowerCase();
|
||||
return tipo === 'application/pdf' || nombre.endsWith('.pdf');
|
||||
}
|
||||
|
||||
private getFormatoPdfLabel(formato: string): string {
|
||||
if (formato === 'ANEXO_TECNICO') {
|
||||
return 'Anexo tecnico';
|
||||
}
|
||||
if (formato === 'ANEXO_URGENCIAS') {
|
||||
return 'Anexo urgencias';
|
||||
}
|
||||
return 'Desconocido';
|
||||
}
|
||||
|
||||
private limpiarArchivosHospitalarios(): void {
|
||||
this.archivoHistorialClinico = null;
|
||||
this.archivoAnexo = null;
|
||||
this.resetAutorrelleno();
|
||||
}
|
||||
|
||||
private resetAutorrelleno(): void {
|
||||
this.autorrellenandoPdf = false;
|
||||
this.autorrellenoInfo = null;
|
||||
this.autorrellenoError = null;
|
||||
}
|
||||
|
||||
getTipoAutorizacionLabel(tipo: string | null | undefined): string {
|
||||
const normalizado = String(tipo || '').toLowerCase();
|
||||
if (normalizado === 'consultas_externas') {
|
||||
@ -348,6 +530,17 @@ export class AutorizacionesComponent {
|
||||
return tipo ? String(tipo) : '';
|
||||
}
|
||||
|
||||
getAmbitoLabel(ambito: string | null | undefined): string {
|
||||
const normalizado = String(ambito || '').toLowerCase();
|
||||
if (normalizado === 'intramural') {
|
||||
return 'Intramural';
|
||||
}
|
||||
if (normalizado === 'extramural') {
|
||||
return 'Extramural';
|
||||
}
|
||||
return ambito ? String(ambito) : '';
|
||||
}
|
||||
|
||||
getEstadoAutorizacionLabel(estado: string | null | undefined): string {
|
||||
const normalizado = String(estado || 'pendiente').toLowerCase();
|
||||
if (normalizado === 'autorizado') {
|
||||
@ -454,10 +647,15 @@ export class AutorizacionesComponent {
|
||||
fecha_autorizacion: this.formatDateInput(autorizacion.fecha_autorizacion),
|
||||
observacion: autorizacion.observacion || '',
|
||||
cup_codigo: autorizacion.cup_codigo || '',
|
||||
cie10_codigo: autorizacion.cie10_codigo || '',
|
||||
cie10_descripcion: autorizacion.cie10_descripcion || '',
|
||||
tipo_autorizacion: autorizacion.tipo_autorizacion || 'consultas_externas',
|
||||
tipo_servicio: autorizacion.tipo_servicio || '',
|
||||
ambito_atencion: autorizacion.ambito_atencion || '',
|
||||
numero_orden: autorizacion.numero_orden || '',
|
||||
};
|
||||
this.cupSeleccionado = null;
|
||||
this.limpiarArchivosHospitalarios();
|
||||
|
||||
this.onTipoAutorizacionChange();
|
||||
this.onIpsChange();
|
||||
@ -476,9 +674,14 @@ export class AutorizacionesComponent {
|
||||
fecha_autorizacion: '',
|
||||
observacion: '',
|
||||
cup_codigo: '',
|
||||
cie10_codigo: '',
|
||||
cie10_descripcion: '',
|
||||
tipo_autorizacion: 'consultas_externas',
|
||||
tipo_servicio: '',
|
||||
ambito_atencion: '',
|
||||
numero_orden: '',
|
||||
};
|
||||
this.limpiarArchivosHospitalarios();
|
||||
}
|
||||
|
||||
guardarAutorizacion(): void {
|
||||
@ -501,6 +704,26 @@ export class AutorizacionesComponent {
|
||||
this.errorAutorizacion = 'Debe seleccionar un CUPS.';
|
||||
return;
|
||||
}
|
||||
if (!this.formAutorizacion.cie10_codigo) {
|
||||
this.errorAutorizacion = 'Debe ingresar el codigo CIE-10.';
|
||||
return;
|
||||
}
|
||||
if (!this.formAutorizacion.cie10_descripcion) {
|
||||
this.errorAutorizacion = 'Debe ingresar el diagnostico.';
|
||||
return;
|
||||
}
|
||||
if (!this.formAutorizacion.ambito_atencion) {
|
||||
this.errorAutorizacion = 'Debe seleccionar el ambito (intramural o extramural).';
|
||||
return;
|
||||
}
|
||||
const ambitoSeleccionado = String(this.formAutorizacion.ambito_atencion || '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const numeroOrdenInput = String(this.formAutorizacion.numero_orden || '').trim();
|
||||
if (ambitoSeleccionado === 'intramural' && !numeroOrdenInput) {
|
||||
this.errorAutorizacion = 'Debe ingresar el numero de orden para intramural.';
|
||||
return;
|
||||
}
|
||||
const tipoAutorizacion = String(
|
||||
this.formAutorizacion.tipo_autorizacion || 'consultas_externas'
|
||||
).toLowerCase();
|
||||
@ -515,6 +738,21 @@ export class AutorizacionesComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const esHospitalario =
|
||||
requiereServicio && String(tipoServicio).toLowerCase() === 'hospitalarios';
|
||||
const requiereAdjuntos = esHospitalario && !this.autorizacionEditando;
|
||||
const tieneAdjuntos =
|
||||
!!this.archivoHistorialClinico || !!this.archivoAnexo;
|
||||
|
||||
if (
|
||||
(requiereAdjuntos || tieneAdjuntos) &&
|
||||
(!this.archivoHistorialClinico || !this.archivoAnexo)
|
||||
) {
|
||||
this.errorAutorizacion =
|
||||
'Debe adjuntar historial clinico (HC) y anexo en hospitalarios.';
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
interno: this.pacienteSeleccionado.interno,
|
||||
id_ips: Number(this.formAutorizacion.id_ips),
|
||||
@ -525,8 +763,15 @@ export class AutorizacionesComponent {
|
||||
fecha_autorizacion:
|
||||
this.formAutorizacion.fecha_autorizacion || undefined,
|
||||
cup_codigo: this.formAutorizacion.cup_codigo,
|
||||
cie10_codigo: this.formAutorizacion.cie10_codigo,
|
||||
cie10_descripcion: this.formAutorizacion.cie10_descripcion,
|
||||
tipo_autorizacion: tipoAutorizacion,
|
||||
tipo_servicio: requiereServicio ? tipoServicio : undefined,
|
||||
ambito_atencion: this.formAutorizacion.ambito_atencion,
|
||||
numero_orden:
|
||||
ambitoSeleccionado === 'intramural' && numeroOrdenInput
|
||||
? numeroOrdenInput
|
||||
: undefined,
|
||||
};
|
||||
|
||||
this.guardandoAutorizacion = true;
|
||||
@ -540,13 +785,36 @@ export class AutorizacionesComponent {
|
||||
|
||||
request$
|
||||
.pipe(
|
||||
switchMap((resp: any) => {
|
||||
const numeroAutorizacion = resp?.numero_autorizacion;
|
||||
if (
|
||||
esHospitalario &&
|
||||
this.archivoHistorialClinico &&
|
||||
this.archivoAnexo &&
|
||||
numeroAutorizacion
|
||||
) {
|
||||
this.subiendoArchivosHospitalarios = true;
|
||||
return this.pacienteService
|
||||
.subirArchivosHospitalarios(
|
||||
numeroAutorizacion,
|
||||
this.archivoHistorialClinico,
|
||||
this.archivoAnexo
|
||||
)
|
||||
.pipe(
|
||||
map(() => ({ resp, archivosOk: true, error: null })),
|
||||
catchError((error) => of({ resp, archivosOk: false, error }))
|
||||
);
|
||||
}
|
||||
return of({ resp, archivosOk: true, error: null });
|
||||
}),
|
||||
finalize(() => {
|
||||
this.guardandoAutorizacion = false;
|
||||
this.subiendoArchivosHospitalarios = false;
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
)
|
||||
.subscribe({
|
||||
next: (resp: any) => {
|
||||
next: ({ resp, archivosOk, error }) => {
|
||||
if (this.autorizacionEditando) {
|
||||
const versionTexto = resp?.version ? ` (v${resp.version})` : '';
|
||||
this.mensajeAutorizacion = `Autorizacion ${resp.numero_autorizacion} actualizada${versionTexto}.`;
|
||||
@ -555,6 +823,16 @@ export class AutorizacionesComponent {
|
||||
} else {
|
||||
this.mensajeAutorizacion = `Autorizacion N ${resp.numero_autorizacion} creada el ${resp.fecha_autorizacion}.`;
|
||||
}
|
||||
|
||||
if (esHospitalario && this.archivoHistorialClinico && this.archivoAnexo) {
|
||||
if (archivosOk) {
|
||||
this.limpiarArchivosHospitalarios();
|
||||
} else {
|
||||
this.errorAutorizacion =
|
||||
error?.error?.error ||
|
||||
'Autorizacion guardada, pero fallo la carga de archivos hospitalarios.';
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error(err);
|
||||
|
||||
@ -76,6 +76,14 @@
|
||||
<span class="summary-label">Sin IPS</span>
|
||||
<span class="summary-value">{{ resumen.sin_ips || 0 }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Sin diagnostico</span>
|
||||
<span class="summary-value">{{ resumen.sin_diagnostico || 0 }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Sin ambito</span>
|
||||
<span class="summary-value">{{ resumen.sin_ambito || 0 }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">CUPS no cubiertos</span>
|
||||
<span class="summary-value">{{ resumen.cups_no_cubiertos || 0 }}</span>
|
||||
|
||||
@ -313,10 +313,22 @@ export class AuthService {
|
||||
fechaInicio: string,
|
||||
fechaFin: string,
|
||||
limit = 500,
|
||||
offset = 0
|
||||
offset = 0,
|
||||
establecimiento?: string,
|
||||
ambito?: string,
|
||||
ips?: string
|
||||
): Observable<any[]> {
|
||||
const headers = this.getAuthHeaders();
|
||||
const params = { fecha_inicio: fechaInicio, fecha_fin: fechaFin, limit, offset };
|
||||
const params: any = { fecha_inicio: fechaInicio, fecha_fin: fechaFin, limit, offset };
|
||||
if (establecimiento) {
|
||||
params.establecimiento = establecimiento;
|
||||
}
|
||||
if (ambito) {
|
||||
params.ambito = ambito;
|
||||
}
|
||||
if (ips) {
|
||||
params.ips = ips;
|
||||
}
|
||||
return this.http.get<any[]>(`${this.API_URL}/autorizaciones-por-fecha`, { headers, params });
|
||||
}
|
||||
|
||||
@ -356,11 +368,27 @@ export class AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
crearJobZipAutorizaciones(fechaInicio: string, fechaFin: string): Observable<JobResponse> {
|
||||
crearJobZipAutorizaciones(
|
||||
fechaInicio: string,
|
||||
fechaFin: string,
|
||||
establecimiento?: string,
|
||||
ambito?: string,
|
||||
ips?: string
|
||||
): Observable<JobResponse> {
|
||||
const headers = this.getAuthHeaders();
|
||||
const payload: any = { fecha_inicio: fechaInicio, fecha_fin: fechaFin };
|
||||
if (establecimiento) {
|
||||
payload.establecimiento = establecimiento;
|
||||
}
|
||||
if (ambito) {
|
||||
payload.ambito = ambito;
|
||||
}
|
||||
if (ips) {
|
||||
payload.ips = ips;
|
||||
}
|
||||
return this.http.post<JobResponse>(
|
||||
`${this.API_URL}/jobs/autorizaciones-zip`,
|
||||
{ fecha_inicio: fechaInicio, fecha_fin: fechaFin },
|
||||
payload,
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,6 +25,8 @@ export interface JobResult {
|
||||
duplicados?: number | null;
|
||||
creadas?: number | null;
|
||||
sin_paciente?: number | null;
|
||||
sin_diagnostico?: number | null;
|
||||
sin_ambito?: number | null;
|
||||
sin_cups?: number | null;
|
||||
sin_ips?: number | null;
|
||||
cups_no_cubiertos?: number | null;
|
||||
|
||||
@ -67,8 +67,13 @@ export interface CrearAutorizacionPayload {
|
||||
id_ips: number;
|
||||
numero_documento_autorizante: number;
|
||||
cup_codigo: string;
|
||||
cie10_codigo?: string;
|
||||
cie10_descripcion?: string;
|
||||
tipo_autorizacion?: string;
|
||||
tipo_servicio?: string;
|
||||
ambito_atencion?: string;
|
||||
numero_orden?: string;
|
||||
estado_entrega?: string;
|
||||
observacion?: string;
|
||||
fecha_autorizacion?: string; // yyyy-MM-dd
|
||||
}
|
||||
@ -78,6 +83,11 @@ export interface RespuestaAutorizacion {
|
||||
fecha_autorizacion: string;
|
||||
version?: number;
|
||||
estado_autorizacion?: string;
|
||||
cie10_codigo?: string | null;
|
||||
cie10_descripcion?: string | null;
|
||||
ambito_atencion?: string | null;
|
||||
numero_orden?: string | null;
|
||||
estado_entrega?: string | null;
|
||||
}
|
||||
|
||||
export interface AutorizacionListado {
|
||||
@ -85,6 +95,8 @@ export interface AutorizacionListado {
|
||||
fecha_autorizacion: string;
|
||||
observacion: string | null;
|
||||
cup_codigo?: string | null;
|
||||
cie10_codigo?: string | null;
|
||||
cie10_descripcion?: string | null;
|
||||
cup_descripcion?: string | null;
|
||||
cup_nivel?: string | null;
|
||||
cup_especialidad?: string | null;
|
||||
@ -92,6 +104,9 @@ export interface AutorizacionListado {
|
||||
tipo_servicio?: string | null;
|
||||
version?: number | null;
|
||||
estado_autorizacion?: string | null;
|
||||
ambito_atencion?: string | null;
|
||||
numero_orden?: string | null;
|
||||
estado_entrega?: string | null;
|
||||
id_ips?: number | null;
|
||||
numero_documento_autorizante?: number | null;
|
||||
nombre_ips: string;
|
||||
@ -99,6 +114,7 @@ export interface AutorizacionListado {
|
||||
departamento?: string | null;
|
||||
ips_tiene_convenio?: boolean | null;
|
||||
nombre_autorizante: string;
|
||||
nombre_solicitante?: string | null;
|
||||
}
|
||||
|
||||
export interface AutorizacionVersion {
|
||||
@ -229,6 +245,41 @@ export class PacienteService {
|
||||
);
|
||||
}
|
||||
|
||||
subirArchivosHospitalarios(
|
||||
numeroAutorizacion: string,
|
||||
historialClinico: File,
|
||||
anexo: File
|
||||
): Observable<any> {
|
||||
const formData = new FormData();
|
||||
formData.append('historial_clinico', historialClinico);
|
||||
formData.append('anexo', anexo);
|
||||
|
||||
const token = this.authService.getToken();
|
||||
const headers = new HttpHeaders({
|
||||
Authorization: `Bearer ${token}`,
|
||||
});
|
||||
|
||||
return this.http.post(
|
||||
`${this.API_URL}/autorizaciones/${numeroAutorizacion}/archivos`,
|
||||
formData,
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
autorrellenarAutorizacionPdf(archivo: File): Observable<any> {
|
||||
const formData = new FormData();
|
||||
formData.append('archivo', archivo);
|
||||
|
||||
const token = this.authService.getToken();
|
||||
const headers = new HttpHeaders({
|
||||
Authorization: `Bearer ${token}`,
|
||||
});
|
||||
|
||||
return this.http.post(`${this.API_URL}/autorizaciones/autorrellenar`, formData, {
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
obtenerVersionesAutorizacion(numeroAutorizacion: string): Observable<AutorizacionVersionResponse> {
|
||||
return this.http.get<AutorizacionVersionResponse>(
|
||||
`${this.API_URL}/autorizaciones/${numeroAutorizacion}/versiones`,
|
||||
@ -345,6 +396,14 @@ export class PacienteService {
|
||||
);
|
||||
}
|
||||
|
||||
actualizarEstadoEntrega(numeroAutorizacion: string, estado: string): Observable<any> {
|
||||
return this.http.patch(
|
||||
`${this.API_URL}/autorizaciones/${numeroAutorizacion}/estado-entrega`,
|
||||
{ estado_entrega: estado },
|
||||
{ headers: this.getAuthHeaders() }
|
||||
);
|
||||
}
|
||||
|
||||
actualizarEstadoAutorizacionesMasivo(
|
||||
fechaInicio: string,
|
||||
fechaFin: string,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user