Plan — Manual de usuario + recorrido guiado ("Explicación")
Date: 2026-09-12 Status: Debatido y decidido con Carlos (2026-09-12) — listo para ejecutar
Qué se construye
Dos piezas que se complementan a propósito:
- Manual de usuario — contenido escrito, con un solo origen (Markdown) y dos salidas: dentro de la app filtrado por los permisos del usuario, y tres PDF imprimibles para capacitación.
- Recorrido guiado — el botón "Explicación": un tour in-app que señala los controles de cada pantalla y explica el flujo, corriendo sobre la UI real.
La división de trabajo entre los dos es deliberada: el manual lleva los conceptos y las reglas de negocio (por qué el pago no se bloquea, qué significa "pagada pero incompleta", por qué fondear una caja no descuenta el prefondeo del país); el tour lleva lo visual (dónde está cada botón y en qué orden se usa). Así el manual no depende de capturas para explicarse, y el tour nunca queda desactualizado porque corre sobre la pantalla real.
Punto de partida
- La ruta ya existe:
/documentacion/manual-usuario, sinmeta.modulea propósito (visible para cualquier autenticado), hoy apuntando a un placeholder "en construcción" (frontend/src/views/ManualUsuarioView.vue, 29 líneas). Verdoc/plans/2026-07-25-seccion-documentacion.md. meStoreya exponemodules: Record<string, ModulePermission>concanRead/canWrite/canDelete— el filtro por permisos no hay que construirlo.- El frontend no tiene ninguna dependencia de Markdown todavía.
- El CI ya levanta Postgres 17 y corre
php artisan migrate+php artisan test(.github/workflows/ci.yml), pero no levanta la app entera ni el emulador de Firebase. - No existe nada de tours/onboarding en el frontend.
Decisiones tomadas — no reabrir sin una razón nueva
| # | Decisión | Por qué |
|---|---|---|
| D1 | Tres recorridos filtrados por permisos efectivos, no por rol nominal | La matriz de permisos es editable por el Admin y un usuario puede tener roles distintos por país. Un manual "para Supervisor" le mentiría a un Supervisor con permisos recortados. Se filtra con meStore.canRead(modulo) |
| D2 | Camino híbrido: un origen Markdown, dos salidas | Autoría cómoda y versionada con el código, pero filtrado real por permisos dentro de la app |
| D3 | Tres PDF, uno por rol | Mismo contenido y mismo filtro que la app; la entrada cambia: en la app son los permisos vivos, en el build es la matriz seedeada de cada rol |
| D4 | Capturas automatizadas en CI (Playwright) | Nunca quedan viejas. Es la pieza de infraestructura del paquete, por eso va última |
| D5 | Tour seco — no siembra datos | Señala los controles que existen siempre (buscador, botones, menú) y describe con texto qué pasaría al usarlos. Funciona aunque el usuario no tenga ninguna transacción cargada |
| D6 | Tour a demanda + automático en el primer login | El botón "?" siempre disponible; el automático necesita guardar estado por usuario en el backend |
Estructura del contenido
Un archivo por módulo de permiso, con frontmatter — espejo exacto de lo que ya declara el router:
doc/manual/
00-primeros-pasos.md (sin modulo → siempre visible: login, sidebar, país/PDV activo)
10-pago.md modulo: pago
20-transacciones.md modulo: transacciones
30-caja.md modulo: caja
31-caja-apertura-cierre.md modulo: apertura_cierre_caja
32-caja-fondeo.md modulo: fondeo_caja
40-prefondeo.md modulo: prefondeo
41-prefondeo-pais.md modulo: prefondeo_pais
50-dashboard.md modulo: dashboard
60-reportes.md modulo: reportes
70-tipo-cambio.md modulo: tipo_cambio
80-admin-usuarios.md modulo: abm_usuarios
81-admin-asignacion.md modulo: abm_asignacion_roles
82-admin-paises.md modulo: abm_paises
83-admin-estaciones.md modulo: abm_estaciones
84-admin-configuracion.md modulo: abm_configuracion
90-auditoria.md modulo: auditoriaFrontmatter mínimo:
yaml
---
titulo: Caja
modulo: caja
orden: 30
---Reglas del contenido:
- El router declara
meta: { module: 'caja' }y el manual declaramodulo: caja. Misma clave, mismo filtro. Un archivo sinmoduloes siempre visible — la misma convención que ya usa el router para rutas sinmeta.module. - Cuando una pantalla mezcla permisos (Caja se opera con
cajapero se fondea confondeo_caja, que Backoffice no tiene), se resuelve partiendo el archivo, nunca con condicionales dentro del Markdown. Sin parser custom, sin lógica escondida en el contenido. - El contenido sale del Análisis Funcional que ya existe (
doc/functional/), reescrito en lenguaje de usuario final: el análisis explica por qué se decidió algo, el manual explica qué tiene que hacer la persona.
Entregas
E1 — El manual dentro de la app
- Escribir los archivos de
doc/manual/(contenido real, no esqueleto). - Reemplazar el placeholder de
ManualUsuarioView.vue: trae los.mdconimport.meta.glob('../../doc/manual/*.md', { query: '?raw', eager: true }), parsea el frontmatter, filtra pormeStore.canRead(modulo)y renderiza. - Agregar el renderer de Markdown como dependencia del frontend. La ruta ya es lazy, así que el parser solo se descarga cuando alguien abre el manual — no entra en el bundle del resto.
- Índice lateral con las secciones visibles para ese usuario, y deep-link por sección (
/documentacion/manual-usuario#caja) para poder enlazar desde cada pantalla. - Terminado cuando: un Backoffice ve solo sus secciones, un Supervisor las suyas y un Admin todas, sin tocar código al cambiar un permiso desde el ABM.
E2 — Los tres PDF (sin capturas todavía)
- Script de build que toma los mismos
.md, los filtra con la matriz seedeada de cada rol (RoleSeeder::MATRIX) y arma un PDF por rol. - Cada PDF imprime en la portada la fecha de generación y con qué matriz se generó — es un artefacto estático y va a quedar viejo si alguien edita permisos después.
- Botón "Descargar PDF" en la pantalla del manual, que baja el PDF del rol que corresponde.
- Terminado cuando: los tres PDF se generan desde un comando y el contenido coincide con lo que cada rol ve en la app.
E3 — El recorrido guiado
- Runner de tours con pasos declarativos, un archivo por flujo (
frontend/src/tours/pago.ts,caja.ts,prefondeo.ts, ...), declarados con la misma clave de módulo que el manual. - Anclaje con atributos
data-tour="buscar-mtcn"explícitos en los elementos. Nunca selectores de clases de Tailwind — el primer refactor de UI los rompería en silencio. - Soporte para tours que cruzan pantallas (el flujo de pago va
/pago→ buscar → pagar → datos → documentos): el runner tiene que poder navegar de ruta y retomar el recorrido. - Pasos que apuntan a un control que el usuario no tiene por permiso: se saltean.
- Botón "?" en cada pantalla que ofrece las dos cosas, leer el manual y ver el recorrido, filtradas por el mismo permiso.
- Disparo automático la primera vez: columna nueva por usuario en el backend (tipo
onboarding_completed_at) y una forma de reiniciarlo desde el perfil. - Terminado cuando: los recorridos de Pago, Caja y Prefondeo corren de punta a punta sin datos cargados, y ninguno ejecuta una acción real.
E4 — Capturas automatizadas en CI (la pieza de infra)
- Seeder de datos de demostración: país, PDV, un usuario por rol, transacciones en
ACCEPTED/PAID/CANCELLED, una caja abierta con movimientos. - Job de CI que levanta backend + frontend + emulador de Firebase (el login es contra Firebase;
start-dev.shya lo levanta en dev, hay que replicarlo en el pipeline). - Playwright entra con cada rol, navega las pantallas y guarda las capturas.
- El build del PDF las inyecta en las secciones que corresponden.
- Terminado cuando: un cambio de UI que rompa una pantalla se refleja en la captura del siguiente build, sin que nadie saque una foto a mano.
Riesgos y cosas a tener presente
- El tour explica, nunca ejecuta. No debe disparar el botón de pagar ni ninguna acción que mueva plata o toque documentos. En un sistema con dinero real y PII de población vulnerable, un tour que "hace click para mostrarte" no es aceptable.
- E4 es lo más caro y lo más frágil — es un entorno completo corriendo en CI, no solo Playwright. Por eso va última: E1-E3 entregan el manual y el recorrido completos sin depender de ella. Si E4 se demora, el paquete igual sirve.
- El seeder de E4 habilita el "tour con datos" si alguna vez se quiere revisar D5. Es el mismo trabajo hecho una sola vez.
- El manual se desactualiza solo si no se lo mantiene. Conviene sumar al checklist de PR: si el cambio toca una pantalla, revisar si toca su archivo del manual.
doc/yfrontend/son directorios hermanos: elimport.meta.globdel frontend cruza al directorio de al lado. Hay que verificarserver.fs.allowen dev.
Preguntas abiertas (menores, no bloquean arrancar)
- Tono e idioma — el análisis funcional está en rioplatense. Para Guatemala probablemente convenga español neutro en el manual y el tour. A confirmar con el cliente.
- Quién escribe el contenido — propuesta: borrador derivado del Análisis Funcional existente, revisado por Carlos antes de publicarlo.
- Dónde vive el PDF — propuesta: se descarga desde la propia pantalla del manual, no se publica como archivo suelto (sigue el mismo criterio de acceso que el resto del sistema).

