layout-gastos — estado vivo
2026-09-05 — HITO: 16/17, conciliación XML por origen sin pasar por config, 2 bugs + 1 gotcha nuevos
Extiende CONT-4/CONT-5 de ayer (solo config 0450) a un panorama completo
por origen — trabaja directo contra Gasto_Registro/Gasto_Registro_
Documento/Comprobante_Digital, para GASTO_DIRECTO/VIAJE/ORDEN_
COMPRA/CONTROL_COMBUSTIBLE (los 4 que sí deberían llevar XML).
Tres bugs reales corregidos (detalle completo en Calidad de datos →
Comprobante_Digital):
1. Cd_Documento tiene 2 formatos de longitud (14 y 18) — filtrar solo
LEN=18 perdía ~23% de los XML ligados.
2. Aceptar ambos formatos duplica el mismo XML si no se deduplica por
(FOLIO, GRD_ID, XML_UUID).
3. Cd_Monto viene en la moneda ORIGINAL del CFDI, nunca convertido a
MXN — comparar el importe ya convertido daba diferencias de hasta 18x
en documentos USD/EUR.
Con las 3 correcciones: VIAJE/ORDEN_COMPRA/CONTROL_COMBUSTIBLE en
100%/100% (cobertura y cuadre de monto, enero 2026). GASTO_DIRECTO
sigue con el hueco real: 85.94% cobertura (35.04% del importe), 89.30%
de eso cuadra en monto.
Gotcha nuevo, investigando el residual de GASTO_DIRECTO: 31/58
XML_UUID sin cuadrar son CFDI tipo RETENCIONES (monto real en fila
hermana bajo Cd_Tabla='CONSTANCIA_RETENCION', ver Calidad de datos) —
16 cuadran exacto con factor 90% (ISR de honorarios). Aplicado en 17:
27.56% → 31.40% de importe que cuadra en GASTO_DIRECTO.
Probado también el grano de componente conexa (union-find, idea de
~/proyectos/conciliacion-master/adjuntar-xml/, otra sesión) — efecto
marginal (+0.17pp), el residual de este proyecto no era mayormente por
facturas repartidas en varios documentos. Reimplementado en versión
ligera (sin Cd_XML) por límite de RAM del entorno (~5GB, confirmado con
free -h que la versión completa agota memoria sin traceback).
Probado en 3 rangos de fecha (enero, enero-marzo, enero-mayo 2026): el %
total no varía mucho (~40-41%), pero CONTROL_COMBUSTIBLE cae a
73.49% en enero-mayo (era 100% en los rangos cortos) — nuevo, sin
investigar.
Ideas de adjuntar-xml/layout-contabilidad/layout_gastos (parseo de
Cd_XML para complementos, Cuenta_X_Pagar para combustible, CFDI
intercompañía) se evaluaron por razonamiento, no se probaron
empíricamente contra esta base — quedan como hipótesis documentadas,
no como hallazgo.
Código: 16_reconciliacion_xml_por_origen.py,
17_reconciliacion_xml_grano_grupo.py. Detalle completo en index.md de
este mismo repo y en PROGRESS.md de conciliacion-master/layout-gastos/
(fuera de este repo).
2026-09-04 — CONT-4/CONT-5 (exploratorio, solo local): conciliación 1:1 contra XML
Extiende la reconstrucción vía Poliza_Configuracion de ayer (14) a una
conciliación real contra XML (CFDI) — dos reportes Streamlit nuevos,
solo corriendo en local (streamlit_venv/, puerto 8506,
127.0.0.1, no expuesto a red), no promovidos a streamlit-reportes.
15_reconciliacion_xml_via_poliza_configuracion.py — grano
(FOLIO, GRD_ID), el sugerido al comparar contra (FOLIO, CUENTA_
CONTABLE) (ver entrada de ayer): el XML liga a Comprobante_Digital.
Cd_Documento, decodificado Gr_Folio (10) + Grd_ID con padding (4) +
sufijo casi siempre fijo '0001' (4) — confirmado contra datos reales
(74,347/74,352 XML con ese sufijo). Es el documento, no la cuenta.
Dos correcciones reales encontradas armando el script (ninguna cambia la
metodología de 14, sí la de esta conciliación nueva):
- Contra qué comparar el monto: el primer intento comparó
CARGO(reconstruido víaPoliza_Configuracion— el importe neto de gasto, sin IVA, que es el que cuadra contraPoliza_Detalle) contraCd_Monto(el total bruto del CFDI, con IVA) — 2.75% de cuadre, casi todo desviado por el impuesto, que vive en otro renglón de la póliza. Corregido: compararGrd_Precio_Neto_Importe(campo nativo deGasto_Registro_Documento, ya incluye impuesto — misma lógica queIMPORTE_FOLIO_SQLdelayout_gastos_lib.pypero a nivel documento) contraCd_Monto. - Un XML puede repetirse en varios
Grd_ID: 1.83% de losXML_UUIDaparecen en más de un documento — una factura partida en N líneas idénticas (ej. cargo recurrente de electricidad dividido en 16 líneas de $237 cada una) queda capturada como N documentos que apuntan al mismoComprobante_Digital—Cd_Montoen esos casos es el total del CFDI completo, no de un solo documento. Hay que sumar el importe de todos los documentos que comparten unXML_UUIDantes de comparar.
Con las dos correcciones: 97.76% (1,922/1,966 XML_UUID) cuadra exacto
contra Cd_Monto. Residual no investigado a fondo — parece concentrado
en documentos en USD (valores repetidos idénticos entre folios distintos,
compatible con doble aplicación de tipo de cambio).
Hallazgo adicional, no buscado: una sola Poliza (config 0450)
puede consolidar varios folios Gasto_Registro distintos bajo el
mismo Pl_Folio (confirmado con datos reales, 3 folios reales bajo
0000472999) — el Abono (pago a proveedor) vive a nivel de esa póliza
consolidada, con Pd_Referencia = referencia de la factura del
proveedor, no Gr_Folio. Por eso el universo de config 0450 siempre
sale con Abono = 0 en CONT-1/CONT-2 también (no es nuevo, ya
pasaba ahí, solo que nadie lo había explicado hasta ahora) — el Abono
simplemente no es atribuible por folio con la información disponible para
esta config.
Streamlit local — CONT-4 y CONT-5, misma pestaña de Reconciliación
que CONT-1/CONT-2 (reusa resumen_por_folio()/resumen_qa() de
layout_gastos_lib.py tal cual, sin cambios — opera a nivel folio, no
depende del grano de la tabla de Datos):
- CONT-4 — grano documento
(FOLIO, GRD_ID, CUENTA), XML ligado 1:1. - CONT-5 — grano cuenta contable
(FOLIO, CUENTA), construido a propósito junto a CONT-4 para comparar: coincide con el documento en 86.28% de los folios (ver entrada de ayer), pero el 13.72% que diverge tiene más XML ligado (81.77% vs 61.92%) — falla justo donde más importa. Cada fila traeN_DOCUMENTOS/N_XML/AMBIGUOpara que se vea explícito cuándo el grano cuenta no corresponde a un solo XML.
Ambos conservan CARGO/ABONO como columnas separadas (mismo formato
que reporte_completo()) — ABONO sale en 0 para toda esta config, ver
hallazgo arriba, no es un bug de estos reportes nuevos.
Código: poliza_configuracion_lib.py (reconstruir_config() movido aquí
desde 14, más abono_via_referencia()/xml_gasto_registro() nuevas),
layout_gastos_config_lib.py (reporte_cont4()/reporte_cont5()),
reporte_ui_config.py, pages/3_CONT-4_...py/4_CONT-5_...py. Validado
con AppTest (sin excepciones, filtros probados) antes de correr el
servidor real.
Cobertura: solo config 0450, igual que 14 — no reemplaza
CONT-1/CONT-2, es un complemento exploratorio. Pendiente: generalizar a
más configs, investigar el residual de USD, decidir si esto se promueve
a producción o se queda como herramienta de diagnóstico local.
2026-09-03 (continuación 6) — Rank-pairing vs. reconstrucción real vía Poliza_Configuracion (14, exploratorio)
Pregunta que motivó esto: el rank-pairing de 07/11/12 (ordenar por
valor dentro de (FOLIO, CECO) y emparejar por posición) empareja "por
casualidad" cuando hay más de una línea de Grc_ID — ¿se puede arreglar
con las tablas de Poliza_Configuracion (configuracion-polizas.md,
poliza-explor)?
Sí, en su mayoría — y con un límite duro medido, no supuesto.
Poliza_Configuracion_Detalle.Pcd_Relacion ya hace el JOIN real a
Gasto_Registro_Control por (Gr_Folio, Grd_ID) que usa el motor de
MPRO para generar cada renglón de póliza. Reconstruyendo esa query tal
cual (14_reconstruccion_via_poliza_configuracion.py, función
reconstruir_config()) para la config 0450 (la dominante — GASTO_
DIRECTO/CONTROL_COMBUSTIBLE/VIAJE/ORDEN_COMPRA, 3,403 de ~3,600
folios de enero-marzo 2026):
- Validación: la reconstrucción, sumada por
(FOLIO,CECO), cuadra 100.00% contraPoliza_Detallereal (5,171/5,171) — confirma que es el mismo JOIN que usó el motor, no una aproximación. - Cuantificación de la ambigüedad: de las 436 combinaciones
(FOLIO,CECO)con más de una línea deGrc_ID(las únicas donde rank-pairing podría fallar), 17 se resuelven en renglones distintos de la config (distinción real, recuperable) y 419 caen en el mismo renglón (el motor las suma conSUM(...) GROUP BY Centro_Costo, sin agrupar también porTipo_Gasto). De esas 419, solo 20 tienen además más de unTipo_Gastoreal — esa es la ambigüedad irreducible: ni el propio motor de MPRO puede recuperarla, la descartó al generar el dato.
Neto: 5,151/5,171 (99.61%) de las combinaciones quedan resueltas con certeza real, no una suposición de orden — el límite duro de rank-pairing para esta config es 0.39%, no un "residual desconocido".
Alcance: solo la config 0450 (1 de ~67 usadas en el universo del
proyecto). No reemplaza 11/12 todavía — es la prueba de que la
técnica funciona y cuánto resuelve, antes de invertir en generalizarla a
las ~66 configs restantes. Ver index.md para el detalle completo y
Pendiente para el siguiente paso.
2026-09-03 (continuación 5) — Dos hallazgos de poliza-explor aplicados: filtro de CONSUMO_INTERNO y causa raíz de COMPROBACION_GASTO
trivasa-context/docs/proyectos/poliza-explor/configuracion-polizas.md
(sesión 2026-09-03, documenta cómo CT001/Poliza_Configuracion generan
las pólizas) resolvió dos cosas que este proyecto tenía como "pendiente"
o "sin explicar":
1. Filtro de doble póliza de CONSUMO_INTERNO, movido de Pl_Comentario
a Poliza_Configuracion. poliza-explor explicó la causa raíz del
patrón de doble póliza: no es una anomalía, son dos configuraciones
activas distintas sobre la misma Pc_Tabla, una marcada (CUENTAS DE
ORDEN) en Pc_Descripcion — y recomendó filtrar ahí, no en
Pl_Comentario (texto derivado, más frágil). Cambiado en 12 (query
POLIZA_SQL_CI): join a Poliza_Configuracion por Pl_Configuracion,
filtro sobre Pc_Descripcion en vez de Pl_Comentario.
Antes de aplicarlo se validó contra datos reales (enero-marzo 2026, 811
pólizas de CONSUMO_INTERNO) que ambos criterios dan el mismo resultado
exacto — cero discrepancias — pero solo usando las 4 variantes de
abreviatura que ya traía el filtro viejo (CUENTAS DE ORDEN / CTS
ORDEN / CTS DE ORDEN / CUENTA ORDEN); la primera prueba con solo
CUENTAS DE ORDEN sobre Pc_Descripcion dio 42 discrepancias porque las
configs 0296/0481 usan la abreviatura (CTS ORDEN)/(CTS DE ORDEN)
en su descripción — el filtro viejo ya la cubría, el nuevo tuvo que
igualarla explícitamente. Corrido 12 completo después del cambio:
mismo resultado exacto, 3,072/3,045 (99.12%) enero 2026 — idéntico al
ya documentado.
2. Causa raíz confirmada del residual COMPROBACION_GASTO (folio
01-0035339, CeCo 000501, $4.9489 — visto como fila huérfana
left_only en las corridas de 07/enero-marzo). poliza-explor lo
rastreó hasta la config 0394 ("COMPROBACION DE GASTOS NACIONAL"): sus
renglones de cargo no cubren el CeCo 000501 PLANTA CONCRETERA en
ninguna condición — es un hueco estructural real en la regla
contable, no ruido de la técnica de rank-pairing de este proyecto. La
póliza real (0000478288) queda descuadrada $5.36 en el sistema fuente:
$4.95 por este hueco + $0.41 de redondeo del prorrateo entre 80
renglones. No es un caso a "arreglar" en este reporte — es exactamente
el tipo de hueco que el reporte está diseñado para exponer.
2026-09-03 (continuación 4) — Python 3.14 en el venv de exploración: no es un límite, era el pin de paquete
Después de pinnear el venv a las versiones exactas de producción (entrada
anterior) surgió la duda de si el dolor de compilar desde source era culpa
de Python 3.14 en sí. No lo es — lo que determina si hay wheel
precompilado es la combinación versión-de-paquete × versión-de-Python, no
Python solo. Confirmado contra PyPI directo (no supuesto): pandas==
2.2.3/pymssql==2.3.2 (las versiones pinneadas, de antes de que 3.14
existiera) nunca van a tener wheel para cp314 — quedaron congeladas en
el pasado —, pero las versiones más nuevas de esos mismos paquetes
(pandas==3.0.5, pymssql==2.4.0, psycopg2-binary==2.9.12) sí publican
wheel manylinux para cp314 desde hace tiempo. streamlit,
streamlit-aggrid, altair y sqlalchemy son puro Python (py3-none-
any) — nunca les importa la versión de Python.
Bump hecho y revalidado en el venv de exploración (streamlit_venv/,
Python 3.14.4 del host, sin cambio): pandas 2.2.3 → 3.0.5,
pymssql 2.3.2 → 2.4.0, psycopg2-binary 2.9.10 → 2.9.12.
Instalación: segundos, cero compilación, cero paquetes de sistema
nuevos — contraste directo contra los ~20 minutos + Cython==3.0.10 +
freetds-dev/libkrb5-dev/libssl-dev/libpq-dev que costó la entrada
anterior con las versiones viejas.
pandas 2.x → 3.x es un salto de major version con cambios de
comportamiento reales (copy-on-write permanente, dtype de string por
default) — no un bump trivial como los otros dos. Se revalidó contra 07
(el script de validación canónico, rank-pairing + agregación) para los
dos rangos ya documentados como baseline, resultado idéntico, cero
diferencias:
- Enero 2026:
7,691/7,691(100.00%) — igual. - Enero-marzo 2026:
17,935/17,944(99.95%), mismos 9 filas / 3 folios residuales de siempre (05-0182622,23-0007297,01-0035339) — igual.
Pendiente antes de considerar el bump listo para producción: esto solo
revalida el camino de 07 (los 5 orígenes "normales"). 09/11/12/13
(CONSUMO_INTERNO, GASTO_REGISTRO_NOMINA, el reporte final) no se
revalidaron todavía con pandas 3.x. streamlit-reportes (producción)
no se tocó — sigue en python:3.12-slim + las versiones viejas
pinneadas; este bump vive solo en el venv de exploración de este repo por
ahora.
2026-09-03 (continuación 3) — Bump de Streamlit en producción + venv de exploración pinneado
Dos temas de infraestructura, ninguno cambia lógica de negocio del reporte:
1. streamlit==1.39.0 en producción → 1.63.0. Al promover CONT-1/
CONT-2 (entrada anterior) apareció TypeError: 'str' object cannot be
interpreted as an integer en ambas páginas — width="stretch" (API nueva
de Streamlit) no existe en 1.39.0, versión pinneada en
streamlit-reportes sin que ningún commit ni doc explicara por qué se
había bajado de una más nueva. Se decidió subir la versión (branch
bump-streamlit-1.63) en vez de bajar el código nuevo a la API vieja.
Bump reveló un segundo bug real, independiente: ModuleNotFoundError:
openpyxl — faltaba en requirements.txt, enmascarado hasta entonces
porque el bug de width fallaba antes en la ejecución del script.
Ninguno de los dos se detectó con curl/chequeos HTTP (200 solo confirma
que el shell estático responde, no que el script corre — Streamlit
ejecuta el script real solo por WebSocket); se encontraron corriendo
AppTest vía docker exec dentro del contenedor real, no en un venv
local aparte. streamlit-aggrid también subió (1.0.5 → 1.2.1.post2):
la vieja fija altair<5, choca con streamlit>=1.63 (pide altair>=5).
Detalle de infraestructura completo en
streamlit-reportes/index.md. Commits en
streamlit-reportes: 3396a89 (fix width), 88da80e (bump streamlit),
4ec16b6 (bump aggrid), caa5f21 (fix openpyxl), 46d5309 (merge a
main).
2. streamlit_venv/ de este repo de exploración, pinneado a las mismas
versiones exactas que producción (streamlit==1.63.0,
pandas==2.2.3, sqlalchemy==2.0.36, pymssql==2.3.2,
streamlit-aggrid==1.2.1.post2, psycopg2-binary==2.9.10,
openpyxl==3.1.5) — para que un AppTest local aquí sea representativo
de lo que corre en Docker, no solo "casi igual". Este host (ctunlinux,
donde vive tanto la exploración como producción) no tiene Python 3.12
disponible en repos (solo 3.14, sin deadsnakes/uv/pyenv) — se
mantuvo Python 3.14 y se pinneó solo el lado de librerías, aceptando que
pandas y pymssql compilen desde source (sin wheel prebuilt para una
versión de Python tan nueva). Dos ajustes necesarios para que compilaran:
Cython==3.0.10 pinneado (la 3.3.0, que se instala por default, rompe el
parseo del .pyx de pymssql) y paquetes de sistema freetds-dev/
libkrb5-dev/libssl-dev/libpq-dev (vía apt, ninguno estaba
instalado). Detalle de buenas prácticas dev/producción (WSL vs.
ctunlinux, venv vs. Docker, cuándo pinnear) en el skill
trivasa-streamlit-reportes (actualizado en la misma sesión con los
gotchas de curl/docker run --env-file y el patrón de bump de
dependencia compartida vía rama).
2026-09-03 (continuación) — Promovido a producción: CONT-1/CONT-2
El reporte Streamlit de exploración (entrada anterior) se promovió a
streamlit-reportes (repo Docker, reportes.frento.com.mx/contabilidad)
como dos páginas nuevas, reemplazando el reporte "Layout de Gastos —
Reconciliación CECO" original (mismo path, mecanismo viejo de máscara de
reversión):
- CONT-1 — 6 orígenes, igual que el reporte de exploración sin nómina.
- CONT-2 — igual +
GASTO_REGISTRO_NOMINA(7mo origen), emparejado por join de texto(FOLIO, CECO, concepto normalizado)— no rank-pairing, no existe FK realTipo_Gasto → Cuenta_Contableni correspondencia posicional confiable para nómina. 6,591/6,591 exacto, cero huérfanos.
De paso, corregido un bug real heredado de queries/v03_detalle_cuenta_
centro_costo.sql en los 13 scripts del proyecto de origen Y en el código
de producción: Gr_Folio ya es el folio completo de display
("SS-NNNNNNN", sucursal 2 dígitos) — se reconstruía sin necesidad
concatenando Sc_Cve_Sucursal (4 dígitos, catálogo distinto). Corregido
en los 13 scripts, 11/12/layout_gastos_lib.py, y el código
promovido en streamlit-reportes.
También se separaron las conexiones de streamlit-reportes/contabilidad
(antes compartían get_engine_test()/TRIVASADB_TEST_* aunque Layout de
Gastos y Cruce XML usan bases distintas por diseño) —
get_engine_205()/get_engine_207(), prefijos TRIVASADB_205_*/
TRIVASADB_207_* explícitos. Cruce XML (ahora CONT-3) pasó de .205
temporal a .207 producción real de paso.
Detalle completo: docs/proyectos/streamlit-reportes/reportes/
layout-gastos-ceco-cont-1.md y -cont-2.md en este mismo repo.
Código: github.com/ehalso/streamlit-reportes, commit 64ceda2.
2026-09-03 — Rediseño sin máscara (11/12) + CONSUMO_INTERNO + reporte Streamlit
Reemplaza la mecánica de 01-09 (regla de reversión por folio: "si
IMPORTE del folio ≤ -$1, usar Abono") por lectura directa de Pd_Tipo
(1=Cargo, 2=Abono) en Poliza_Detalle — la regla de reversión existía
solo porque la query vieja (v03_detalle_cuenta_centro_costo.sql,
heredada de FlexMonster) colapsaba Pd_Tipo en dos columnas sumadas
(CARGO/ABONO) antes de que Python pudiera decidir cuál era la real;
al no colapsar, TIPO_MOVIMIENTO sale del dato, cero inferencia.
Mismo resultado exacto (validado no solo en agregado, sino fila por
fila, por posición dentro de (FOLIO, CECO), contra el CSV real del
diseño viejo): enero 2026 100.00% (7,691/7,691), enero-marzo 99.95%
(17,935/17,944) — cero diferencias en 6,891 y 15,647 combinaciones
(FOLIO, CENTRO) respectivamente.
Grano base establecido: (FOLIO, CECO, TIPO_GASTO) — 1 fila por
combinación, con CUENTA/CUENTA_DESCRIPCION/TIPO_MOVIMIENTO/IMPORTE
de la línea real de póliza que le tocó por rank-pairing. Confirmado que
nunca hay CARGO y ABONO a la vez en la misma fila, ni siquiera en el
residual de GASTO_RECLASIFICACION que mezcla signos en un mismo centro
(05-0182622, centro 103) — ahí caen en TIPO_GASTO distintos (uno
real, otro NaN por ser la línea sobrante sin match), nunca compiten por
la misma fila. Esto habilita pivotar a columnas CARGO/ABONO
separadas sin riesgo.
Dos bugs reales encontrados y corregidos en el camino (ninguno cambió el % final, pero sí la honestidad del mecanismo):
Gr_Tablaviene''(noNULL) paraGASTO_DIRECTO— confirmado en BD (610 folios/6,531 filas CECO, enero 2026). Antes se tapaba con un.fillna()de pandas después del hecho; ahora se normaliza en el SQL mismo (CASE WHEN ISNULL(Gr_Tabla,'')='' THEN 'GASTO_DIRECTO' ELSE Gr_Tabla END).- Al no colapsar
Pd_Tipo, aparecían 210 líneas de póliza (enero 2026) sinPd_Centro_Costoreal — la contrapartida normal de doble entrada (Abono aProveedor Nacional/Provisión de costo estándar/Préstamos a Terceros, cuentas de balance sin centro). El diseño viejo las descartaba por casualidad (VALOR=Cargopor defecto,Cargo=0en esa fila); el diseño nuevo las descartaba por otro accidente (groupbyconNaNen una llave de seguridad). Ahora se excluyen a propósito:AND pd.Pd_Centro_Costo <> ''en el SQL, documentado.
CONSUMO_INTERNO incorporado (12_reporte_base_ceco_consumo_interno.py)
con la misma filosofía (Pd_Tipo real, filtro de doble póliza rescatado
de 09). Resultado idéntico al ya documentado: 99.12% (3,045/3,072,
enero 2026), 27 filas sin cuadrar (23 folios) = hueco de datos ya
conocido. Confirmación nueva: TIPO_MOVIMIENTO sale 100% CARGO, 0
ABONO — antes 09 ni siquiera miraba Pd_Tipo=2 (su query solo
traía SUM(CASE WHEN Pd_Tipo=1...)), así que "son cargos" era un
supuesto no verificado; ahora es un hecho leído del dato real.
Reporte Streamlit (streamlit_app.py + layout_gastos_lib.py,
patrón del skill trivasa-streamlit-reportes): pivota TIPO_MOVIMIENTO+
IMPORTE a columnas CARGO/ABONO separadas, sidebar Periodo+Filtros,
tab de Reconciliación con semáforo por origen, descarga a Excel.
Validado con AppTest (sin excepciones, filtros interactivos probados)
antes de correr el servidor real. Corriendo en 127.0.0.1:8506
(streamlit_venv/ local, streamlit no estaba instalado en el sistema).
Código: conciliacion-master/layout-gastos/ (11_reporte_base_ceco.py,
12_reporte_base_ceco_consumo_interno.py, layout_gastos_lib.py,
streamlit_app.py) — sesión de trabajo en la misma máquina que
13_reconciliacion_nomina_ceco.py (ver sección GASTO_REGISTRO_NOMINA
de index.md, trabajo de otra sesión de Claude en paralelo).
Pendiente (sin cambios respecto a lo ya documentado, ver index.md):
filtro de doble póliza de CONSUMO_INTERNO sigue siendo LIKE sobre
texto libre; rama de capitalización de activo fijo no construida; los
~9 folios residuales (<0.06%) sin resolver, ahora visibles en el reporte
como filas con TIPO_GASTO/CUENTA = (residual) en vez de ocultarse.
2026-09-02 — Reconciliación a nivel CECO (centro de costo)
Nuevo sub-proyecto, código en
~/trivasa/proyectos-bi/conciliacion-master/layout-gastos/ (scripts
01-09, progresión completa documentada en el PROGRESS.md de ese
repo). Extiende la reconciliación Cargo/Abono de folio (ya al 100%, ver
2026-08-18 abajo) a nivel centro de costo: ¿el reparto por CECO de
Gasto_Registro_Control coincide con Poliza_Detalle? Diagnóstico de
calidad de dato, no la fuente del layout final.
- Resultado: 99.75% (10,762 filas, enero 2026, 6 orígenes) — 100% en
CONTROL_COMBUSTIBLE/ORDEN_COMPRA/VIAJE/GASTO_DIRECTO/GASTO_RECLASIFICACION, 99.12% enCONSUMO_INTERNO. - Llave real de consolidación de la póliza:
Tg_Cve_Tipo_Gasto× Centro de Costo, no el documento (Grd_ID) — hallazgo central de la sesión. Colapsar por documento da 93-94%; por tipo de gasto da 99%+. Confirmado con folio real deTELEFONIA(7 recibos → 53 filas por documento, 27 por tipo de gasto, exacto el número de líneas reales de la póliza). - Reversión (
IMPORTE ≤ -$1): confirmado también a nivel CECO — el Cargo cae en cuenta de Provisión/Pasivo sin centro de costo (una sola línea), el Abono trae el detalle real por centro. GASTO_RECLASIFICACION: el signo deGrc_Importepor línea (no si el folio completo es negativo) decide Cargo vs. Abono — extiende el caso especial 2 (que solo llegaba a nivel folio) a CECO.- Corrige la nota de "26 folios sin póliza de gasto real" de
CONSUMO_INTERNO(ver2026-08-18abajo): 21 de 23 revisados (rango enero-marzo) sí tienen su gasto contabilizado — capitalizado como activo fijo vía código de proyecto (U-NNNenGrd_Comentario→Pd_Referencia= ese código corto, no el folio → cuenta1210.xxx). Solo 2-3 folios genuinamente sin ninguna póliza. - Cruce con XML (
Comprobante_Digital): el problema de "Varios" comprobantes por folio es casi siempre artefacto de grano — 16 de 17 casos de enero 2026 desaparecen al bajar de folio a documento. - Detalle completo (incluye el residual conocido de <0.06%, y el origen
nuevo de bajo volumen
COMPROBACION_GASTOinvestigado) en elPROGRESS.mddel sub-proyecto.
2026-09-11 — HITO: layout de 60 columnas del Word construido, en conciliacion-cfdi
Retomado el objetivo original del proyecto (ver index.md) —
github.com/ehalso/conciliacion-cfdi, layout_gastos_poliza/ scripts
18-25. Requerimiento completo (nunca antes leído) rescatado de
~/backups/ y transcrito en layout_gastos_60col/legado/layout-gastos-
streamlit-claude/docs/00_requerimiento.md.
- Hallazgo metodológico: la reconstrucción vía
Poliza_Configuracion(sección "Rank-pairing vs. reconstrucción real", 2026-09-03) resuelve un problema (atribuirTipo_Gastopor línea) que este entregable no necesita. El join directoPd_Referencia=Gr_Folio(sin rank-pairing, sin regla de reversión) da Cargo/Abono exactos para los 5 orígenes +GASTO_RECLASIFICACIONcompleto (que la reconstrucción vía config no cubría — solo el lado Cargo). - Antes de llegar a esa conclusión, sí se generalizó
reconstruir_ config()de la config0450a las 44 configs reales del universo: 99.74%/99.39% de cobertura (folios/Cargo$), 100% cruzado contra rank-pairing. Bug real corregido en el camino:poliza_configuracion_lib.reconstruir_config()—Pcd_Condicionvacío se envolvía como(), SQL inválido (nunca visto porque solo se había probado con0450, que no tiene ningún renglón así). - Corrección importante:
05-0174748(CONSUMO_INTERNO), documentado en este proyecto como "sin póliza", en realidad tiene 4 pólizas — el filtro de doble póliza (memo/gasto real) se le escapa una tercera póliza tipo "Provisión" que no menciona "orden" en su comentario ni en la descripción de su config. Bug real, 1 de 9,090 combinaciones (FOLIO,CECO) en todo el trimestre — ver detalle enindex.md. - La rama de capitalización de activo fijo de
CONSUMO_INTERNO(resuelta 2026-08-08, nunca portada a producción) se reincorporó al layout: 45/47 folios recuperados, mismo resultado de agosto. - Probada y descartada la alternativa "filtrar por cuenta contable
en vez de comentario" para el doble-póliza de
CONSUMO_INTERNO— excluye 1,319 de 2,970 folios legítimos a escala completa (muchas cuentas de gasto real de este origen no son raíz6xxx). GASTO_REGISTRO_NOMINAinvestigado a fondo:Gr_Genera_Cxp='NO'en el 100% de sus folios (no pasa por CXP por diseño), sus "proveedores" son 4 cuentas de pasivo placeholder (RFC genéricoXAXX010101000, no proveedores reales), yComprobante_Digital.Cd_Tabla='NOMINA'dejó de alimentarse desde 2022 — la nómina 2026 se timbra en un sistema externo no integrado conTRIVASADB3.- Validado contra el reporte nativo de MPro (Excel real, no derivado): 7 orígenes, enero 2026, coincide exacto en folios/SUBTOTAL_NETO/ IMPUESTO/TOTAL; Cargo−Abono con diferencia de $56.59 sobre $39.47M (0.00014%, redondeo a centavos).
- Pendiente sin cerrar:
MONTO_COBRADOinfla ~41.6% porCxp_Foliocompartido entre folios (factura consolidada);FACTURA_REF/Descuentosin fuente; vista agrupada por cuenta (segunda vista del Word). Detalle completo enlayout_gastos_poliza/PROGRESS.mddeconciliacion-cfdi(entrada 2026-09-11).
2026-08-24
- Corrección: la nota de "el Abono siempre sale en 0" para
CONSUMO_INTERNO(línea de abajo, 2026-08-18) estaba incompleta — el Abono sí existe (1140.010.XXX.007"Salida Consumo Interno" por almacén), solo que esa línea no llevaPd_Referencia, así que un query filtrado porPd_Referencia = Gr_Folionunca la ve. Encontrado desdeconsumo-interno-fiforound2 siguiendo el join completoConsumo_Interno → Gasto_Registro → Poliza_Control → Poliza_Detalle. Detalle en consumo-interno-fifo/index.md.
2026-08-18
CONSUMO_INTERNOreconciliado: la documentación previa lo excluía del universo Cargo/Abono asumiendo "sin póliza por diseño" — falso, confirmado con datos reales. Descubierto: cada folio genera dos pólizas paralelas (movimiento de inventario vs. reconocimiento de gasto real), el join ingenuo sumaba el Cargo de ambas (~2x el importe nativo, Abono siempre 0). Filtrando a la póliza de gasto real: 99.12% de conciliación (2,944/2,970 folios, enero 2026), validado también a nivelGrd_IDyGrc_ID(reparto por centro de costo). Detalle completo en index.md.- Dos formas de aislar la póliza de gasto real dan el mismo resultado
exacto: filtro por cuenta contable (raíz
F/ empieza6/ descripción "costo estandar") vs. filtro porPoliza.Pl_Comentario(excluir "CUENTAS DE ORDEN"/"CTS ORDEN"/"CUENTA ORDEN") — el segundo, ya usado y validado antes en el proyectoconsumo_interno_trazabilidadpara el mismo problema, es más robusto (no depende de adivinar por código de cuenta). - Nuevo hallazgo de calidad de datos documentado en
docs/schema/calidad-de-datos.md—Poliza_Detalle.Pd_Referenciaes ambiguo paraCONSUMO_INTERNO(dos pólizas comparten la misma referencia). - Re-validada la conciliación v0.5 (casos especiales 1-4) para orígenes "normales" contra datos en vivo de enero 2026: 1,425/1,425 folios (100% dentro de $1), consistente con lo documentado históricamente.