Autor: Iker Acevedo¶
Fecha creacion: 2026-11-06
Estado: desarrollo (PR 637 en preprod desde 2026-11-08; no hay evidencia de que esté ya en main)
Actualizado: 2026-11-09 (PR 637)
Tipo: vista
Vista: Novedades, distancia a la dirección del pedido¶
Selector: app-novedades
Ubicación: src/app/views/logistica/novedades
Ruta: /app/logistica/novedades
Acceso: Autenticado
Historia: 027 · Spec: 012 · Repo: Angular SitioLogiGho
Marcas: observado (archivo citado), Iker (fecha), hipótesis. Código leído en
origin/preprod(PR 620, 628 y 637 mergeados; sin diferencias con la rama de trabajofeature/novedades-mejoras-visualesen esta carpeta el 2026-11-09). Aquí solo se documenta lo que agregó la historia 027; el resto de la vista (KPIs, motivos, evidencias) no se cubre. Si el código contradice una versión anterior de esta página, manda el código (ver Observaciones).
¿Qué hace?¶
Para las guías de Interrapidísimo muestra a cuántos metros por calle (en carro) de la dirección del pedido marcó el mensajero el intento, con un semáforo, y permite comparar el punto del mensajero con la dirección en un mapa. Los datos los calcula la función Distancias; la vista solo los lee. Envía y Servientrega no traen coordenada: la columna no aplica y queda vacía.
Columna de distancia y chip¶
- Columna Distancia en la tabla (escritorio) y chip equivalente en las tarjetas (móvil). Observado:
novedades.component.html. - El chip lleva color según los metros por calle (
novedades.rules.ts,METROS_VERDE_MAX = 250,METROS_AMARILLO_MAX = 500):
| Metros | Chip |
|---|---|
| ≤ 250 | Verde |
| 251 a 500 | Amarillo |
| > 500 | Rojo |
- Se usa el color que calculó el backend si es válido; si no, sale de los metros (
colorDeDistancia). El formato es es-CO con punto de miles (1.240 m). - Cada chip tiene
aria-labelcon la distancia y el color.
Etiquetas sin metros¶
| Etiqueta | Cuándo |
|---|---|
| Sin dirección | El intento no trae coordenada (SinGeo). También se muestra si el último motivo no tiene geoUrl y aún no hay distancia |
| Dirección por validar | DireccionNoEncontrada o BajaConfianza (incluye el rojo no confirmado) |
| Error al calcular | Error, o Calculada sin metros |
| Pendiente | Documento sin el campo (aún no procesado por la función) |
Hay además un botón con el contador de guías «sin dirección» que activa ese filtro.
Filtro de distancia (5 opciones)¶
Desplegable «Distancia» (app-filtro-desplegable), con contador por opción y chip de filtro activo:
| Opción | Valor interno |
|---|---|
| ≤ 250 m | verde |
| 251–500 m | amarillo |
| > 500 m | rojo |
| Sin dirección | sin-direccion |
| Dirección por validar | por-validar |
«Todas» (valor vacío) deja pasar todo, incluidas Envía y Servientrega. Observado: OPCIONES_DISTANCIA y cumpleFiltroDistancia en novedades.rules.ts.
Detalle: pestañas «Mensajero» y «Comparar con dirección»¶
En la tarjeta del mapa de una guía de Inter, el detalle ofrece dos pestañas (PestanaMapa, con roles ARIA tab/tabpanel). Desde el PR 637 el mapa es propio
(components/mapa-ruta/mapa-ruta.component.ts, Maps JavaScript API; observado):
- Mensajero: un marcador con el logo de Inter en el punto donde marcó el intento, sin ruta.
- Comparar con dirección: pin rojo en la dirección del pedido, marcador de Inter en el punto del mensajero y una línea recta punteada de referencia. No se pide ninguna ruta a Google desde el navegador
(decisión de Iker 2026-11-08: la llave del navegador no tenía Routes y cada apertura era un cobro). La distancia por calle ya la guarda el backend y el recorrido real se ve con el botón Abrir ruta en Maps
(solo enlaces
https://www.google.com/maps…; viene deDistanciaUltima.UrlRuta). Muestra además el chip grande con la distancia, «Por calle», «En línea recta» y la precisión de la dirección. - Con
conRuta = false(dirección por validar) se marcan los dos puntos pero no se dibuja la línea. - Si no hay llave, el script no carga o Google rechaza la llave (
gm_authFailure), la vista cae al iframemaps.google.com/maps?q=…&output=embedde siempre. - Si el intento no tiene coordenada: «Sin dirección: Inter no envió la ubicación de este intento». Con varios intentos, tocar uno cambia el mapa a esa ubicación.
- La distancia de cada intento se busca por su
HuellaGeo(y la fecha si hay varias con el mismo punto). - Pendiente (M-276): dibujar el recorrido real por calle exigiría guardar el trazado (polyline) en el backend y recalcular ~8 000 rutas con costo extra de Google; no se decidió.
Dirección del repartidor (v8)¶
La dirección en texto del punto del mensajero sale de SeguimientoNovedadesInter.DireccionMensajero.Texto cuando su HuellaGeo coincide con la del último punto del intento (costo USD 0). El mapper
(direccionMensajeroDesdeDoc) solo la acepta con Estado = Calculada, texto no vacío y una huella válida; el texto se muestra como texto, nunca como HTML. Para intentos anteriores al último se mantiene la geocodificación
inversa del navegador (helpers/direccion-inversa.ts: una llamada por coordenada, guardada en memoria; sin llave, no se muestra). Decisión y alternativas: ADR-006.
Llave de navegador¶
- Vive en
environment.googleMapsEmbedKey(environment.tsyenvironment.preprod.ts). Aunque se llama «Embed», hoy sirve para cargar la Maps JavaScript API y la geocodificación del navegador. - Es pública por diseño (viaja en el navegador) y va restringida por dominio (referente) y por API. No se escribe su valor en la documentación. Es distinta de la llave de servidor de Distancias (
GOOGLE_MAPS_API_KEY, cifrada). - Pendiente (se arrastra del 2026-11-06): confirmar con Iker que la restricción por dominio incluye el dominio de producción.
- M-269:
google.maps.Markerestá deprecado; migrar aAdvancedMarkerElement(requiere Map ID).
Estados Logigho e Interrapidísimo en el panel (v8)¶
El panel de detalle de una guía de Inter muestra dos estados distintos (observado: novedades.component.html):
| Etiqueta | Campo | Qué es |
|---|---|---|
| Estado Logigho | PedidosInter.Estado (se lee con el detalle) |
El estado del pedido en Logigho (Intento de entrega, Devolución Completada, Pagada, …). — si no hay pedido; … mientras carga el detalle |
| Estado Interrapidísimo | UltimaRespuesta.estado_actual, con respaldo en UltimoMotivo.EstadoInter |
El estado actual de la guía en Inter (el mismo que usan los Excel de Slack) |
Para Envía y Servientrega solo se muestra «Estado».
Cerradas de Inter: periodo y cursor por _id (v8, Iker 2026-11-08)¶
- Periodo elegible: 7 días, 30 días, 90 días o «Todo el historial» (
PERIODOS_CERRADAS,PeriodoCerradas = 7 | 30 | 90 | 0; 0 = todo). Lo ya traído no se vuelve a pedir: un periodo mayor cubre a los menores (cubre()). - Cursor por clave (
_id) en lugar de$skip: cada tanda pide_id > último recibido, ordenada por_id; no se vuelve a recorrer ni ordenar lo anterior. Tandas de 3 000 (TANDA_CERRADAS; medido contra prod 2026-11-08: 1 000 → ~0,5 s, 2 000 → ~0,8 s, 3 000 → ~1,1 s, 5 000 → ~1,7 s por tanda). Tope de seguridad de 100 tandas. - Tres cursores paralelos (
MAX_CURSORES = 3) para «Todo el historial» sin otro filtro y con al menos 6 000 filas: un conteo y un corte por_idreparten el recorrido en tramos iguales (~4,9 s para 17 124 cerradas, contra ~6,9 s con un solo cursor). Si algo falla al calcular los cortes, se usa un cursor. - Lista liviana: sin
UltimaRespuestaniHistorial(los intentos se piden con el detalle) y solo los subcampos que usa la vista; el cruce de intentos conPedidosInteres perezoso, en tandas de 400. - Sin
$facet,$lookupni$unionWith(DocumentDB). Todo pormetodoGenerico. - Atajo «Traer todo el historial con esta distancia» (
traerCerradasDeDistancia): trae del servidor las cerradas de los rangos de distancia elegidos. M-277: su regla de «Sin dirección» (DistanciaUltima.Estado = 'SinGeo') no coincide del todo con la regla local.
Cifras que dependen del usuario (M-279)¶
Las consultas filtran por las tiendas asignadas al usuario (sessionStorage tiendas_asignadas; si trae «Todas», no se filtra; observado: novedades.repository.ts). Por eso las cifras de la vista no coinciden
con las de los reportes de Slack, que traen todas las tiendas (el 2026-11-08 la cuenta de prueba tenía 205 tiendas). La nota del filtro de distancia aclara que los conteos son de las guías cargadas (activas y cerradas del periodo elegido).
Filtro de distancia de selección múltiple (v8)¶
El desplegable «Distancia» ahora es de selección múltiple ([multiple]="true", elegirDistancias): se pueden combinar, por ejemplo, «> 500 m» y «Dirección por validar». Los conteos por opción se calculan sobre lo cargado.
Las opciones y sus valores internos no cambian (tabla de arriba).
Lecturas de Mongo (metodoGenerico)¶
Todo el front sigue por metodoGenerico (Iker 2026-11-05). Observado: helpers/novedades.repository.ts.
| Dato | Dónde se pide |
|---|---|
DistanciaUltima (incluye Modo y HuellaGeo) |
Proyección del listado de SeguimientoNovedadesInter |
UltimaRespuesta.estado_actual (como EstadoActual) |
Listado (solo ese subcampo) |
Distancias, DireccionPedido (latitud, longitud, tipo de ubicación, coincidencia parcial y estado) y DireccionMensajero (Estado, Texto, HuellaGeo) |
Proyección del detalle |
PedidosInter.Estado, Direccion, Ciudad, Departamento, UrlGuia |
Detalle (Estado Logigho) |
No se pide UltimaRespuesta completa ni los datos del destinatario para la distancia.
Excel de la vista¶
La descarga de la vista (novedades-excel.helper.ts) suma cuatro columnas al final (solo Inter; vacías en Envía y Servientrega):
| Columna | Contenido |
|---|---|
Distancia (m) |
Metros redondeados, si está calculada |
Color |
Verde, Amarillo o Rojo |
Estado distancia |
Calculada o la etiqueta («Sin dirección», «Dirección por validar», …) |
Ruta en Maps |
Solo enlaces https://www.google.com/maps…; si no, vacío |
El archivo no lleva datos del destinatario (nombre, teléfono, dirección).
Reglas (helpers/novedades.rules.ts)¶
| Función | Qué calcula |
|---|---|
colorPorMetros / colorDeDistancia |
Color por metros o el del backend |
categoriaDistancia |
no-aplica, pendiente, calculada, sin-direccion, por-validar, error |
etiquetaDistancia |
Texto de la celda y del Excel |
cumpleFiltroDistancia |
¿La guía pasa el filtro? |
distanciaDeIntento |
Distancia que corresponde a un intento (por HuellaGeo y fecha) |
urlRutaSegura |
Valida que el enlace sea de https://www.google.com/maps |
urlEmbedRuta |
URL del Embed API, o null sin llave o con coordenadas inválidas |
precisionDireccion |
Texto de la precisión de la geocodificación |
Pruebas: novedades.rules.spec.ts, novedades.store.spec.ts, novedades.mapper.spec.ts, novedades-excel.helper.spec.ts, novedades.component.spec.ts (81 specs, build de PreProd; spec 012 2026-11-05).
Seguridad¶
- Los enlaces solo se aceptan si empiezan por
https://www.google.com/maps(hostwww.google.com); cualquier otro se descarta. - Las URLs del iframe se sanean una sola vez por URL (caché) y solo se arman con coordenadas válidas.
- El Excel no incluye datos personales.
Historial de cambios¶
| Fecha | Autor | Cambio |
|---|---|---|
| 2026-11-05 | Iker Acevedo | PR 620 (preprod): columna, filtro, etiquetas, pestañas y Excel |
| 2026-11-06 | Iker Acevedo | Rama feature/novedades-llave-embed: llave de navegador para el mapa embebido |
| 2026-11-09 | Iker Acevedo (redactado con IA) | PR 637 (2026-11-08): dirección del repartidor desde el backend, mapa sin Routes API, periodo de cerradas y cursor por _id, filtro de distancia múltiple, panel con Estado Logigho e Interrapidísimo |
Observaciones¶
- La vista consume los campos que escribe Distancias; mientras no corra el relleno de producción, las guías de prod mostrarán «Pendiente» (hipótesis).
- Los números del Excel de la vista son los mismos que ve el usuario en pantalla, no los de los reportes de Slack (esos se rigen por Reglas de los reportes).
- Pendiente: revisión visual de Iker (vista y Excel) en PreProd.
- Discrepancia corregida (2026-11-09): esta página decía que la pestaña «Comparar» usaba el Maps Embed API en modo
directions(iframe con ruta). El código actual (PR 637) usa el mapa propio con línea recta y no pide rutas.urlEmbedRutasigue ennovedades.rules.tsy el componente aún lo calcula (novedades.component.ts, línea 963); es el respaldo del iframe cuando falla el mapa propio (hipótesis: no se probó cada rama). - Comentario obsoleto en el código:
listarCerradasInterdice «Orden estable porFechaCierreyGuiapara que$skipno repita»; el cuerpo hoy usa cursor por_id(mejora de limpieza, sin impacto funcional). - M-278: «Todo el historial» usa 3 cursores en paralelo; no se probó bajo carga ni se midió el tamaño comprimido. Si molesta a DocumentDB, volver a 1 cursor (~6,9 s).
- M-280: los PR del front a
mainrepetirán el conflicto «both added» porque los PR 625 y 628 entraron por squash apreprod; el PR 637 se resolvió conmerge -s ourstras comprobar quepreprodera idéntico al commit 94a5db33 de la rama (opción B de Iker 2026-11-08). - Los PR de esta vista: 620, 625, 628 y 637 (Angular
preprod).