CRUD y validaciones de modulo de facturas externas
This commit is contained in:
@ -11,6 +11,7 @@ import vehicleConfigRouter from "./modules/vehicles-config/vehiclesConfigs.route
|
|||||||
import vAssigRouter from "./modules/vehicle-assignments/vehicleassig.routes";
|
import vAssigRouter from "./modules/vehicle-assignments/vehicleassig.routes";
|
||||||
import fuelRequestRouter from "./modules/fuel-requests/fuelRequest.routes";
|
import fuelRequestRouter from "./modules/fuel-requests/fuelRequest.routes";
|
||||||
import approvalrouter from "./modules/approvals/approval.routes";
|
import approvalrouter from "./modules/approvals/approval.routes";
|
||||||
|
import invoicerouter from "./modules/external-invoices/externalInvoice.routes";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ app.use("/api/vconfig", vehicleConfigRouter);
|
|||||||
app.use("/api/vAssig", vAssigRouter);
|
app.use("/api/vAssig", vAssigRouter);
|
||||||
app.use("/api/fuelRequest", fuelRequestRouter);
|
app.use("/api/fuelRequest", fuelRequestRouter);
|
||||||
app.use("/api/approvals", approvalrouter);
|
app.use("/api/approvals", approvalrouter);
|
||||||
|
app.use("/api/exterinvoice", invoicerouter);
|
||||||
|
|
||||||
|
|
||||||
app.use(express.urlencoded({
|
app.use(express.urlencoded({
|
||||||
|
|||||||
@ -73,12 +73,14 @@ export class ApprovalController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error:any) {
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
message:
|
message: "Error al aprobar solicitud",
|
||||||
"Error al obtener aprobación",
|
error: error.message,
|
||||||
error
|
stack: error.stack
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,328 @@
|
|||||||
|
import { Request, Response } from "express";
|
||||||
|
|
||||||
|
import { ExternalInvoiceService } from "./externalInvoice.service";
|
||||||
|
|
||||||
|
const service = new ExternalInvoiceService();
|
||||||
|
|
||||||
|
export class ExternalInvoiceController {
|
||||||
|
|
||||||
|
//Obtener todas las facturas
|
||||||
|
|
||||||
|
async getAll(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoices =
|
||||||
|
await service.getAll();
|
||||||
|
|
||||||
|
res.json(invoices);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al obtener facturas",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Obtener factura por ID
|
||||||
|
|
||||||
|
async getById(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.getById(
|
||||||
|
req.params.id as string
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
|
||||||
|
return res.status(404).json({
|
||||||
|
message:
|
||||||
|
"Factura no encontrada"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(invoice);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al obtener factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Crear factura
|
||||||
|
|
||||||
|
async create(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.create(
|
||||||
|
req.body
|
||||||
|
);
|
||||||
|
|
||||||
|
res.status(201).json(
|
||||||
|
invoice
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al crear factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Actualizar factura
|
||||||
|
|
||||||
|
async update(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.update(
|
||||||
|
|
||||||
|
req.params.id as string,
|
||||||
|
req.body
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
|
||||||
|
return res.status(404).json({
|
||||||
|
message:
|
||||||
|
"Factura no encontrada"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(invoice);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al actualizar factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Aprobar factura
|
||||||
|
|
||||||
|
async approve(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.approve(
|
||||||
|
|
||||||
|
req.params.id as string,
|
||||||
|
req.body.approvedBy
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
|
||||||
|
return res.status(404).json({
|
||||||
|
message:
|
||||||
|
"Factura no encontrada"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(invoice);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al aprobar factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rechazar factura
|
||||||
|
|
||||||
|
async reject(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.reject(
|
||||||
|
|
||||||
|
req.params.id as string,
|
||||||
|
req.body.approvedBy,
|
||||||
|
req.body.comments
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
|
||||||
|
return res.status(404).json({
|
||||||
|
message:
|
||||||
|
"Factura no encontrada"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(invoice);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al rechazar factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Buscar por estado
|
||||||
|
|
||||||
|
async getByStatus(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoices =
|
||||||
|
await service.getByStatus(
|
||||||
|
req.params.status as string
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json(invoices);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al buscar facturas",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Buscar por zona
|
||||||
|
|
||||||
|
async getByZone(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoices =
|
||||||
|
await service.getByZone(
|
||||||
|
req.params.zone as string
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json(invoices);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al buscar facturas",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Eliminar factura
|
||||||
|
|
||||||
|
async delete(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const invoice =
|
||||||
|
await service.delete(
|
||||||
|
req.params.id as string
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!invoice) {
|
||||||
|
|
||||||
|
return res.status(404).json({
|
||||||
|
message:
|
||||||
|
"Factura no encontrada"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message:
|
||||||
|
"Factura eliminada correctamente"
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
message:
|
||||||
|
"Error al eliminar factura",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { ObjectId } from "mongoose";
|
||||||
|
|
||||||
|
export interface IExternalInvoice {
|
||||||
|
|
||||||
|
invoiceNumber:string;
|
||||||
|
|
||||||
|
zone:string;
|
||||||
|
|
||||||
|
employeeId:number;
|
||||||
|
|
||||||
|
vehicleId:number;
|
||||||
|
|
||||||
|
routeId?:number;
|
||||||
|
|
||||||
|
departmentId:number;
|
||||||
|
|
||||||
|
fuelTypeId:ObjectId;
|
||||||
|
|
||||||
|
invoiceDate:Date;
|
||||||
|
|
||||||
|
gallons:number;
|
||||||
|
|
||||||
|
pricePerGallon:number;
|
||||||
|
|
||||||
|
totalAmount?:number;
|
||||||
|
|
||||||
|
createdBy:ObjectId;
|
||||||
|
|
||||||
|
approvedBy?:ObjectId;
|
||||||
|
|
||||||
|
comments?:string;
|
||||||
|
|
||||||
|
status:string;
|
||||||
|
|
||||||
|
}
|
||||||
108
backend/src/modules/external-invoices/externalInvoice.model.ts
Normal file
108
backend/src/modules/external-invoices/externalInvoice.model.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
const externalInvoiceSchema = new mongoose.Schema({
|
||||||
|
|
||||||
|
invoiceNumber:{
|
||||||
|
type:String,
|
||||||
|
required:true,
|
||||||
|
unique:true
|
||||||
|
},
|
||||||
|
|
||||||
|
zone:{
|
||||||
|
type:String,
|
||||||
|
required:true,
|
||||||
|
enum:[
|
||||||
|
"Ocotepeque",
|
||||||
|
"Gracias",
|
||||||
|
"Santa Bárbara"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
employeeId:{
|
||||||
|
type:Number,
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
vehicleId:{
|
||||||
|
type:Number,
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
routeId:{
|
||||||
|
type:Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
fuelTypeId:{
|
||||||
|
type:mongoose.Schema.Types.ObjectId,
|
||||||
|
ref:"FuelType",
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
invoiceDate:{
|
||||||
|
type:Date,
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
gallons:{
|
||||||
|
type:Number,
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
pricePerGallon:{
|
||||||
|
type:Number,
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
totalAmount:{
|
||||||
|
type:Number,
|
||||||
|
default:0
|
||||||
|
},
|
||||||
|
|
||||||
|
createdBy:{
|
||||||
|
type:mongoose.Schema.Types.ObjectId,
|
||||||
|
ref:"User",
|
||||||
|
required:true
|
||||||
|
},
|
||||||
|
|
||||||
|
approvedBy:{
|
||||||
|
type:mongoose.Schema.Types.ObjectId,
|
||||||
|
ref:"User"
|
||||||
|
},
|
||||||
|
|
||||||
|
comments:{
|
||||||
|
type:String
|
||||||
|
},
|
||||||
|
|
||||||
|
status:{
|
||||||
|
type:String,
|
||||||
|
enum:[
|
||||||
|
"PENDIENTE",
|
||||||
|
"APROBADO",
|
||||||
|
"RECHAZADO"
|
||||||
|
],
|
||||||
|
default:"PENDIENTE"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps:true,
|
||||||
|
versionKey:false
|
||||||
|
});
|
||||||
|
|
||||||
|
//Calcular total antes de guardar
|
||||||
|
|
||||||
|
externalInvoiceSchema.pre(
|
||||||
|
"save",
|
||||||
|
async function () {
|
||||||
|
|
||||||
|
this.totalAmount =
|
||||||
|
this.gallons *
|
||||||
|
this.pricePerGallon;
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default mongoose.model(
|
||||||
|
"ExternalInvoice",
|
||||||
|
externalInvoiceSchema
|
||||||
|
);
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
import { ExternalInvoiceController } from "./externalInvoice.controller";
|
||||||
|
|
||||||
|
import { authMiddleware } from "../auth/auth.middleware";
|
||||||
|
|
||||||
|
const invoicerouter = Router();
|
||||||
|
|
||||||
|
const controller = new ExternalInvoiceController();
|
||||||
|
|
||||||
|
//Obtener todas las facturas
|
||||||
|
|
||||||
|
invoicerouter.get("/", authMiddleware, controller.getAll);
|
||||||
|
|
||||||
|
//Obtener factura por ID
|
||||||
|
|
||||||
|
invoicerouter.get("/:id", authMiddleware, controller.getById);
|
||||||
|
|
||||||
|
//Crear factura
|
||||||
|
|
||||||
|
invoicerouter.post("/", authMiddleware, controller.create);
|
||||||
|
|
||||||
|
//Actualizar factura
|
||||||
|
|
||||||
|
invoicerouter.put("/:id", authMiddleware, controller.update);
|
||||||
|
|
||||||
|
//Aprobar factura
|
||||||
|
|
||||||
|
invoicerouter.put("/:id/approve", authMiddleware, controller.approve);
|
||||||
|
|
||||||
|
//Rechazar factura
|
||||||
|
|
||||||
|
invoicerouter.put("/:id/reject", authMiddleware, controller.reject);
|
||||||
|
|
||||||
|
//Buscar por estado
|
||||||
|
|
||||||
|
invoicerouter.get("/status/:status", authMiddleware, controller.getByStatus);
|
||||||
|
|
||||||
|
//Buscar por zona
|
||||||
|
|
||||||
|
invoicerouter.get("/zone/:zone", authMiddleware, controller.getByZone);
|
||||||
|
|
||||||
|
//Eliminar factura
|
||||||
|
|
||||||
|
invoicerouter.delete("/:id", authMiddleware, controller.delete);
|
||||||
|
|
||||||
|
export default invoicerouter;
|
||||||
172
backend/src/modules/external-invoices/externalInvoice.service.ts
Normal file
172
backend/src/modules/external-invoices/externalInvoice.service.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import ExternalInvoiceModel from "./externalInvoice.model";
|
||||||
|
|
||||||
|
import { IExternalInvoice } from "./externalInvoice.interface";
|
||||||
|
|
||||||
|
export class ExternalInvoiceService {
|
||||||
|
|
||||||
|
//Crear factura externa
|
||||||
|
|
||||||
|
async create(
|
||||||
|
data: IExternalInvoice
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Calcular monto total
|
||||||
|
data.totalAmount =
|
||||||
|
data.gallons *
|
||||||
|
data.pricePerGallon;
|
||||||
|
const invoice =
|
||||||
|
new ExternalInvoiceModel(data);
|
||||||
|
|
||||||
|
return await invoice.save();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Obtener todas las facturas
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.find()
|
||||||
|
.populate("fuelTypeId")
|
||||||
|
.populate("createdBy")
|
||||||
|
.populate("approvedBy")
|
||||||
|
.sort({
|
||||||
|
createdAt: -1
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener factura por ID
|
||||||
|
|
||||||
|
async getById(
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.findById(id)
|
||||||
|
.populate("fuelTypeId")
|
||||||
|
.populate("createdBy")
|
||||||
|
.populate("approvedBy");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Actualizar factura
|
||||||
|
|
||||||
|
async update(
|
||||||
|
id: string,
|
||||||
|
data: Partial<IExternalInvoice>
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Recalcular total si cambian datos
|
||||||
|
if (
|
||||||
|
data.gallons &&
|
||||||
|
data.pricePerGallon
|
||||||
|
) {
|
||||||
|
|
||||||
|
data.totalAmount =
|
||||||
|
data.gallons *
|
||||||
|
data.pricePerGallon;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.findByIdAndUpdate(
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Aprobar factura
|
||||||
|
|
||||||
|
async approve(
|
||||||
|
id: string,
|
||||||
|
approvedBy: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.findByIdAndUpdate(
|
||||||
|
|
||||||
|
id,
|
||||||
|
|
||||||
|
{
|
||||||
|
status: "APROBADO",
|
||||||
|
approvedBy
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rechazar factura
|
||||||
|
|
||||||
|
async reject(
|
||||||
|
id: string,
|
||||||
|
approvedBy: string,
|
||||||
|
comments?: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.findByIdAndUpdate(
|
||||||
|
|
||||||
|
id,
|
||||||
|
|
||||||
|
{
|
||||||
|
status: "RECHAZADO",
|
||||||
|
approvedBy,
|
||||||
|
comments
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
new: true
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar por estado
|
||||||
|
|
||||||
|
async getByStatus(
|
||||||
|
status: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.find({
|
||||||
|
status
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar por zona
|
||||||
|
|
||||||
|
async getByZone(
|
||||||
|
zone: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.find({
|
||||||
|
zone
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Eliminar factura
|
||||||
|
|
||||||
|
async delete(
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return await ExternalInvoiceModel
|
||||||
|
.findByIdAndDelete(id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import mongoose from "mongoose";
|
|||||||
const fuelRequestSchema = new mongoose.Schema({
|
const fuelRequestSchema = new mongoose.Schema({
|
||||||
|
|
||||||
requestNumber:{
|
requestNumber:{
|
||||||
type:Number,
|
type:String,
|
||||||
required:true,
|
required:true,
|
||||||
unique:true
|
unique:true
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user