Skip to content

feat(ti/documentacion-api): catalogo de APIs con captura propia y RBAC fino

Rafael Bautista requested to merge ti into qa

Vista nueva en /ti/sistemas/documentacion-api: el catalogo de las APIs del grupo, con alta, edicion y baja logica desde el portal. La hoja "Registro | Documentacion API | Corporativo Caabsa" se carga UNA vez como semilla y se abandona; a partir de ahi la fuente de verdad es la tabla. No se agrega parser a sheets_sync.py: es carga batch, no una fuente sincronizada.

SIN espejo en el OAE, y por construccion. El componente vive en portal_ti/components/documentacion-api/ y no en components/dashboard/ti/, que es la carpeta que docker-compose monta dentro de frontend/ como @ti-dashboards/*. Al quedar fuera, el shell OAE no puede importarla aunque alguien lo intente; no depende de que nadie se acuerde del acuerdo.

Datos (tabla ti_doc_api). La clave natural es el ENDPOINT y no el nombre: dos sistemas pueden publicar un GetVersion. El UNIQUE es PARCIAL sobre las filas vivas, para que dar de baja una API libere su URL. Las fechas se capturan como mes y ano ("julio 2025") y se guardan en dos formas — el texto tal cual se escribio y su DATE al dia 1 —, porque sin el texto no se reconstruye lo capturado y sin la fecha no se puede ordenar ni semaforizar la vigencia. La columna estatus existe porque en la hoja el "DEPRECIADO-" venia EMBEBIDO en la descripcion, y un estado disfrazado de texto libre no se filtra ni se cuenta. revisar_captura marca la clasificacion que hubo que normalizar al cargar: la hoja traia ENDPOINT, que no es ninguno de los cuatro valores que su propio encabezado declara, asi que cae a 'interna' pero queda senalada en la vista en vez de perderse en silencio. El solicitante guarda las dos cosas, id del catalogo cuando cruza y texto cuando no: un area como OAE es un portal, no una empresa, y no se le inventa una.

El vinculo al catalogo es surrogate SIN ForeignKey, igual que juridico y ventas. Lo atrapo ci_validate_models.py en el primer intento: nadie en el repo ata la tabla de un modulo al catalogo con FK declarativo. Ademas evita que "el area borro una empresa" se vuelva un error de escritura en un modulo ajeno; si el id desaparece, la vista degrada al texto capturado.

Permisos: RBAC fino de TI, tercera instancia del patron validado en juridico y RH (ti_modulo / ti_usuario_modulo / ti_usuario_rol). Con una diferencia estructural: esos dos dominios tienen BD propia y resuelven contra un mirror con SQL crudo, y TI no la tiene — vive en la base de control, junto a users, asi que es ORM y las FK son reales, sin mirror ni upsert-on-auth. Row-level: quien captura edita y da de baja lo suyo, y lo ajeno SOLO el admin del sistema (decision del area). Las filas de la carga batch no tienen autor, asi que solo las toca el admin: nadie hereda la autoria de lo que cargo un script. Las bajas son logicas y exigen motivo — se dan justo para poder responder despues "por que desaparecio esta API", y un motivo vacio haria inutil el mecanismo. La papelera y el restaurar son de gerente_ti y admin. Se administra desde portal_ti /admin/usuarios; sin esa pantalla el permiso solo se podria otorgar por API, que es tanto como decir que no se podria otorgar.

_migrate_ti_rbac siembra el catalogo de modulos y, a diferencia de _migrate_ti, NO hace early-return cuando ya corrio: hace upsert por clave. _migrate_ti se sale si el area existe, y por eso una sub-area agregada despues nunca aparecia en entornos ya migrados.

Carga batch: scripts/import_doc_api_sheet.py, idempotente por endpoint, que nunca pisa una fila capturada en el portal y aborta si el encabezado de la hoja cambio — cargar los responsables en la columna de proveedores por un reordenamiento seria peor que no cargar. Es de UN SOLO USO: se borra cuando esten cargados los tres entornos (anotado en docs/PENDIENTES.md).

Kit de formularios de portal_ti: se agregan SelectField, ComboBoxField, MesAnioField y SugerenciaField, que el kit no tenia. El ComboBoxField es catalogo maestro con BUSQUEDA y escape a texto libre; reemplazo a un primer intento con que fallo por las dos razones que resuelve — con ~200 empresas nadie encuentra la suya sin teclear, y la salida a texto libre quedaba escondida al final del desplegable, con una leyenda que prometia "capturala a mano" sobre un recuadro que no dejaba escribir. Cierra su panel con mousedown sobre un contenedor que envuelve input Y lista: si solo cubriera el input, el mousedown sobre una opcion contaria como clic afuera y cerraria el panel antes del click, que es el mismo bug que sigue abierto en la campana de notificaciones.

Las cuatro KPI cards abren su detalle — la tarjeta dice cuantas y el detalle dice cuales, que es la pregunta que sigue siempre. El drill calcula sobre las filas vivas, igual que el resumen del backend, para que no descuadre cuando la papelera esta encendida.

Verificado: npm run build de portal_ti (el CI solo compila frontend/), check_lookandfeel.py limpio sobre los archivos nuevos, ci_validate_models.py, y los seis endpoints probados contra el backend real — alta, 409 en endpoint duplicado, 422 en clasificacion invalida y en baja sin motivo, papelera y restaurar. Suite backend en su baseline: 235 passed, y los 14 fallos de test_juicios_api.py son pre-existentes (db_juridico local vacia). Se agregan 21 pruebas de la normalizacion de la captura, que es donde esta la logica delicada y donde hay DOS escritores: el formulario y el script de carga.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Merge request reports

Loading