conciliacion-cfdi
Objetivo
Conciliación automatizada entre los CFDI timbrados ante el SAT (bodega
raw_sat en Postgres, ver warehouse) y los
registros contables de Management Pro / mpro (SQL Server), para responder,
periodo a periodo: ¿qué documentos de mpro corresponden a cada CFDI, y el
importe contabilizado (cargo/abono en póliza) cuadra con el importe fiscal
del CFDI? Cubre las tres direcciones de CFDI que maneja Trivasa:
recibidos, emitidos, y retención (constancias de retención/intereses a
prestamista).
Dónde vive el código
Repo separado, no en este monorepo: github.com/ehalso/conciliacion-cfdi
(público) — toda la extracción, el parseo de XML, la lógica de conciliación
y los reportes .xlsx viven ahí.
Consolidación 2026-09-10 — antes eran varios proyectos hermanos respondiendo la misma pregunta por separado, ahora es un solo repo:
~/proyectos/conciliacion-master/ ya no existe — se migró por completo
y se borró (respaldo final en
~/backups/conciliacion-master-final-2026-09-10.zip; el respaldo de la
primera pasada, antes de completar layout-gastos, en
~/backups/consolidacion-conciliacion-2026-09-10.zip):
adjuntar-xml(recibidos, nivel documento) se portó arecibidos/nivel_documento/.conciliacion-emitidos(emitidos + retención, nivel documento) se portó aemitidos/nivel_documento/yretencion/cruce_sat/.layout-gastos(reconciliación CECO/Gasto_Registro, scripts01-17) se portó completo alayout_gastos_poliza/— incluido lo exploratorio (14-17, reconstrucción víaPoliza_Configuracion) que en la primera pasada se había dejado atrás. Su UI (CONT-1/2/4/5) vive enreportes_streamlit/layout_gastos_poliza/; CONT-6 (promoción derecibidos/nivel_documento/04_conciliacion_mpro_vs_xml.py) se separó areportes_streamlit/recibidos_nivel_documento/— ver layout-gastos/index.md.funcionales-auditoria(reportes nativos RPTRV79, dominio de auditoría de compras, no de conciliación) pasó a su propio repo,github.com/ehalso/reportes-mpro— incluidas las 3 páginas Streamlit que lo envolvían, antes mezcladas en el hub delayout-gastos.
Además, ~/proyectos/layout-contabilidad/layout-gastos-streamlit-claude se
retiró — reemplazado por streamlit-reportes (el deployment real de
producción, ver streamlit-reportes).
Acceso a datos: conexión directa por SQLAlchemy (psycopg2/pymssql,
src/bridge_client.py) a las tres bases — Postgres raw_sat y los dos SQL
Server de mpro. El modo anterior (API puente HTTP vía
query-api, reportesweb.frento.com.mx) queda
documentado como fallback histórico, ya no es el camino primario desde
2026-09-09.
Estructura del repo (2026-09-10 — por pregunta de negocio, no por sesión)
Cada dominio se concilia desde dos ángulos independientes y complementarios, no un método único:
nivel_documento— ¿el documento que capturó/originó el CFDI en mpro trae el mismo importe? (compara contra la tabla propia del módulo, nunca contra la póliza).nivel_poliza— ¿lo que se contabilizó enPoliza_Detalle(Cargo/Abono real) cuadra contra el CFDI? Un nivel más profundo: el documento puede estar perfecto y la póliza no aislarlo bien.cruce_sat— cruce contra una fuente independiente de mpro (los XML que el PAC deja en el share del SAT, cargados directo a Postgres sin pasar por el ERP) — el único ángulo que puede detectar un comprobante que se timbró y el ERP nunca registró en ningún módulo.
recibidos/{nivel_documento, nivel_poliza, cruce_sat}
emitidos/{nivel_documento, nivel_poliza, cruce_sat}
retencion/cruce_sat/
cobranza_rep/
layout_gastos_poliza/
reportes_streamlit/
cruce_sat/ de recibidos y emitidos son placeholders documentados
(pendientes de construir); el de retención (retencion_reconciliation.py)
ya está corrido y validado — ver el hallazgo abajo.
Estado (2026-09-10)
- Recibidos:
nivel_poliza(baseline_universal.py, el método vigente): H1 2026 (ene-jun) en 99.36% (9,511/9,572). El 0.64% restante son familias con mecanismo identificado, no misterios — ver calidad de datos, secciónComprobante_Digital(CFDI).nivel_documento(exadjuntar-xml,03/04/05): 95.76% de importe conciliado, 99.77% de IVA acreditable con CFDI ligado (enero 2026) — responde una pregunta quenivel_polizano cubre (los impuestos a nivel documento;baseline_universal.pydeja el IVA fuera del chequeo automatizado a propósito, se postea consolidado por día/póliza).- Emitidos:
nivel_documento(conciliacion_emitidos_documento.py): 100% en todo H1 2026 (14,554/14,554 CFDI conciliables — factura, nota de crédito, retenciones). Por construcción: el CFDI se genera DESDE el documento de mpro, no puede diferir.nivel_poliza(baseline_universal_emitido.py, nuevo 2026-09-10): NOTA_CREDITO 99.5% (separar el Cargo por cuenta contable — devolución de mercancía vs. reversión de costo de venta — resolvió lo que parecía ruido). FACTURA sigue sin método funcional (0.6%, el 92% del universo monetario) — la póliza de ingreso de VENTA no aísla el documento porPd_Referenciacomo sí hacen recibidos y NOTA_CREDITO. Candidato: preguntar a Trivasa cómo se referencia el documento ahí, o un chequeo agregado por sucursal/día.- Retención (
retencion/cruce_sat/retencion_reconciliation.py): nivel 1 corrido para H1 2026 — dos conceptos con mecánica distinta bajo el mismocve_retenc(arrendamiento/honorarios 10% víaCONSTANCIA_RETENCIONvs. intereses a prestamista 20% víaGASTO_REGISTRO, nuncaCONSTANCIA_RETENCION). Hallazgo real de negocio: 29 constancias de retención por $536,597.04 ($107,319.45 de ISR) que el SAT tiene timbradas y el ERP nunca registró — 14 en enero, 15 en febrero, ninguna en marzo-junio. Confirmado por dos implementaciones independientes (dos sesiones distintas llegaron al mismo número por caminos separados) — ver sección dedicada en calidad de datos. - Cobranza (REP): pendiente, sin construir — metodología ya medida en
el proyecto original
conciliacion-emitidos(96.18% de facturas con cobranza conciliada, enero 2026), falta portarla.
Decisión de arquitectura: los ajustes del CFDI (Descuento/IEPS/impuestos locales) ahora viven en raw_sat, no se re-parsean del XML de mpro
Hasta 2026-09-09, baseline_universal.py necesitaba, en cada corrida,
volver a bajar el Cd_XML de cada CFDI desde Comprobante_Digital (mpro) y
reparsearlo con lxml para sacar Descuento, IEPS trasladado, e impuestos
locales (raw_sat.cfdi_recibidos solo trae el subtotal bruto del SAT, sin
estos ajustes) — el paso más caro del pipeline, sin caché entre entornos.
2026-09-10: se agregaron 15 columnas nuevas, nullable, a
raw_sat.cfdi_recibidos/cfdi_emitidos — pobladas por el mismo loader de
raw_sat_xml (ver consulta-xmls) al parsear
el XML durante la ingesta, en dos tandas el mismo día:
- Primera tanda (misma lógica ya validada en
conciliacion-cfdi/src/cfdi_parser.py):descuento,ieps_trasladado,impuestos_locales_trasladados,impuestos_locales_retenidos,total_impuestos_retenidos. - Segunda tanda (pedido de negocio nuevo, no viene de
cfdi_parser.py): desgloseret_iva/ret_isr(Impuestos/Retenciones), complemento Pagos (REP) —pagos_monto_total,pagos_iva_total,pagos_dr_uuids(;-separado, únicos, orden alfabético),pagos_dr_pagado—, complemento ValesDeDespensa (vales_despensa_total,vales_despensa_n_trab),complementos(inventario de qué complementos trae el CFDI,;-separado, sin el Timbre) ybase_exenta(Traslados conTipoFactor='Exento', por@Base, no@Importe).
baseline_universal.py ahora lee la primera tanda directo de raw_sat
(ajustes_desde_sat(), en vez de xml_ajustes()) — validado exacto
contra los 6 meses de H1 2026 (mismo 99.36%/61 pendientes de siempre, sin
re-bajar ni un solo XML de mpro). Ambas tablas quedaron backfillado
completo para todo 2026 disponible en el mount (cfdi_recibidos
enero-septiembre, cfdi_emitidos enero-septiembre también — ya no solo
enero, corregido tras un backfill completo posterior el mismo día).
raw_sat.cfdi_retencion llegó ya parseado desde su ingesta original
(monto_total_operacion, monto_total_retenido, cve_retenc, con
cobertura histórica completa 2016-2026) — por eso retencion_
reconciliation.py y el cruce contra SAT de emitidos tampoco necesitaron
parsear XML nunca.
Para cualquier otro proyecto que necesite estos mismos ajustes: ya
están en raw_sat.cfdi_recibidos/cfdi_emitidos/cfdi_retencion — no
hace falta reimplementar el parseo.
Gotchas de esquema/calidad de dato que salieron de aquí
Documentados en detalle en
schema/calidad-de-datos.md (secciones
Comprobante_Digital y "Retención"): el catálogo real de Cd_Tabla, los
dos formatos de longitud de Cd_Documento para GASTO_REGISTRO, el tipo
de cambio por origen (y que Cheque no tiene columna de moneda propia),
el complemento implocal:ImpuestosLocales, el reciclaje de folio entre
Venta_Encabezado/Factura_Encabezado (nunca confiar en un match de
Pc_Documento sin verificar la fecha), y los dos mecanismos de "CFDI de
retención huérfano" (omisión de etiquetado vs. sustitución de CFDI sin
re-ligar, vía CfdiRetenRelacionados).