Skip to content

PROGRESS

Estado (2026-09-10)

🟢 Backfill de cfdi_recibidos 2025 completo. A petición explícita del usuario, se corrieron los 12 periodos de 2025 (--periodo 2025-01 ... --periodo 2025-12) con cargar_cfdi_recibidos.py, mismo wrapper run_tracked.sh del cron. Los 12 meses cargaron limpio, 0 fallidos (entre 4,644 y 6,113 archivos por mes). raw_sat.cfdi_recibidos pasó de solo 2026 (enero-septiembre) a cubrir periodo 2025-01 → 2026-09, 110,965 filas totales. Corrida en dos tandas (la primera se cortó por timeout del shell a los ~4 meses) — sin problema porque el loader hace upsert por uuid (ON CONFLICT), así que reprocesar los mismos periodos es seguro.

Único detalle menor: 2025-03 quedó con 5,144 filas en Postgres vs 5,145 "cargados/actualizados" que reportó el log — consistente con 2 archivos compartiendo el mismo uuid (el upsert los colapsa a 1 fila), no un fallo del parser. No se investigó a fondo, volumen insignificante.

🟢 15 columnas nuevas en cfdi_recibidos/cfdi_emitidos (dos tandas, mismo día) + limpieza de huérfanas + AUTOHEAL. Pedido de negocio para que conciliacion-cfdi (ver su índice) no tenga que re-parsear el XML de mpro en cada corrida:

  1. Primera tanda (5 columnas: descuento, ieps_trasladado, impuestos_locales_trasladados, impuestos_locales_retenidos, total_impuestos_retenidos) — misma lógica que conciliacion-cfdi/src/cfdi_parser.py. Bug encontrado al implementarla: comprobante.iter() sin argumento itera también nodos Comment, y etree.QName() truena sobre ellos — 10 archivos reales con comentarios XML fallaron por esto en la primera corrida. Corregido con comprobante.iter("*") — ver gotcha dedicado en index.md.
  2. Backfill completo 2026 en ambas tablas tras la primera tanda: cfdi_recibidos (45,760 filas) y cfdi_emitidos (6,502 filas), enero-septiembre.
  3. Huérfanas descubiertas durante la verificación: 171 filas de cfdi_recibidos con datos válidos pero cuyo XML ya no existe en el mount (rotación normal del share, no un bug — ver gotcha dedicado en index.md). Se creó raw_sat_xml/maintenance.py (diff + borrar, mismo patrón que dlt/maintenance.py) y se borraron las 171 con confirmación explícita del usuario.
  4. AUTOHEAL habilitado en check_raw_volume.py: para estas dos tablas, el check ahora compara contra archivos reales del mount (no contra "la corrida anterior") y, si no da pass, limpia huérfanas solo — con el mismo límite de seguridad de maintenance.py de por medio.
  5. Segunda tanda (10 columnas más, mismo día, pedido de negocio nuevo): ret_iva/ret_isr (desglose de Retenciones), complemento Pagos/REP (pagos_monto_total, pagos_iva_total, pagos_dr_uuids, pagos_dr_pagado — con las dos variantes de complemento, con/sin nodo Totales, probadas por separado), complemento ValesDeDespensa (vales_despensa_total, vales_despensa_n_trab), complementos (inventario de qué complementos trae cada CFDI) y base_exenta. Re-corrido el backfill 2026 completo tras agregarlas — reveló que CartaPorte es, por mucho, el complemento más común (28,971 de 45,747 CFDI recibidos de 2026), no documentado antes en este proyecto.
  6. Verificación final: check_raw_volume.check_cfdi_vs_carpeta() da pass en ambas tablas (recibidos: 45,747 filas Postgres vs 45,757 archivos, diff=-10 explicado por los 10 XML que fallan parseo — HTML guardado como .xml/corrupto, no huérfanas; emitidos: 6,502 = 6,502).

Ver columnas completas en warehouse.md.

Estado (2026-09-07)

🟢 El origen ya empezó a subir cfdi_emitidos 2026 — ya no está vacío. La sorpresa del 2026-09-02 (la carpeta 2026 sin ninguna factura real de ese año) era temporal, no un problema permanente del origen: el proveedor (SincronizarXml en .211) retomó la carga y empezó a poblar 2026/ con la misma convención plana {año}/{mes} que ya usan 2025 y que el loader ya soportaba sin cambios de código. cfdi_retencion no tuvo que tocarse — ya estaba al día solo (su cron recorre toda la carpeta cada corrida).

