From e01422e443ff170d63dd8094543ee36208f1ce29 Mon Sep 17 00:00:00 2001 From: Horux Dev Date: Thu, 28 May 2026 21:44:32 +0000 Subject: [PATCH] fix(facturacion): filtrar searchConceptos y searchRfcs por contribuyenteId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - searchConceptos: agrega AND c.contribuyente_id = cuando se recibe contribuyenteId - searchRfcs: restringe el catálogo global de rfcs a aquellos que aparecen en CFDIs del contribuyente (como emisor o receptor) - Usa parametrización dinámica (3800099{params.length}) para evitar errores de índice --- .../src/controllers/facturacion.controller.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/api/src/controllers/facturacion.controller.ts b/apps/api/src/controllers/facturacion.controller.ts index 1fa9632..7b5c2d6 100644 --- a/apps/api/src/controllers/facturacion.controller.ts +++ b/apps/api/src/controllers/facturacion.controller.ts @@ -580,7 +580,13 @@ export async function searchConceptos(req: Request, res: Response, next: NextFun const params: any[] = []; if (q.length >= 2) { params.push(`%${q}%`); - whereSearch = `AND (cc.descripcion ILIKE $1 OR cc.clave_prod_serv ILIKE $1)`; + whereSearch = `AND (cc.descripcion ILIKE $${params.length} OR cc.clave_prod_serv ILIKE $${params.length})`; + } + + let whereContribuyente = ''; + if (contribuyenteId) { + params.push(contribuyenteId); + whereContribuyente = `AND c.contribuyente_id = $${params.length}`; } const { rows } = await pool.query(` @@ -605,6 +611,7 @@ export async function searchConceptos(req: Request, res: Response, next: NextFun WHERE c.status NOT IN ('Cancelado', '0') ${whereType} ${whereSearch} + ${whereContribuyente} ORDER BY cc.clave_prod_serv, cc.descripcion, c.fecha_emision DESC LIMIT 30 `, params); @@ -708,7 +715,7 @@ export async function searchRfcs(req: Request, res: Response, next: NextFunction const q = (req.query.q as string || '').trim(); if (q.length < 3) return res.json([]); - const contribuyenteId = (req.query.contribuyenteId as string || '').trim(); + const contribuyenteId = (req.query.contribuyenteId as string || '').replace(/[^a-f0-9-]/gi, ''); const pool = req.tenantPool!; // RFC del tenant despacho para excluirlo (no se factura a sí mismo) @@ -719,10 +726,17 @@ export async function searchRfcs(req: Request, res: Response, next: NextFunction }); const tenantRfc = tenant?.rfc || ''; - // Búsqueda en el catálogo completo de RFCs. El contribuyente activo solo - // filtra CFDIs relacionados / PPD, no el autocompleto de RFCs — de lo - // contrario no se podría facturar a un cliente nuevo que nunca haya - // aparecido en un CFDI previo. + const params: any[] = [tenantRfc, `%${q}%`]; + let whereContribuyente = ''; + if (contribuyenteId) { + params.push(contribuyenteId); + whereContribuyente = `AND id IN ( + SELECT rfc_receptor_id FROM cfdis WHERE contribuyente_id = $${params.length} AND rfc_receptor_id IS NOT NULL + UNION + SELECT rfc_emisor_id FROM cfdis WHERE contribuyente_id = $${params.length} AND rfc_emisor_id IS NOT NULL + )`; + } + const { rows } = await pool.query(` SELECT id, rfc, razon_social as "razonSocial", regimen_fiscal as "regimenFiscal", @@ -730,9 +744,10 @@ export async function searchRfcs(req: Request, res: Response, next: NextFunction FROM rfcs WHERE rfc != $1 AND (rfc ILIKE $2 OR razon_social ILIKE $2) + ${whereContribuyente} ORDER BY razon_social LIMIT 10 - `, [tenantRfc, `%${q}%`]); + `, params); res.json(rows); } catch (error) { next(error); }