Modulo reportes
This commit is contained in:
@ -3,6 +3,10 @@ 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
|
||||
|
||||
@ -98,4 +102,209 @@ export class FuelRequestService {
|
||||
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."
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
252
backend/src/modules/reports/report.controller.ts
Normal file
252
backend/src/modules/reports/report.controller.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import { Request, Response } from "express";
|
||||
import { ReportService } from "./report.service";
|
||||
|
||||
const service = new ReportService();
|
||||
|
||||
export class ReportController {
|
||||
|
||||
async getSummary(
|
||||
req:Request,
|
||||
res:Response
|
||||
){
|
||||
|
||||
try{
|
||||
|
||||
const summary = await service.getSummary();
|
||||
res.json(summary);
|
||||
|
||||
}catch(error){
|
||||
res.status(500).json({
|
||||
message:"Error al obtener reportes",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Reporte de Fuel Request
|
||||
async getFuelRequestsReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getFuelRequestReport();
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte de requisiciones.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Reporte de Facturas Externas
|
||||
async getExternalInvoicesReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getExternalInvoiceReport();
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte de facturas externas.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte de LPG
|
||||
async getLpgReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getLpgReport();
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte de LPG.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte de Anomalías
|
||||
async getAnomaliesReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getAnomaliesReport();
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte de anomalías.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte de Lecturas de Bomba
|
||||
async getPumpReadingsReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getPumpReadingsReport();
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte de lecturas de bomba.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte por Vehículo
|
||||
async getVehicleReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getVehicleReport(
|
||||
Number(req.params.vehicleId)
|
||||
);
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte por vehículo.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte por Empleado
|
||||
async getEmployeeReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getEmployeeReport(
|
||||
Number(req.params.employeeId)
|
||||
);
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte por empleado.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte por Departamento
|
||||
async getDepartmentReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getDepartmentReport(
|
||||
Number(req.params.departmentId)
|
||||
);
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte por departamento.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Reporte por Ruta
|
||||
async getRouteReport(
|
||||
req: Request,
|
||||
res: Response
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
const report =
|
||||
await service.getRouteReport(
|
||||
Number(req.params.routeId)
|
||||
);
|
||||
|
||||
res.status(200).json(report);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
res.status(500).json({
|
||||
message: "Error al generar el reporte por ruta.",
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
29
backend/src/modules/reports/report.routes.ts
Normal file
29
backend/src/modules/reports/report.routes.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Router } from "express";
|
||||
import { ReportController } from "./report.controller";
|
||||
import { authMiddleware } from "../auth/auth.middleware";
|
||||
|
||||
const reportRouter = Router();
|
||||
const controller = new ReportController();
|
||||
|
||||
|
||||
reportRouter.get("/summary", authMiddleware, controller.getSummary);
|
||||
|
||||
reportRouter.get("/fuel-request", authMiddleware, controller.getFuelRequestsReport);
|
||||
|
||||
reportRouter.get("/external-invoice", authMiddleware, controller.getExternalInvoicesReport);
|
||||
|
||||
reportRouter.get("/LPG", authMiddleware, controller.getLpgReport);
|
||||
|
||||
reportRouter.get("/anomalies", authMiddleware, controller.getAnomaliesReport);
|
||||
|
||||
reportRouter.get("/pump-readings", authMiddleware, controller.getPumpReadingsReport);
|
||||
|
||||
reportRouter.get("/vehicle/:vehicleId", authMiddleware, controller.getVehicleReport);
|
||||
|
||||
reportRouter.get("/employee/:employeeId", authMiddleware, controller.getEmployeeReport);
|
||||
|
||||
reportRouter.get("/department/:departmentId", authMiddleware, controller.getDepartmentReport);
|
||||
|
||||
reportRouter.get("/route/:routeId", authMiddleware, controller.getRouteReport);
|
||||
|
||||
export default reportRouter;
|
||||
198
backend/src/modules/reports/report.service.ts
Normal file
198
backend/src/modules/reports/report.service.ts
Normal file
@ -0,0 +1,198 @@
|
||||
import fuelRequestModel from "../fuel-requests/fuelRequest.model";
|
||||
import externalInvoiceModel from "../external-invoices/externalInvoice.model";
|
||||
import lpgModel from "../lpg/lpg.model";
|
||||
import approvalModel from "../approvals/approval.model";
|
||||
import anomalyModel from "../anomalies/anomaly.model";
|
||||
import pumpReadingModel from "../pump-reading/pumpReading.model";
|
||||
|
||||
|
||||
export class ReportService{
|
||||
|
||||
async getSummary(){
|
||||
//Total de requisiciones
|
||||
const fuelRequests = await fuelRequestModel.countDocuments();
|
||||
|
||||
//Total de facturas externas
|
||||
const externalInvoices = await externalInvoiceModel.countDocuments();
|
||||
|
||||
//Total de requisiciones LPG
|
||||
const lpgRequests = await lpgModel.countDocuments();
|
||||
|
||||
//Total de galones solicitados
|
||||
const pendingApprovals = await approvalModel.countDocuments({
|
||||
status:"PENDIENTE"
|
||||
});
|
||||
|
||||
//Total de anomalías abiertas
|
||||
const openAnomalies = await anomalyModel.countDocuments({
|
||||
status:"ABIERTA"
|
||||
})
|
||||
|
||||
//Tootal de lecturas de bomba
|
||||
const pumpReadings = await pumpReadingModel.countDocuments();
|
||||
|
||||
const gallons = await fuelRequestModel.aggregate([
|
||||
{
|
||||
$group:{
|
||||
_id:null,
|
||||
totalGallons:{
|
||||
$sum: "$gallonQuantity"
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
//Total invertido
|
||||
const totalCost = await fuelRequestModel.aggregate([
|
||||
{
|
||||
$group:{
|
||||
_id:null,
|
||||
totalAmount:{
|
||||
$sum: "$totalAmount"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
]);
|
||||
|
||||
return{
|
||||
fuelRequests,
|
||||
externalInvoices,
|
||||
lpgRequests,
|
||||
pendingApprovals,
|
||||
openAnomalies,
|
||||
pumpReadings,
|
||||
|
||||
totalGallons:
|
||||
gallons.length > 0
|
||||
? gallons[0].totalGallons
|
||||
:0,
|
||||
|
||||
|
||||
totalCost:
|
||||
totalCost.length > 0
|
||||
? totalCost[0].totalAmount
|
||||
:0
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async getFuelRequestReport(){
|
||||
|
||||
return await fuelRequestModel
|
||||
.find()
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.populate("createdBy")
|
||||
.populate("authorizedBy")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getExternalInvoiceReport(){
|
||||
|
||||
return await externalInvoiceModel
|
||||
.find()
|
||||
.populate("fuelTypeId")
|
||||
.populate("createdBy")
|
||||
.populate("authorizedBy")
|
||||
.sort({
|
||||
invoiceDate:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getLpgReport(){
|
||||
|
||||
return await lpgModel
|
||||
.find()
|
||||
.populate("requestedBy")
|
||||
.populate("approvedBy")
|
||||
.sort({
|
||||
requestDate:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getAnomaliesReport(){
|
||||
|
||||
return await anomalyModel
|
||||
.find()
|
||||
.populate("reportedBy")
|
||||
.populate("resolvedBy")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getPumpReadingsReport(){
|
||||
|
||||
return await pumpReadingModel
|
||||
.find()
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.populate("createdBy")
|
||||
.sort({
|
||||
readingDate:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getVehicleReport(
|
||||
vehicleId:number
|
||||
){
|
||||
|
||||
return await fuelRequestModel
|
||||
.find({
|
||||
vehicleId
|
||||
})
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getEmployeeReport(
|
||||
employeeId:number
|
||||
){
|
||||
|
||||
return await fuelRequestModel
|
||||
.find({
|
||||
employeeId
|
||||
})
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getDepartmentReport(
|
||||
departmentId:number
|
||||
){
|
||||
|
||||
return await fuelRequestModel
|
||||
.find({
|
||||
departmentId
|
||||
})
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
|
||||
async getRouteReport(
|
||||
routeId:number
|
||||
){
|
||||
|
||||
return await fuelRequestModel
|
||||
.find({
|
||||
routeId
|
||||
})
|
||||
.populate("fuelPumpId")
|
||||
.populate("fuelTypeId")
|
||||
.sort({
|
||||
createdAt:-1
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user