Qué se hizo hoy (2026-09-07)

  1. Verificación de contenido, no solo de nombre de carpeta. Antes de asumir que ya había datos reales, se confirmó por el atributo Fecha= dentro de una muestra de XML (no por el nombre de la subcarpeta) que XML EMITIDOS/ 2026/01/ sí contiene facturas emitidas reales de enero 2026 (ej. Fecha="2026-01-02T09:30:18") — 6,502 archivos. Agosto/septiembre 2026 (el rango que cubre el cron diario de "mes actual + mes anterior") seguían sin aparecer en el mount a esta fecha.
  2. Sin cambios de código. cargar_cfdi_emitidos.py ya sabía resolver la convención plana {año}/{mes} (period_folders(), agregada desde que se escribió el script) — la única razón por la que enero no se había cargado es que el cron normal solo procesa mes actual + mes anterior, y enero ya había quedado fuera de esa ventana.
  3. Carga manual del hueco: python3 cargar_cfdi_emitidos.py --periodo 2026-01 (con el mismo wrapper run_tracked.sh del cron, para que quede en el health check). Resultado: 6,502 encontrados, 6,502 cargados, 0 nómina omitida, 0 fallidos. Verificado en Postgres: raw_sat.cfdi_emitidos pasó de 13,208 a 19,710 filas, rango de periodo ahora 2025-09 → 2026-02 (los 2 registros en 2026-02 son facturas de fin de enero con timestamp cruzando a las primeras horas de febrero, insignificante).
  4. Pendiente, no accionable todavía: agosto y septiembre 2026 de emitidos siguen sin aparecer en el share. Cuando el origen los suba, el cron normal los recogerá solo (ya caen en su ventana de mes actual + mes anterior) — no hace falta backfill manual salvo que el origen vuelva a destapar meses viejos fuera de esa ventana (como pasó con enero), caso en el que sí hay que correr --periodo a mano.

Estado (2026-09-02)

🟢 Extendido a las 3 tablas de XML del SAT. Antes solo cfdi_recibidos — ahora también cfdi_emitidos y cfdi_retencion, mismo patrón (loader propio por carpeta, cron escalonado, run_tracked.sh). Además: se encontró y corrigió un bug real de IVA duplicado que afectaba a cfdi_recibidos desde antes de esta pasada. cfdi_recibidos ya tiene 2026 completo (enero-septiembre).

cfdi_recibidos — backfill enero-mayo + septiembre 2026 (mismo día)

El backfill inicial del 2026-08-31 solo había cubierto julio+agosto (default del script, mes actual + mes anterior). A petición explícita del usuario se completó el resto del año: enero-mayo cargaron limpio (0 fallidos cada uno, 27,845 filas). Septiembre (mes en curso, apenas 286 archivos disponibles) tuvo 23 fallidos de 286 — no es un problema del parser: esos 23 .xml en realidad contienen una página HTML de error ("Ha ocurrido un error al procesar su última acción"), no un CFDI — el proceso que descarga los XML del origen a veces guarda esa página de error con extensión .xml cuando algo falla del lado del SAT/portal. Tabla completa: 44,564 filas, periodo 2026-01 a 2026-09.

