import fuelRequestModel from "./fuelRequest.model"; import { IFuelRequest } from "./fuelRequest.interface"; import { generateRequestNumber } from "../../utils/requestNumber"; import vehiclesConfigModel from "../vehicles-config/vehiclesConfig.model"; import anomalyModel from "../anomalies/anomaly.model"; import { IAnomaly } from "../anomalies/anomaly.interface"; export class FuelRequestService { //Obtener todas las requisiciones async getAll() { return await fuelRequestModel .find() .populate("fuelPumpId") .populate("fuelTypeId") .populate("createdBy") .populate("authorizedBy") .sort({ createdAt: -1 }); } //Obtener reauisición por ID async getById( id:string ) { return await fuelRequestModel .findById(id) .populate("fuelPumpId") .populate("fuelTypeId") .populate("createdBy") .populate("authorizedBy"); } //Crear una nueva requisición async create ( data:IFuelRequest ){ ///Validar kilometraje if(data.currentMileage < data.previousMileage) { throw new Error ("El kilometraje no puede ser menor que el anterior"); } //Validar que los galones sean mayor a 0 if(data.gallonQuantity <= 0){ throw new Error ("La cantidad de galones debe ser mayor que 0"); } //Generar número automático data.requestNumber = generateRequestNumber(); //Calcular distancia recorrida data.distanceTraveled = data.currentMileage - data.previousMileage; //Calcular monto total data.totalAmount = data.gallonQuantity * data.pricePerGallon; //Estado inicial data.status = "PENDIENTE"; //Crear documento const request = new fuelRequestModel(data); //Guardar en DB const fuelRequest = await request.save(); //Validaciones automáticas await this.detectAnomalies( fuelRequest ); return fuelRequest; } async update( id:string, data:Partial ){ //Buscar la requisición actual const request = await fuelRequestModel.findById(id); if(!request){ return null; } const previousMileage = data.previousMileage ?? request.previousMileage; const currentMileage = data.currentMileage ?? request.currentMileage; const gallonQuantity = data.gallonQuantity ?? request.gallonQuantity; const pricePerGallon = data.pricePerGallon ?? request.pricePerGallon; //Recalcular distancia recorrida data.distanceTraveled = currentMileage - previousMileage; //Validar kilometraje if(data.distanceTraveled < 0){ throw new Error("El kilometraje actual no puede ser menor que el anterior"); } //Recalcular monto total data.totalAmount = gallonQuantity * pricePerGallon; //Actualizar la requisición const updateRequest = await fuelRequestModel.findByIdAndUpdate( id, data, { new:true } ); return updateRequest; } //Eliminar requisición async delete( id:string ){ return await fuelRequestModel .findByIdAndDelete(id); } private async detectAnomalies( fuelRequest:any ){ await this.validateMileage( fuelRequest ); await this.validateFuelConsumption( fuelRequest ); await this.validateTankCapacity( fuelRequest ); await this.validatePreviousFuelRequest( fuelRequest ); } private async validateMileage( data:any ){ //Kilometraje menor que el anterior if ( data.currentMileage < data.previousMileage ){ await this.createAnomaly( data, "KILOMETRAJE FUERA DE RANGO", "El kilometraje actual es menor que el anterior" ); } //Odomentro en mal estado if(data.currentMileage === data.previousMileage){ await this.createAnomaly( data, "ODOMETRO EN MAL ESTADO", "El kilometraje no cambió respecto al anterior" ); } //Diferencia mayor a 500km if(data.distanceTraveled>500){ await this.createAnomaly( data, "KILOMETRAJE FUERA DE RANGO", "La distancia recorrida supera los 500 kilometros" ); } } //Validar rendimiento de combustible private async validateFuelConsumption( data:any ){ //Evitar división entre 0} if( data.gallonQuantity <=0 ){ return; } //Calcular distancia recorrida const distance = data.currentMileage - data.previousMileage; //Calcular rendimiento const performance = distance/ data.gallonQuantity; //Bajo rendimiento if (performance < 10){ await this.createAnomaly( data, "RENDIMIENTO BAJO", `El vehículo tiene un rendimiento de ${performance.toFixed(2)} km por galón.` ) } } //Validar capacidad del tanque private async validateTankCapacity( data:any ){ const vehicleConfig = await vehiclesConfigModel.findOne({ vehicleId:data.vehicleId }); //Sino existe configuración no validar if(!vehicleConfig){ return; } //Validar capacidad del tanque if( data.gallonQuantity > vehicleConfig.tankCapacity ){ await this.createAnomaly( data, "CONSUMO ELEVADO", `Se solicitaron ${data.gallonQuantity} galones, pero la capacidad del tanque es de ${vehicleConfig.tankCapacity} galones.` ) } } // Validar contra la carga anterior private async validatePreviousFuelRequest( data:any ) { // Buscar la última requisición del vehículo const previousRequest = await fuelRequestModel .findOne({ vehicleId: data.vehicleId, _id: { $ne: data._id } }) .sort({ createdAt: -1 }); // Si no existe una carga anterior, salir if (!previousRequest) { return; } // Calcular diferencia de galones const difference = Math.abs( data.gallonQuantity - previousRequest.gallonQuantity ); // Si la diferencia es mayor a 5 galones if (difference > 5) { await this.createAnomaly( data, "CONSUMO ELEVADO", `La carga actual difiere en ${difference} galones respecto a la carga anterior.` ); } } //Crear anomalía private async createAnomaly( fuelRequest: any, anomalyType: IAnomaly["anomalyType"], description: string ) { return await anomalyModel.create({ vehicleId: fuelRequest.vehicleId, employeeId: fuelRequest.employeeId, fuelRequestId: fuelRequest._id, anomalyType, description, detectedBy: "SISTEMA", status: "ABIERTA", comments: "Anomalía generada automáticamente por el sistema." }); } }