Autor: Iker Acevedo¶
Fecha creacion: 2026-10-17 Estado: propuesta (pendiente de aprobación) Tipo: ejemplo de migración (vista piloto)
Ejemplo: migrar tienda/pedidos¶
tienda/pedidos es la vista piloto. Este documento muestra qué hay hoy, a dónde se mueve cada parte y en qué
orden, sin cambiar el comportamiento ni el backend.
1. Cómo está hoy (2026-10-17)¶
| Archivo | Tamaño | Contenido |
|---|---|---|
pedidos.component.ts |
1.555 líneas | HTTP, descompresión, reglas, filtros, conteos, exportación CSV, permisos por rol, estado de modales |
pedidos.component.html |
23 KB | Toda la vista |
pedidos.component.scss |
22 KB | 82 colores hexadecimales |
data-processor.worker.ts |
3 KB | Descomprime Zstd y oculta campos de costos según el rol |
estado.enum.ts |
— | Activo, enTransito, finalizados |
La vista tiene cuatro bloques visuales y usa modales que ya son componentes compartidos:
flowchart TB
A["Barra de acciones
crear, cotizar, importar, exportar, origen Logigho/Hoko"]
B["Filtros
fechas, tienda, producto, estado, filtro dinámico, anular guías"]
C["Resumen por estado
Activo, En tránsito, Finalizados + pestañas"]
D["Tabla de pedidos
app-tables"]
A --> B --> C --> D
M["Modales compartidos (src/app/components)
creación, importación, exportación, cotizador, generar guías, anular guías"]
2. A dónde va cada parte¶
Hoy en pedidos.component.ts |
Destino |
|---|---|
fetchTableData, fetchTableDataTienda, fetchTableDataCarga, buildUrl, processWithWorker y el worker |
repository/ |
generateColumns, generateRows, extractProducts, extractTransport, extractEstados, extractTiendas, matchEstado, formato de moneda de processData |
helpers/pedidos.reglas.ts |
getCountByStatus, getCountByCategory, getPercentageByCategory, getCountByStatuses, getPercentage |
helpers/ + computed en el store (hoy se recalculan en cada detección de cambios) |
applyFilters, filterGeneric, filterRowsDate, onDateRangeChange, handleFilter, filterStatus, selectTab, selectStatus |
Acción en el facade, cálculo en helpers/, estado en el store |
exportToCSV, exportToCSV_New |
facade.exportar() + armado del CSV en helpers/ (se unifican en una sola versión) |
canAnularGuias, showLogighoButton, showHokoButton, rolesAsignados |
facade.permisos(), calculado en un solo archivo (helpers/pedidos.permisos.ts) mientras el backend no los entregue |
showButtons / hideButtons (origen Interrapidísimo / Hoko) |
facade.cambiarOrigen(origen) |
open*Modal, close*Modal, toggle*Dropdown, onDocumentClick, isToday |
La versión visual (estado solo visual) |
3. Estructura final¶
views/tienda/pedidos/
├── pedidos.page.ts
├── pedidos.experiencias.ts
├── models/pedidos.models.ts # Pedido, FiltrosPedidos, ResumenEstados, PermisosPedidos, OrigenPedidos
├── repository/
│ ├── pedidos.repository.ts
│ └── data-processor.worker.ts
├── state/
│ ├── pedidos.store.ts
│ ├── pedidos.facade.ts
│ └── pedidos.facade.spec.ts
├── helpers/
│ ├── pedidos.reglas.ts
│ ├── pedidos.reglas.spec.ts
│ └── pedidos.permisos.ts
└── experiencias/
├── clasica/ # Lo de hoy, movido sin cambios visuales
│ ├── pedidos-clasica.component.*
│ └── partes/ # barra de acciones, filtros, resumen por estado
└── <experiencia-b>/ # Cuando exista su diseño
4. El facade de pedidos¶
@Injectable()
export class PedidosFacade {
private readonly repository = inject(PedidosRepository);
private readonly store = inject(PedidosStore);
readonly cargando = this.store.cargando;
readonly error = this.store.error;
readonly origen = this.store.origen; // 'Interrapidisimo' | 'Hoko'
readonly columnas = this.store.columnas;
readonly filas = this.store.filasVisibles; // computed: filas + filtros + estado seleccionado
readonly resumen = this.store.resumenPorEstado; // computed: conteos y porcentajes
readonly opciones = this.store.opcionesFiltro; // tiendas, productos, estados, transportadoras
readonly filtros = this.store.filtros;
readonly permisos = this.store.permisos; // { anularGuias, verCostos, origenLogigho, origenHoko }
async iniciar(): Promise<void> {
this.store.setPermisos(permisosPedidos());
await this.cambiarOrigen('Interrapidisimo');
}
async cambiarOrigen(origen: OrigenPedidos): Promise<void> {
this.store.setCargando(true);
try {
const [pedidos, tiendas] = await Promise.all([
this.repository.consultarPedidos(origen),
this.repository.consultarTiendas(origen),
]);
this.store.setDatos(origen, pedidos, tiendas);
} catch {
this.store.setError('No se pudieron obtener los pedidos.');
} finally {
this.store.setCargando(false);
}
}
aplicarFiltros(cambio: Partial<FiltrosPedidos>): void {
this.store.setFiltros(cambio);
}
seleccionarEstado(estado: EstadoPedido | null): void {
this.store.setEstadoSeleccionado(estado);
}
exportar(): void {
descargarArchivo(armarCsvPedidos(this.filas(), this.columnas()), 'pedidos.csv');
}
}
Todas las experiencias usan exactamente este facade. Lo único que cambia entre ellas es el HTML, el SCSS y el UI kit:
<!-- experiencias/clasica/pedidos-clasica.component.html (fragmento) -->
<c-card class="mb-4">
<c-card-body>
<app-pedidos-clasica-resumen
[resumen]="pedidos.resumen()"
(estadoSeleccionado)="pedidos.seleccionarEstado($event)" />
<app-tables [columns]="pedidos.columnas()" [rows]="pedidos.filas()" />
</c-card-body>
</c-card>
<!-- experiencias/<experiencia-b>/pedidos-<experiencia-b>.component.html (fragmento) -->
<section class="pedidos">
<app-b-indicadores [items]="pedidos.resumen()" (seleccion)="pedidos.seleccionarEstado($event)" />
<app-b-panel-filtros [opciones]="pedidos.opciones()" (cambio)="pedidos.aplicarFiltros($event)" />
<app-b-tabla [columnas]="pedidos.columnas()" [filas]="pedidos.filas()" />
</section>
5. Migración en PRs pequeños¶
Ninguno de estos PRs cambia lo que ve el usuario.
| PR | Qué hace | Riesgo | Cómo se valida |
|---|---|---|---|
| 1 | helpers/pedidos.reglas.ts con pruebas; el componente las llama |
Bajo | Pruebas + la vista se ve igual |
| 2 | repository/ y mover el worker |
Bajo | Mismas peticiones en la pestaña Network |
| 3 | store + facade + pedidos.permisos.ts; el componente usa solo el facade |
Medio | Pruebas del facade + recorrido de filtros, estados, exportar y anular |
| 4 | pedidos.page.ts + pedidos.experiencias.ts; mover el componente a experiencias/clasica/ |
Bajo | Pantallazos idénticos |
| 5 | Tokens en experiencias/clasica/ |
Bajo | Pantallazos idénticos |
Cuando exista el diseño de otra experiencia, su versión de pedidos es un PR más que solo agrega una carpeta y una
línea en pedidos.experiencias.ts.
6. Seguridad en esta vista¶
| Hoy (observado en código) | Objetivo |
|---|---|
La tienda consultada sale de sessionStorage y viaja como parámetro Tienda |
El backend obtiene las tiendas del usuario a partir del token, como APILambdaConsultarPedidos con UsuarioTienda |
Los costos (TotalFlete, TotalServicio, TotalSobreflete) llegan al navegador y el worker los oculta según el rol |
El backend no envía campos que el usuario no puede ver |
| Permisos decididos con nombres de rol en el componente | Primero un solo archivo (pedidos.permisos.ts); después, permisos entregados por el backend |
Este cambio de backend se trabaja en la línea de seguridad y solo toca pedidos.repository.ts.
Historial de cambios¶
| Fecha | Autor | Cambio |
|---|---|---|
| 2026-10-17 | Iker Acevedo | Creación de la propuesta |