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:
- Primera tanda (5 columnas:
descuento,ieps_trasladado,impuestos_locales_trasladados,impuestos_locales_retenidos,total_impuestos_retenidos) — misma lógica queconciliacion-cfdi/src/cfdi_parser.py. Bug encontrado al implementarla:comprobante.iter()sin argumento itera también nodosComment, yetree.QName()truena sobre ellos — 10 archivos reales con comentarios XML fallaron por esto en la primera corrida. Corregido concomprobante.iter("*")— ver gotcha dedicado enindex.md. - Backfill completo 2026 en ambas tablas tras la primera tanda:
cfdi_recibidos(45,760 filas) ycfdi_emitidos(6,502 filas), enero-septiembre. - Huérfanas descubiertas durante la verificación: 171 filas de
cfdi_recibidoscon datos válidos pero cuyo XML ya no existe en el mount (rotación normal del share, no un bug — ver gotcha dedicado enindex.md). Se creóraw_sat_xml/maintenance.py(diff + borrar, mismo patrón quedlt/maintenance.py) y se borraron las 171 con confirmación explícita del usuario. - 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 dapass, limpia huérfanas solo — con el mismo límite de seguridad demaintenance.pyde por medio. - 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 nodoTotales, probadas por separado), complemento ValesDeDespensa (vales_despensa_total,vales_despensa_n_trab),complementos(inventario de qué complementos trae cada CFDI) ybase_exenta. Re-corrido el backfill 2026 completo tras agregarlas — reveló queCartaPortees, por mucho, el complemento más común (28,971 de 45,747 CFDI recibidos de 2026), no documentado antes en este proyecto. - Verificación final:
check_raw_volume.check_cfdi_vs_carpeta()dapassen 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)
- 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) queXML 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. - Sin cambios de código.
cargar_cfdi_emitidos.pyya 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. - Carga manual del hueco:
python3 cargar_cfdi_emitidos.py --periodo 2026-01(con el mismo wrapperrun_tracked.shdel 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_emitidospasó de 13,208 a 19,710 filas, rango deperiodoahora2025-09→2026-02(los 2 registros en2026-02son facturas de fin de enero con timestamp cruzando a las primeras horas de febrero, insignificante). - 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
--periodoa 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)
cargar_cfdi_emitidos.py(nuevo) →raw_sat.cfdi_emitidos. CarpetaXML EMITIDOS, mucho más grande (~360K XML históricos vs 10,631 de recibidos) y mucho menos ordenada: mezcla CFDI 3.3 (namespacecfd/3, años viejos) y 4.0 (cfd/4) — el namespace se detecta por archivo, no se asume fijo. Decisiones explícitas del usuario:- Nómina fuera. La carpeta
_CA(recibos de nómina, complementonomina11/nomina12) no entra acfdi_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. - 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
2026no 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. cargar_cfdi_retencion.py(nuevo) →raw_sat.cfdi_retencion. CarpetaXML RETENCIONES— esquema de SAT distinto al de un CFDI normal (CFDI de Retenciones e Información de Pagos, namespace.../retencionpago/1o/2, nocfdi:Comprobante). Caso de negocio real: ISR retenido sobre intereses pagados a accionistas/inversionistas (de ahí subcarpetasACCIONISTAS/INVERSIONISTASen algunos años). Las dos versiones del esquema describen lo mismo con distinto nombre de atributo (RFCEmisorv1 vsRfcEv2, 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).- ⚠️ 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:Trasladopara 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 solocfdi:Impuestos/cfdi:Traslados/cfdi:Traslado(hijo directo de la raíz), no.//. Se re-corriócargar_cfdi_recibidos.pypara 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 acargar_cfdi_emitidos.pyantes de su primera corrida (nunca llegó a producción con el bug). - Cron: 2 líneas nuevas, escalonadas 5 min después de recibidos (
5 5emitidos,10 5retención) para no pegarle al mismo mount a la vez. Mismo wrapperrun_tracked.sh, mismo lograw_sat_xml.log. Verificado concrontab -lcompleto (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 unNo such file or directorysobre una ruta truncada, sin causa clara identificada. Se resolvió usandocrontab - < 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)
- 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ó atrivasa-bi-core/raw_sat_xml/— ya era ELT real (rama//.211/ SincronizarXml ──py──► raw_satdel diagrama destack-bi.md), documentada ahí desde antes pero nunca colocada físicamente en el repo correcto. - Credenciales de Postgres, sin duplicar una copia propia. Se agregó
db_credentials.postgres_dw()ashared/db_credentials.py(leedlt/.dlt/secrets.toml → [destination.postgres.credentials], misma credencial que ya usan los 6dlt/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 encredenciales-y-conexiones.md(2026-08-12, casovarela-bot: Infisical es respaldo, no fuente real cuando ya existe unsecrets.toml). - Dependencia faltante:
lxmlno estaba instalado a nivel sistema —pip install lxml --break-system-packages(mismo patrón quepsycopg2-binary, ya instalado así). Sinvenv— este repo corre sus scripts con el Python de sistema, no como el proyecto original que dependía de unvenv/que nunca se reconstruyó. - Mount CIFS repuntado.
/etc/fstab: nueva línea apuntando atrivasa-bi-core/raw_sat_xml/mnt_xml/en vez de la ruta viejatrivasa-bi-dev/.... Credenciales SMB sin cambios (siguen en~/.smbcredentials/sincronizarxml+ respaldo en Infisical, verindex.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
sedcon 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 un2>/dev/nullen el mismo comando. Se detectó de inmediato por losparse errordemount, 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 desed. Verificado condiffcontra el backup antes de continuar. Sin impacto real (no hubo reboot de por medio), pero si se automatiza edición de/etc/fstabde nuevo: nunca silenciar stderr de un comando que reescribe ese archivo. - Crontab repuntado, misma línea
0 5 * * *, ahora envuelta enobservability/checks/run_tracked.sh raw_sat_xml(antes no pasaba por ningún wrapper de observabilidad) — aparece en el dashboardelt-healthigual que losdlt/load_*.py. Probado manualmente end-to-end (wrapper + script + Postgres), corrida limpia. - 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_recibidostiene 16,049 filas totales,periodo='2026-08'con 5,112 filas yfecha_cargade hoy — la tabla ya no tiene el hueco desde 2026-08-11. - Limpieza de la copia vieja.
cargar_cfdi_recibidos.pyy elmnt_xml/viejo se borraron de~/por_ordenar/exploracion/consulta_xmls_gastos/(quedaría duplicado y confuso mantener dos copias) — elREADME.mdde 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): siguedisabled, 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/WorkingDirectorydel.serviceapuntan 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.