Files
rutero/route-planner/src/app/shared/services/planning-export.service.ts
T

107 lines
4.8 KiB
TypeScript

import { Injectable } from '@angular/core';
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';
import { RouteState } from '../../core/route-state/route';
import { sequenceRoutes, summarizeDailyRoutes } from '../utils/route-sequencing.utils';
@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 dailyRoutes = summarizeDailyRoutes(assigned);
const totalDistance = dailyRoutes.reduce((sum, route) => sum + route.distanceKm, 0);
const totalTravel = dailyRoutes.reduce((sum, route) => sum + route.travelHours, 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 = sequenceRoutes(assigned).map(segment => ({
'Usuario ID': segment.route.json.usuario_id,
Usuario: segment.route.json.nombre_usuario,
'PDV ID': segment.route.json.pdv_id,
PDV: segment.route.json.nombre_pdv,
Día: segment.route.json.dia,
'Distancia (km)': segment.route.json.distancia,
'Desplazamiento (h)': segment.route.json.horas_desplazamiento,
'Horas trabajo': segment.route.json.horas_trabajo,
'Horas disponibles': segment.route.json.hora_laboral_semanal_usuario,
'Orden visita': segment.order,
'Origen del tramo': segment.previousPdvName ?? `Casa de ${segment.route.json.nombre_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]) => {
const utilization = user.capacidad ? (user.trabajo / user.capacidad) * 100 : 0;
return {
'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 (%)': Number(utilization.toFixed(1)),
Estado: utilization > 100 ? 'Excede capacidad' : utilization > 90 ? 'Riesgo' : 'OK'
};
});
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');
this.appendSheet(workbook, dailyRoutes.map(route => ({
Usuario: route.userName,
Día: route.day,
Paradas: route.routes.length,
'Distancia secuencial (km)': Number(route.distanceKm.toFixed(2)),
'Desplazamiento secuencial (h)': Number(route.travelHours.toFixed(2)),
'Carga total (h)': Number(route.totalHours.toFixed(2)),
'Capacidad diaria (h)': Number(route.capacityHours.toFixed(2)),
Estado: route.exceedsCapacity ? 'Excede capacidad' : 'OK'
})), 'Rutas diarias');
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);
}
}