Qué se hizo hoy (2026-09-02)

  1. cargar_cfdi_emitidos.py (nuevo) → raw_sat.cfdi_emitidos. Carpeta XML EMITIDOS, mucho más grande (~360K XML históricos vs 10,631 de recibidos) y mucho menos ordenada: mezcla CFDI 3.3 (namespace cfd/3, años viejos) y 4.0 (cfd/4) — el namespace se detecta por archivo, no se asume fijo. Decisiones explícitas del usuario:
  2. Nómina fuera. La carpeta _CA (recibos de nómina, complemento nomina11/nomina12) no entra a cfdi_emitidos — no es ingreso, mezclarlo ensuciaría subtotal/total. Como la carpeta perdió el split AC/CA desde 2025 (los XML quedan sueltos en la carpeta del mes), la exclusión se hace por contenido (es_nomina(), detecta el complemento por local-name sin importar la versión), no por carpeta.
  3. Backfill: "todo 2026", no el histórico completo (que hubiera sido ~360K archivos, mucho más lento sobre el mount CIFS). Resultado real, con una sorpresa: la carpeta 2026 no tiene facturas con fecha 2026 todavía — su contenido real son subcarpetas mal archivadas de 2025-09 y 2025-11 (error de captura del origen, no de este loader). 16,031 encontrados, 13,208 cargados (7,825 AUTOEMITIDO + 5,383 EMITIDO), 2,823 nómina omitida, 0 fallidos. El histórico 2020-2025 queda pendiente, fuera de alcance de esta pasada.
  4. cargar_cfdi_retencion.py (nuevo) → raw_sat.cfdi_retencion. Carpeta XML RETENCIONES — esquema de SAT distinto al de un CFDI normal (CFDI de Retenciones e Información de Pagos, namespace .../retencionpago/1 o /2, no cfdi:Comprobante). Caso de negocio real: ISR retenido sobre intereses pagados a accionistas/inversionistas (de ahí subcarpetas ACCIONISTAS/INVERSIONISTAS en algunos años). Las dos versiones del esquema describen lo mismo con distinto nombre de atributo (RFCEmisor v1 vs RfcE v2, etc.) — el parser mapea por versión. Volumen chico (~2,400 XML en 10 años) y sin convención de carpetas estable, así que el script no filtra por periodo: recorre toda la carpeta en cada corrida (cron incluido) — costo trivial y se auto-repara si la carpeta cambia de convención otra vez. Backfill completo (todo el histórico, 2016-2026): 2,442 encontrados, 2,439 cargados, 3 fallidos (archivos mal colocados en la carpeta — CFDI normal en vez del esquema de retenciones, no soportado, volumen insignificante).
  5. ⚠️ Bug real encontrado y corregido: IVA duplicado en cfdi_recibidos. Al escribir el parser de emitidos (mismo patrón que recibidos) se encontró que el XPath .//cfdi:Traslado para sumar IVA también matcheaba el desglose por Concepto además del resumen a nivel Comprobante — duplicando el IVA en el 100% de las facturas que tienen IVA (confirmado con una muestra real de 300 XML de agosto 2026: 105 tenían IVA, 0 con el valor correcto). Este bug ya estaba en el script antes de esta pasada — no algo introducido en la reactivación del 2026-08-31, venía de antes. Fix: sumar solo cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado (hijo directo de la raíz), no .//. Se re-corrió cargar_cfdi_recibidos.py para los 3 periodos ya cargados (2026-06/07/08, 16,001 filas) — el IVA quedó en ~la mitad del valor anterior en cada uno (ej. 2026-06: $11,192,318.78 → $5,596,354.75). El fix se aplicó también a cargar_cfdi_emitidos.py antes de su primera corrida (nunca llegó a producción con el bug).
  6. Cron: 2 líneas nuevas, escalonadas 5 min después de recibidos (5 5 emitidos, 10 5 retención) para no pegarle al mismo mount a la vez. Mismo wrapper run_tracked.sh, mismo log raw_sat_xml.log. Verificado con crontab -l completo (15 líneas, las 13 anteriores intactas).

    ⚠️ Gotcha de esta sesión, sin impacto real: instalar el crontab pasando el archivo como argumento (crontab archivo.txt) fallaba con un No such file or directory sobre una ruta truncada, sin causa clara identificada. Se resolvió usando crontab - < archivo.txt (stdin) en vez de pasar la ruta como argumento.

Estado (2026-08-31)

🟢 ELT reactivado y promovido a trivasa-bi-core/raw_sat_xml/. Cron corriendo, mount montado, hueco de carga rellenado. La webapp de consulta (puerto 8600) sigue inactiva a propósito — fuera de alcance de este trabajo, ver index.md.

