refactor: migrate all tenant services and controllers to pool-based queries

Replace Prisma raw queries with pg.Pool for all tenant-scoped services:
cfdi, dashboard, impuestos, alertas, calendario, reportes, export, and SAT.
Controllers now pass req.tenantPool instead of req.tenantSchema.
Fixes SQL injection in calendario.service.ts (parameterized interval).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Consultoria AS
2026-03-15 23:29:20 +00:00
parent 7eaeefa09d
commit b064f15404
15 changed files with 359 additions and 412 deletions

View File

@@ -5,8 +5,8 @@ import type { CfdiFilters } from '@horux/shared';
export async function getCfdis(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const filters: CfdiFilters = {
@@ -22,7 +22,7 @@ export async function getCfdis(req: Request, res: Response, next: NextFunction)
limit: parseInt(req.query.limit as string) || 20,
};
const result = await cfdiService.getCfdis(req.tenantSchema, filters);
const result = await cfdiService.getCfdis(req.tenantPool, filters);
res.json(result);
} catch (error) {
next(error);
@@ -31,11 +31,11 @@ export async function getCfdis(req: Request, res: Response, next: NextFunction)
export async function getCfdiById(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const cfdi = await cfdiService.getCfdiById(req.tenantSchema, String(req.params.id));
const cfdi = await cfdiService.getCfdiById(req.tenantPool, String(req.params.id));
if (!cfdi) {
return next(new AppError(404, 'CFDI no encontrado'));
@@ -49,11 +49,11 @@ export async function getCfdiById(req: Request, res: Response, next: NextFunctio
export async function getXml(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const xml = await cfdiService.getXmlById(req.tenantSchema, String(req.params.id));
const xml = await cfdiService.getXmlById(req.tenantPool, String(req.params.id));
if (!xml) {
return next(new AppError(404, 'XML no encontrado para este CFDI'));
@@ -69,8 +69,8 @@ export async function getXml(req: Request, res: Response, next: NextFunction) {
export async function getEmisores(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const search = (req.query.search as string) || '';
@@ -78,7 +78,7 @@ export async function getEmisores(req: Request, res: Response, next: NextFunctio
return res.json([]);
}
const emisores = await cfdiService.getEmisores(req.tenantSchema, search);
const emisores = await cfdiService.getEmisores(req.tenantPool, search);
res.json(emisores);
} catch (error) {
next(error);
@@ -87,8 +87,8 @@ export async function getEmisores(req: Request, res: Response, next: NextFunctio
export async function getReceptores(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const search = (req.query.search as string) || '';
@@ -96,7 +96,7 @@ export async function getReceptores(req: Request, res: Response, next: NextFunct
return res.json([]);
}
const receptores = await cfdiService.getReceptores(req.tenantSchema, search);
const receptores = await cfdiService.getReceptores(req.tenantPool, search);
res.json(receptores);
} catch (error) {
next(error);
@@ -105,14 +105,14 @@ export async function getReceptores(req: Request, res: Response, next: NextFunct
export async function getResumen(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
const año = parseInt(req.query.año as string) || new Date().getFullYear();
const mes = parseInt(req.query.mes as string) || new Date().getMonth() + 1;
const resumen = await cfdiService.getResumenCfdis(req.tenantSchema, año, mes);
const resumen = await cfdiService.getResumenCfdis(req.tenantPool, año, mes);
res.json(resumen);
} catch (error) {
next(error);
@@ -121,16 +121,15 @@ export async function getResumen(req: Request, res: Response, next: NextFunction
export async function createCfdi(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
// Only admin and contador can create CFDIs
if (!['admin', 'contador'].includes(req.user!.role)) {
return next(new AppError(403, 'No tienes permisos para agregar CFDIs'));
}
const cfdi = await cfdiService.createCfdi(req.tenantSchema, req.body);
const cfdi = await cfdiService.createCfdi(req.tenantPool, req.body);
res.status(201).json(cfdi);
} catch (error: any) {
if (error.message?.includes('duplicate')) {
@@ -142,8 +141,8 @@ export async function createCfdi(req: Request, res: Response, next: NextFunction
export async function createManyCfdis(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
if (!['admin', 'contador'].includes(req.user!.role)) {
@@ -160,9 +159,9 @@ export async function createManyCfdis(req: Request, res: Response, next: NextFun
totalFiles: req.body.totalFiles || req.body.cfdis.length
};
console.log(`[CFDI Bulk] Lote ${batchInfo.batchNumber}/${batchInfo.totalBatches} - ${req.body.cfdis.length} CFDIs para schema ${req.tenantSchema}`);
console.log(`[CFDI Bulk] Lote ${batchInfo.batchNumber}/${batchInfo.totalBatches} - ${req.body.cfdis.length} CFDIs`);
const result = await cfdiService.createManyCfdisBatch(req.tenantSchema, req.body.cfdis);
const result = await cfdiService.createManyCfdisBatch(req.tenantPool, req.body.cfdis);
res.status(201).json({
message: `Lote ${batchInfo.batchNumber} procesado`,
@@ -171,7 +170,7 @@ export async function createManyCfdis(req: Request, res: Response, next: NextFun
inserted: result.inserted,
duplicates: result.duplicates,
errors: result.errors,
errorMessages: result.errorMessages.slice(0, 5) // Limit error messages
errorMessages: result.errorMessages.slice(0, 5)
});
} catch (error: any) {
console.error('[CFDI Bulk Error]', error.message, error.stack);
@@ -181,15 +180,15 @@ export async function createManyCfdis(req: Request, res: Response, next: NextFun
export async function deleteCfdi(req: Request, res: Response, next: NextFunction) {
try {
if (!req.tenantSchema) {
return next(new AppError(400, 'Schema no configurado'));
if (!req.tenantPool) {
return next(new AppError(400, 'Tenant no configurado'));
}
if (!['admin', 'contador'].includes(req.user!.role)) {
return next(new AppError(403, 'No tienes permisos para eliminar CFDIs'));
}
await cfdiService.deleteCfdi(req.tenantSchema, String(req.params.id));
await cfdiService.deleteCfdi(req.tenantPool, String(req.params.id));
res.status(204).send();
} catch (error) {
next(error);