Files
sistema_combustible/backend/src/modules/fuel-requests/fuelRequest.service.ts

339 lines
7.7 KiB
TypeScript

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<IFuelRequest>
){
//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."
});
}
}