From 8aefb2575df8d18a75d96ebd873ec73c732a4b4c Mon Sep 17 00:00:00 2001 From: karol Date: Sat, 20 Jun 2026 17:18:18 +0000 Subject: [PATCH] Implementacion modulo aprobaciones --- backend/src/app.ts | 3 + .../modules/approvals/approval.controller.ts | 302 ++++++++++++++++++ .../modules/approvals/approval.interface.ts | 17 + .../src/modules/approvals/approval.model.ts | 46 +++ .../src/modules/approvals/approval.routes.ts | 42 +++ .../src/modules/approvals/approval.service.ts | 209 ++++++++++++ .../fuel-requests/fuelRequest.model.ts | 5 + .../fuel-requests/fuelRequest.routes.ts | 1 - 8 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 backend/src/modules/approvals/approval.controller.ts create mode 100644 backend/src/modules/approvals/approval.interface.ts create mode 100644 backend/src/modules/approvals/approval.model.ts create mode 100644 backend/src/modules/approvals/approval.routes.ts create mode 100644 backend/src/modules/approvals/approval.service.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index e3aa06d..7d7fb2f 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -10,6 +10,8 @@ import fuelPumpRouter from "./modules/fuel_pumps/fuelPump.routes"; import vehicleConfigRouter from "./modules/vehicles-config/vehiclesConfigs.routes"; import vAssigRouter from "./modules/vehicle-assignments/vehicleassig.routes"; import fuelRequestRouter from "./modules/fuel-requests/fuelRequest.routes"; +import approvalrouter from "./modules/approvals/approval.routes"; + @@ -27,6 +29,7 @@ app.use("/api/pump", fuelPumpRouter); app.use("/api/vconfig", vehicleConfigRouter); app.use("/api/vAssig", vAssigRouter); app.use("/api/fuelRequest", fuelRequestRouter); +app.use("/api/approvals", approvalrouter); app.use(express.urlencoded({ diff --git a/backend/src/modules/approvals/approval.controller.ts b/backend/src/modules/approvals/approval.controller.ts new file mode 100644 index 0000000..02cf51a --- /dev/null +++ b/backend/src/modules/approvals/approval.controller.ts @@ -0,0 +1,302 @@ +import { + Request, + Response +} +from "express"; + +import { + ApprovalService +} +from "./approval.service"; + +const service = +new ApprovalService(); + +export class ApprovalController { + + /** + * Obtener todas las aprobaciones + */ + async getAll( + req: Request, + res: Response + ) { + + try { + + const approvals = + await service.getAll(); + + res.json( + approvals + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al obtener aprobaciones", + error + }); + + } + + } + + /** + * Obtener aprobación por ID + */ + async getById( + req: Request, + res: Response + ) { + + try { + + const approval = + await service.getById( + req.params.id as string + ); + + if (!approval) { + + return res.status(404).json({ + message: + "Aprobación no encontrada" + }); + + } + + res.json( + approval + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al obtener aprobación", + error + }); + + } + + } + + /** + * Crear aprobación + */ + async create( + req: Request, + res: Response + ) { + + try { + + const approval = + await service.create( + req.body + ); + + res.status(201).json( + approval + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al crear aprobación", + error + }); + + } + + } + + /** + * Aprobar solicitud + */ + async approve( + req: Request, + res: Response + ) { + + try { + + const approval = + await service.approve( + + req.params.id as string, + + req.body.approvedBy, + + req.body.comments + + ); + + res.json( + approval + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al aprobar solicitud", + error + }); + + } + + } + + /** + * Rechazar solicitud + */ + async reject( + req: Request, + res: Response + ) { + + try { + + const approval = + await service.reject( + + req.params.id as string, + + req.body.approvedBy, + + req.body.comments + + ); + + res.json( + approval + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al rechazar solicitud", + error + }); + + } + + } + + /** + * Buscar por estado + */ + async getByStatus( + req: Request, + res: Response + ) { + + try { + + const approvals = + await service.getByStatus( + req.params.status as string + ); + + res.json( + approvals + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al buscar aprobaciones", + error + }); + + } + + } + + /** + * Buscar por tipo de referencia + */ + async getByReferenceType( + req: Request, + res: Response + ) { + + try { + + const approvals = + await service.getByReferenceType( + req.params.referenceType as string + ); + + res.json( + approvals + ); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al buscar aprobaciones", + error + }); + + } + + } + + /** + * Eliminar aprobación + */ + async delete( + req: Request, + res: Response + ) { + + try { + + const approval = + await service.delete( + req.params.id as string + ); + + if (!approval) { + + return res.status(404).json({ + message: + "Aprobación no encontrada" + }); + + } + + res.json({ + message: + "Aprobación eliminada correctamente" + }); + + } + catch (error) { + + res.status(500).json({ + message: + "Error al eliminar aprobación", + error + }); + + } + + } + +} \ No newline at end of file diff --git a/backend/src/modules/approvals/approval.interface.ts b/backend/src/modules/approvals/approval.interface.ts new file mode 100644 index 0000000..33744b6 --- /dev/null +++ b/backend/src/modules/approvals/approval.interface.ts @@ -0,0 +1,17 @@ +import { ObjectId } from "mongoose"; + +export interface IApproval { + + referenceType:string; + + referenceId:ObjectId; + + approvedBy?:ObjectId; + + approvedDate?:Date; + + status:string; + + comments?:string; + +} \ No newline at end of file diff --git a/backend/src/modules/approvals/approval.model.ts b/backend/src/modules/approvals/approval.model.ts new file mode 100644 index 0000000..7f843d0 --- /dev/null +++ b/backend/src/modules/approvals/approval.model.ts @@ -0,0 +1,46 @@ +import mongoose from "mongoose"; + +const approvalSchema = +new mongoose.Schema({ + + referenceType:{ + type:String, + required:true + }, + + referenceId:{ + type:mongoose.Schema.Types.ObjectId, + required:true + }, + + approvedBy:{ + type:mongoose.Schema.Types.ObjectId, + ref:"User" + }, + + approvedDate:{ + type:Date + }, + + status:{ + type:String, + enum:[ + "PENDIENTE", + "APROBADO", + "RECHAZADO" + ], + default:"PENDIENTE" + }, + + comments:{ + type:String + } + +},{ + timestamps:true +}); + +export default mongoose.model( + "Approval", + approvalSchema +); \ No newline at end of file diff --git a/backend/src/modules/approvals/approval.routes.ts b/backend/src/modules/approvals/approval.routes.ts new file mode 100644 index 0000000..78ab006 --- /dev/null +++ b/backend/src/modules/approvals/approval.routes.ts @@ -0,0 +1,42 @@ +import { Router } from "express"; + +import { ApprovalController } from "./approval.controller"; + +import { authMiddleware } from "../auth/auth.middleware"; + +const approvalrouter = Router(); + +const controller = new ApprovalController(); + +//Obtener todas las aprobaciones + +approvalrouter.get( "/", authMiddleware, controller.getAll); + +//Obtener aprobación por ID + +approvalrouter.get( "/:id", authMiddleware, controller.getById); + +//Crear aprobación + +approvalrouter.post( "/", authMiddleware, controller.create); + +//Aprobar solicitud + +approvalrouter.put( "/:id/approve", authMiddleware, controller.approve); + +//Rechazar solicitud + +approvalrouter.put( "/:id/reject", authMiddleware, controller.reject); + +//Buscar por estado +approvalrouter.get("/status/:status", authMiddleware, controller.getByStatus); + +//Buscar por tipo + +approvalrouter.get("/type/:referenceType", authMiddleware, controller.getByReferenceType); + +//Eliminar aprobación + +approvalrouter.delete("/:id", authMiddleware, controller.delete); + +export default approvalrouter; \ No newline at end of file diff --git a/backend/src/modules/approvals/approval.service.ts b/backend/src/modules/approvals/approval.service.ts new file mode 100644 index 0000000..8f8c084 --- /dev/null +++ b/backend/src/modules/approvals/approval.service.ts @@ -0,0 +1,209 @@ +import ApprovalModel from "./approval.model"; + +import FuelRequestModel from "../fuel-requests/fuelRequest.model"; + +import { IApproval } from "./approval.interface"; + +export class ApprovalService { + + //Obtener todas las aprobaciones + + async getAll() { + + return await ApprovalModel + .find() + .populate("approvedBy") + .sort({ + createdAt: -1 + }); + + } + + //Obtener aprobación por ID + + async getById( + id: string + ) { + + return await ApprovalModel + .findById(id) + .populate("approvedBy"); + + } + + //Crear aprobación + + async create( + data: IApproval + ) { + + const approval = + new ApprovalModel(data); + + return await approval.save(); + + } + + // + async approve( + approvalId: string, + approvedBy: string, + comments?: string + ) { + + // Buscar aprobación + const approval = + await ApprovalModel.findById( + approvalId + ); + + if (!approval) { + + throw new Error( + "Aprobación no encontrada" + ); + + } + + // Actualizar aprobación + approval.status = + "APROBADO"; + + approval.approvedBy = + approvedBy as any; + + approval.approvedDate = + new Date(); + + approval.comments = + comments || ""; + + await approval.save(); + + if ( + approval.referenceType === + "FuelRequest" + ) { + + await FuelRequestModel + .findByIdAndUpdate( + + approval.referenceId, + + { + status: + "APROBADO" + }, + + { + new: true + } + + ); + + } + + return approval; + + } + + //Rechazar solicitud + + async reject( + approvalId: string, + approvedBy: string, + comments?: string + ) { + + // Buscar aprobación + const approval = + await ApprovalModel.findById( + approvalId + ); + + if (!approval) { + + throw new Error( + "Aprobación no encontrada" + ); + + } + + // Actualizar aprobación + approval.status = + "RECHAZADO"; + + approval.approvedBy = + approvedBy as any; + + approval.approvedDate = + new Date(); + + approval.comments = + comments || ""; + + await approval.save(); + + if ( + approval.referenceType === + "FuelRequest" + ) { + + await FuelRequestModel + .findByIdAndUpdate( + + approval.referenceId, + + { + status: + "RECHAZADO" + }, + + { + new: true + } + + ); + + } + + return approval; + + } + + //Obtener aprobaciones por estado + + async getByStatus( + status: string + ) { + + return await ApprovalModel.find({ + status + } as any); + + } + + //Obtener aprobaciones por tipo de referencia + + async getByReferenceType( + referenceType: string + ) { + + return await ApprovalModel.find({ + referenceType + } as any); + + } + + //Eliminar aprobación + + async delete( + id: string + ) { + + return await ApprovalModel + .findByIdAndDelete(id); + + } + +} \ No newline at end of file diff --git a/backend/src/modules/fuel-requests/fuelRequest.model.ts b/backend/src/modules/fuel-requests/fuelRequest.model.ts index 2b5fa3b..104887f 100644 --- a/backend/src/modules/fuel-requests/fuelRequest.model.ts +++ b/backend/src/modules/fuel-requests/fuelRequest.model.ts @@ -18,6 +18,11 @@ const fuelRequestSchema = new mongoose.Schema({ required:true }, + vehicleId:{ + type:Number, + required:true + }, + routeId:{ type:Number, required:true diff --git a/backend/src/modules/fuel-requests/fuelRequest.routes.ts b/backend/src/modules/fuel-requests/fuelRequest.routes.ts index 549f918..064a114 100644 --- a/backend/src/modules/fuel-requests/fuelRequest.routes.ts +++ b/backend/src/modules/fuel-requests/fuelRequest.routes.ts @@ -16,7 +16,6 @@ fuelRequestRouter.get( //Obtener requisición por ID - fuelRequestRouter.get( "/:id", authMiddleware,