Qué se hizo hoy (2026-08-31)

  1. Movido, no solo repuntado. El loader (cargar_cfdi_recibidos.py) se sacó de ~/por_ordenar/exploracion/consulta_xmls_gastos/ (zona de exploración cruda, fuera de git) y se promovió a trivasa-bi-core/raw_sat_xml/ — ya era ELT real (rama //.211/ SincronizarXml ──py──► raw_sat del diagrama de stack-bi.md), documentada ahí desde antes pero nunca colocada físicamente en el repo correcto.
  2. Credenciales de Postgres, sin duplicar una copia propia. Se agregó db_credentials.postgres_dw() a shared/db_credentials.py (lee dlt/.dlt/secrets.toml → [destination.postgres.credentials], misma credencial que ya usan los 6 dlt/load_*.py) — reemplaza el hack de ruta relativa roto que tenía el script original (SCRIPT_DIR.parent.parent / "postgres-warehouse" / ".env", que ya no resolvía a nada real). Se evaluó explícitamente usar Infisical para inyectar esta credencial en runtime y se descartó — decisión ya documentada en credenciales-y-conexiones.md (2026-08-12, caso varela-bot: Infisical es respaldo, no fuente real cuando ya existe un secrets.toml).
  3. Dependencia faltante: lxml no estaba instalado a nivel sistema — pip install lxml --break-system-packages (mismo patrón que psycopg2-binary, ya instalado así). Sin venv — este repo corre sus scripts con el Python de sistema, no como el proyecto original que dependía de un venv/ que nunca se reconstruyó.
  4. Mount CIFS repuntado. /etc/fstab: nueva línea apuntando a trivasa-bi-core/raw_sat_xml/mnt_xml/ en vez de la ruta vieja trivasa-bi-dev/.... Credenciales SMB sin cambios (siguen en ~/.smbcredentials/sincronizarxml + respaldo en Infisical, ver index.md) — el usuario confirmó que esa parte no necesitaba tocarse. sudo systemctl daemon-reload + mount -a: los 3 mounts CIFS del host (win200_c, win200_d, SincronizarXml) montaron limpios.

    ⚠️ Gotcha propio de esta sesión: un sed con sintaxis rota (4 delimitadores # en vez de 3) corrompió las 17 líneas de /etc/fstab (prefijó texto literal a cada línea) — el error quedó oculto por un 2>/dev/null en el mismo comando. Se detectó de inmediato por los parse error de mount, se restauró desde /etc/fstab.bak-2026-08-28 (backup ya existente de la limpieza del 2026-08-28) y se reaplicó el cambio de ruta con reemplazo exacto de string (sin regex) en vez de sed. Verificado con diff contra el backup antes de continuar. Sin impacto real (no hubo reboot de por medio), pero si se automatiza edición de /etc/fstab de nuevo: nunca silenciar stderr de un comando que reescribe ese archivo.

  5. Crontab repuntado, misma línea 0 5 * * *, ahora envuelta en observability/checks/run_tracked.sh raw_sat_xml (antes no pasaba por ningún wrapper de observabilidad) — aparece en el dashboard elt-health igual que los dlt/load_*.py. Probado manualmente end-to-end (wrapper + script + Postgres), corrida limpia.
  6. Hueco de carga rellenado. Corrida manual cubrió julio + agosto 2026 (default del script): 10,631 XML encontrados, 10,631 cargados/actualizados, 0 fallidos. Verificado en Postgres: raw_sat.cfdi_recibidos tiene 16,049 filas totales, periodo='2026-08' con 5,112 filas y fecha_carga de hoy — la tabla ya no tiene el hueco desde 2026-08-11.
  7. Limpieza de la copia vieja. cargar_cfdi_recibidos.py y el mnt_xml/ viejo se borraron de ~/por_ordenar/exploracion/consulta_xmls_gastos/ (quedaría duplicado y confuso mantener dos copias) — el README.md de esa carpeta se actualizó para apuntar a la ubicación nueva y dejar claro que solo la webapp sigue ahí.

Qué sigue inactivo, a propósito

  • Webapp de consulta (consulta-xmls-gastos.service, puerto 8600): sigue disabled, código sigue en ~/por_ordenar/exploracion/consulta_xmls_gastos/webapp/. No se tocó — decisión explícita 2026-08-31, fuera de alcance de esta reactivación. Retomar si hace falta: mismo diagnóstico que ya estaba en la versión anterior de este archivo (ExecStart/WorkingDirectory del .service apuntan a una ruta que ya no existe).

Historial — limpieza previa (2026-08-28)

Antes de la reactivación de arriba, se había hecho limpieza completa en vez de dejar el pipeline crasheando: consulta-xmls-gastos.service detenido y deshabilitado, crontab comentado, mount desmontado y comentado en fstab (backup en /etc/fstab.bak-2026-08-28, el mismo que se usó hoy para recuperarse del gotcha del sed), y ~/trivasa-bi-dev borrado por completo (ya no quedaba nada útil ahí). Causa raíz de la ruptura original: el código real se había movido/recreado en ~/por_ordenar/exploracion/ consulta_xmls_gastos/ sin actualizar las 3 cosas que apuntaban a la ruta vieja ~/trivasa-bi-dev/... (fstab, crontab, systemd). Credenciales del mount CIFS (SAMBA_SINCRONIZARXML_*) respaldadas en Infisical (secret-management, env prod) ese mismo día — siguen vigentes, sin cambios en esta pasada.