Skip to content

feat(ti/licenciamiento): tabla propia con CRUD; la hoja deja de ser la fuente

Rafael Bautista requested to merge ti into qa

Licenciamiento leia la pestaña "Licencias" EN VIVO en cada request (services/licencias_sheet.py, cache en memoria de 300s). No habia donde capturar: toda alta, correccion o baja tenia que hacerse en el Google Sheet. Ahora la fuente de verdad es ti_licencias y la captura ocurre en el portal.

Por que NO se registro en sheets_sync

La via "natural" habria sido agregar un parser a services/sheets_sync.py. Es la peor opcion posible aqui: ese camino entra por _safe_replace, que hace DELETE + INSERT de la tabla COMPLETA en cada corrida. Cualquier fila capturada o corregida en el portal moriria en el siguiente sync, sin aviso. Esta tabla no se sincroniza y el modelo lo deja escrito para que nadie lo "arregle" despues.

Esquema — UNA tabla, sin catalogos

Se sigue el patron de ti_doc_api: baja logica con motivo/quien/cuando, auditoria, origen ('sheet'|'portal'), catalogos cerrados como constantes con CHECK en BD, y los abiertos (software, tipo de licencia, servicio) por SELECT DISTINCT de lo ya capturado. Cero tablas de catalogo: el esquema CRUD de Proyectos TI llego a tener seis y se elimino entero en mayo de 2026; las ocho tablas siguen residuales sin que nadie las consulte.

  • id ES el Id de la hoja (1..1252). El area agrego esa columna justo para poder rastrear las filas, asi que se conserva en vez de generar ids nuevos. La carga reposiciona la secuencia; la primera alta del portal fue la 1253.
  • Sin clave natural: se permiten repetidos a proposito. Un usuario puede tener dos licencias del mismo software y tipo, y la hoja ya trae 21 combinaciones repetidas de software+correo.
  • estado (activa/inactiva) es del NEGOCIO y no es la baja logica. Una licencia inactiva sigue siendo un registro valido. Las 9 INACTIVA de la hoja entraron como activas en registro.
  • La empresa es el UNICO punto de cruce con el catalogo de /admin; portafolio y estatus se derivan de ahi. La columna PORTAFOLIO de la hoja se IGNORA: traia la taxonomia CCC (7 valores), que no es la misma que empresas_grupo_caabsa.portafolio (agrupada por holding). Sostener las dos es como se contradicen dos pantallas del mismo portal.

Calidad del dato, visible en vez de silenciosa

campos_faltantes por fila y faltantes_por_campo en el resumen. empresa encabeza la lista porque sin ella no hay cruce y la fila queda fuera de los filtros mandatorios. La carga reporta lo mismo, agrupado, antes de escribir.

Carga inicial (local): 1252 filas, ids 1..1252, gasto MXN 2,480,465.41 / USD 128,933.00 -- coincide al centavo con lo medido leyendo la hoja directo, que es la comprobacion de que el parseo no deformo nada. Cruzan 1128; 124 quedan como texto y se corrigen con alias en /admin o editando en el portal.

scripts/import_licencias_sheet.py es de UN SOLO USO y se borra cuando los tres entornos esten cargados, igual que el de Documentacion API.

Verificado: ci_validate_models.py OK (19 tablas en main) y ciclo CRUD completo contra el backend -- alta con id 1253, edicion, baja logica con motivo, papelera para gerente, restaurar. Un error corregido antes de cargar: resolve() del matcher devuelve la clave como empresa_id, no id; con la primera version el cruce habria quedado nulo en las 1252 filas sin que nada lo señalara.

NO se retira todavia la lectura en vivo del Sheet: la vista actual la sigue consumiendo y quitarla ahora dejaria /ti/sistemas/licenciamiento rota. Se retira en el commit que migre el frontend, junto con el codigo muerto de la hoja vieja (models/licenciamiento.py, _parse_licenciamiento, sync_licenciamiento_only) y la tabla licenciamiento de 110 filas.

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

Merge request reports

Loading