feat: implement route planning dashboard including KPIs, user workload tracking, and route assignment management features
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { RouteState } from '../../core/route-state/route';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PlanningExportService {
|
||||
constructor(private routeState: RouteState) {}
|
||||
|
||||
export(): void {
|
||||
const assigned = this.routeState.getAssignedSnapshot();
|
||||
const unassigned = this.routeState.getNoAssignedSnapshot();
|
||||
const users = new Map<string, { nombre: string; rutas: number; distancia: number; trabajo: number; capacidad: number }>();
|
||||
|
||||
for (const route of assigned) {
|
||||
const key = String(route.json.usuario_id);
|
||||
const current = users.get(key) ?? {
|
||||
nombre: route.json.nombre_usuario,
|
||||
rutas: 0,
|
||||
distancia: 0,
|
||||
trabajo: 0,
|
||||
capacidad: Number(route.json.hora_laboral_semanal_usuario) || 0
|
||||
};
|
||||
current.rutas++;
|
||||
current.distancia += Number(route.json.distancia) || 0;
|
||||
current.trabajo += Number.parseFloat(String(route.json.horas_trabajo ?? '')) || 0;
|
||||
users.set(key, current);
|
||||
}
|
||||
|
||||
const total = assigned.length + unassigned.length;
|
||||
const totalDistance = assigned.reduce((sum, route) => sum + (Number(route.json.distancia) || 0), 0);
|
||||
const totalTravel = assigned.reduce((sum, route) => sum + (Number(route.json.horas_desplazamiento) || 0), 0);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
|
||||
const summary = [
|
||||
{ Indicador: 'PDVs totales', Valor: total },
|
||||
{ Indicador: 'PDVs asignados', Valor: assigned.length },
|
||||
{ Indicador: 'PDVs sin asignar', Valor: unassigned.length },
|
||||
{ Indicador: 'Cobertura (%)', Valor: total ? Number(((assigned.length / total) * 100).toFixed(1)) : 0 },
|
||||
{ Indicador: 'Usuarios activos', Valor: users.size },
|
||||
{ Indicador: 'Días planificados', Valor: new Set(assigned.map(route => route.json.dia)).size },
|
||||
{ Indicador: 'Distancia total (km)', Valor: Number(totalDistance.toFixed(2)) },
|
||||
{ Indicador: 'Desplazamiento total (h)', Valor: Number(totalTravel.toFixed(2)) }
|
||||
];
|
||||
|
||||
const assignedRows = assigned.map(route => ({
|
||||
'Usuario ID': route.json.usuario_id,
|
||||
Usuario: route.json.nombre_usuario,
|
||||
'PDV ID': route.json.pdv_id,
|
||||
PDV: route.json.nombre_pdv,
|
||||
Día: route.json.dia,
|
||||
'Distancia (km)': route.json.distancia,
|
||||
'Desplazamiento (h)': route.json.horas_desplazamiento,
|
||||
'Horas trabajo': route.json.horas_trabajo,
|
||||
'Horas disponibles': route.json.hora_laboral_semanal_usuario
|
||||
}));
|
||||
const unassignedRows = unassigned.map(route => ({
|
||||
'PDV ID': route.pdv_id,
|
||||
PDV: route.pdv_nombre,
|
||||
'Mínimo horas/semana': route.minimo_horas_semana,
|
||||
Latitud: route.latitud,
|
||||
Longitud: route.longitud,
|
||||
Motivo: route.motivo
|
||||
}));
|
||||
const userRows = Array.from(users, ([id, user]) => ({
|
||||
'Usuario ID': id,
|
||||
Usuario: user.nombre,
|
||||
Rutas: user.rutas,
|
||||
'Distancia (km)': Number(user.distancia.toFixed(2)),
|
||||
'Horas trabajo': Number(user.trabajo.toFixed(2)),
|
||||
'Capacidad (h)': user.capacidad,
|
||||
'Utilización (%)': user.capacidad ? Number(((user.trabajo / user.capacidad) * 100).toFixed(1)) : 0
|
||||
}));
|
||||
|
||||
this.appendSheet(workbook, summary, 'Resumen');
|
||||
this.appendSheet(workbook, assignedRows, 'Rutas asignadas');
|
||||
this.appendSheet(workbook, unassignedRows, 'PDVs sin asignar');
|
||||
this.appendSheet(workbook, userRows, 'Carga por usuario');
|
||||
|
||||
const output = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
||||
saveAs(new Blob([output], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), 'planificacion_rutas.xlsx');
|
||||
}
|
||||
|
||||
private appendSheet(workbook: XLSX.WorkBook, rows: object[], name: string): void {
|
||||
const sheet = XLSX.utils.json_to_sheet(rows.length ? rows : [{ Información: 'Sin registros' }]);
|
||||
XLSX.utils.book_append_sheet(workbook, sheet, name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user