# #25150 — Reportes y `mb_backoffice`: quién depende de permisos globales

Antes de reducir `macrobase@%` a `mb_backoffice` (#25023, mbinv#1333) hay que saber qué reportes del sitio central
(`macrobase/mbinv`) recorren esquemas de otros clientes. Con `mb_backoffice` (sólo `mbinvmacrobase`, `_temporalesmacrobase`
y 6 tablas de `mbinvbitacoramb`) MySQL muestra en `INFORMATION_SCHEMA` sólo lo que la cuenta puede ver, así que un reporte
así **no falla: omite clientes sin avisar**. Medido el 16-oct-2026 en el QA `demomacrobase` (misma forma de permisos):
`INFORMATION_SCHEMA.TABLES` con `maetie` en esquemas `%mbinv%` devuelve **1** esquema con esa cuenta y **117** con una global.

Este documento: lo medido (producción sólo lectura, QA), lo corregido y lo que queda para decidir.

## Resumen

| Reporte | ¿Depende de una cuenta global? | Qué se hizo |
|---|---|---|
| DTEs (`Apis::getDTEs`) | **Sí** (leía `maetie`, `documentos_maestro`, `hmaetra` de cada esquema) | **Corregido** en este PR: cada proyecto contesta por su base (patrón de #25131/#25144) |
| Tamaño de Bases de Datos | **No hoy** | Sin cambio de código; ver abajo |
| Clientes Sin Actividad | No (cuenta propia en otro servidor) | Sin cambio; **hallazgo de seguridad** aparte |
| Detalle de BacksUps | No (cuenta propia en otro servidor) | Sin cambio |
| Consolidado de Errores SAM | No usa cuenta global, pero **ya está roto** | Sin cambio; decisión de MANUEL |
| Reporte de Errores | Igual que el anterior | Sin cambio; decisión de MANUEL |
| Cuadre Transacciones | No (sólo tablas del propio sitio) | Sin cambio |

## DTEs — lo corregido

- `Apis::getDTEs` ya no consulta `INFORMATION_SCHEMA` ni arma SQL contra esquemas ajenos. La lista de clientes sale de
  `Apis::getProyectos()` (la misma de errores globales).
- Ruta nueva `POST /admin/api/dtes_propios` (`AdministracionController::dtesPropiosAction`): devuelve los DTEs de la base de
  **este** sitio, con la misma consulta de antes pero sin calificar el esquema y con parámetros (el NIT ya no se pega en el SQL).
  `security.yml` la abre **sólo a 127.0.0.1**; desde cualquier otra IP exige sesión de administrador.
- La central (`DtesGlobales::recoger`) llama a cada proyecto por la ruta interna (`LlamadaInternaProyecto`, sin reintento
  público porque la ruta lo rechazaría) con tiempo máximo de 600 s por proyecto (el de Reportes Globales).
- **Lo que no se alcanza se dice:** la respuesta lleva `sin_revisar` («proyecto: motivo»). El reporte DTEs
  (`Reportes/DTEs.php`) **no se entrega incompleto**: si hay proyectos sin revisar, falla y nombra los primeros 10.
  Se excluyen (igual que errores globales) `demogasolinera` y los proyectos que apuntan a la base de pruebas.
  **Decisión de CLAUDE, reversible:** antes la cuenta global lo veía todo, así que un cliente caído habría salido con datos; ahora sale error.
- **Datos parciales = error** (hallazgo de Codex): `Apis::getDTEs` devuelve `error: true` si hay proyectos sin revisar (con `dtes` y
  `sin_revisar` en el arreglo); lo consumen igual el reporte DTEs y `GET /admin/feldtes`. La ruta pública `feldtes` responde sólo
  «no se pudo revisar N proyecto(s)» y manda el detalle (clientes, errores de MySQL, URL internas) al log.
- Tope total de 1,800 s para el recorrido (los proyectos van en fila): los que faltan quedan en `sin_revisar` («sin tiempo»).
- `tipos`/`tiendas` se validan como números antes de pegarlos en el `IN (...)` (misma exposición que la consulta vieja, ahora cerrada).
- `/admin/feldtes` es **pública** desde antes de este PR (para INFILE) y ahora provoca ~134 llamadas internas por petición: queda
  como riesgo conocido; tiquete aparte para autenticarla o limitarla a un NIT (no se cambió el contrato sin saber quién la usa).
- `LlamadaInternaProyecto::paraEsteServidor()` unifica el armado de la llamada (también lo usa errores globales).
- Ya no se usa `Apis::getDTEs` para otra cosa: llamadores medidos con `grep`: `Reportes/DTEs.php` y `GET /admin/feldtes`
  (ruta pública para INFILE, sin cambio de contrato: sólo se agrega `sin_revisar`).

### QA (16-oct-2026, oci-mbinv, sitio temporal `qa25150cliente`, copia de `demosally`)

- **Paridad exacta** contra la consulta vieja (esquema calificado, misma base): 77 filas y 426,669 DTEs en ambos.
- Desde `127.0.0.1`: HTTP 200 con datos. Desde la IP pública del servidor: **302 al login**.
- Central (`DtesGlobales`, clase real) con un proyecto real y uno inexistente: devuelve el real y el otro queda en `sin_revisar`.
  Filtro de NIT con y sin guion: mismas 77 filas; NIT inexistente: 0 filas.
- `Apis::getDTEs` por `GET /admin/feldtes` en ese QA: lista los 134 proyectos del directorio, HTTP 200 en 0.37 s y los 134
  quedan en `sin_revisar` con su motivo, **porque el `web_url_publica` de ese QA es una IP, no el de producción**.
  **No probado de punta a punta con el `web_url_publica` real de la central.**
- Pruebas automáticas nuevas (`DtesGlobalesTest`, 15): la ruta no es pública, `getDTEs` no vuelve a listar esquemas, la central
  junta/no pierde proyectos mudos, el NIT va como parámetro. **Vistas fallar**: con el `Apis.php` y el `security.yml` viejos fallan 2.

### Riesgos conocidos

- La consulta de `hmaetra` sobre un rango largo (el reporte se pide desde 1900) ahora corre dentro de la petición de cada
  cliente: puede tardar en uno grande. Tiempo máximo 600 s; si se agota, queda en `sin_revisar`.
- La consulta de `maetie` usa `GROUP_CONCAT` sin `GROUP BY` con columnas sueltas (igual que la vieja): depende del `sql_mode`
  del servidor, que no cambió (MySQL 8.0.46, medido en QA).
- Las llamadas son en fila: 134 proyectos × consulta liviana; el tiempo total no se midió con los clientes reales.
- Es **código nuevo en cada cliente**: la ruta sólo existe en los sitios donde ya se actualizó el código. Un cliente con código
  viejo queda en `sin_revisar` (404). Hay que desplegar el código a todos **antes** de reducir `macrobase@%`.

## Tamaño de Bases de Datos — no depende hoy

Lee `conexiones_report` (servidor, usuario, clave, base, puerto) y consulta `INFORMATION_SCHEMA.TABLES` **con esa cuenta
guardada**, no con la de la central. **Medido en producción (sólo lectura, 16-oct-2026): `conexiones_report` tiene 0 filas en
los 124 esquemas donde existe.** Sin filas la lista de opciones sale vacía y el reporte no se puede ejecutar. No se cambió
código: si alguien carga una fila con una cuenta global, el reporte vería sólo lo que esa cuenta vea (y no depende de `macrobase@%`).

## Los otros cuatro

- **Clientes Sin Actividad:** se conecta a `132.226.40.48:3309`, base `macrobas_versiones`, con un usuario y una clave
  **escritos en el código** (`ClientesSinActividad.php`). No usa `macrobase@%`. ⚠️ **Hallazgo de seguridad aparte:** una clave
  versionada en el repositorio (no se repite aquí). Merece tiquete propio (sacarla a `parametros_backoffice`/entorno y rotarla).
- **Detalle de BacksUps (`SoporteBackUp`):** usa `backup_ip` / `backup_base` / `backup_usuario` / `backup_clave` de
  `parametros_backoffice`: en producción, servidor externo `74.208.206.88`, base `mbbackups`, usuario `equivalenza`.
  No depende de `macrobase@%`.
- **Consolidado de Errores SAM y Reporte de Errores:** ambos leen `_temporales.bitacorasam202304` (esquema y mes **fijos en
  el código**). Medido en producción: el esquema `_temporales` existe y tiene `bitacorasam202608`, `202609` y `202610`,
  pero **no `202304`**: los dos reportes **ya fallan hoy con cualquier cuenta** («tabla no existe»). Además `_temporales`
  **no** está entre los permisos de `mb_backoffice` (sólo `_temporalesmacrobase`), así que aunque se arreglara el mes
  seguirían fallando tras reducir permisos. **Decisión de MANUEL:** (A) retirarlos (nadie puede estar usándolos), o
  (B) repararlos —leer los meses del rango— lo que exige darle a `mb_backoffice` `SELECT` sobre `_temporales.bitacorasam*`
  (cambio de privilegios, no hecho). Uso real de estos dos reportes: **no medido**.
- **Cuadre Transacciones:** sólo `hmaetra`, `hdetmed`, `hdettra`, `maetie`, `documentos_maestro` sin calificar (la base de
  quien lo ejecuta). Se lee `database_name` y `temporales` pero no entran a la consulta. No depende.

## No cubierto

- Inventario completo de lecturas **entre esquemas** del sitio `macrobase/mbinv` más allá de estos reportes (por ejemplo,
  `ProcesosController` tiene referencias literales a `_temporales` en las líneas ~5268, ~6693 y ~16456, sin revisar a fondo).
  `INFORMATION_SCHEMA` aparece en ~20 archivos; la mayoría consulta la base propia.
- #25017 (SAM sinfirma/eventos/reportes global) es de otra ventana.
- No se ejecutó el SQL de #1333 ni se cambió ningún privilegio. Producción: sólo lecturas.
- QA temporal: sitio `qa25150cliente` en oci-mbinv (≈600 MB, sin costo adicional de nube). Se desmonta al cerrar el PR.
