- Catálogo de obligaciones fiscales expandido a 30 entradas con campo requierePago. - Soporte de frecuencia cuatrimestral en obligaciones y declaraciones. - Automatización de cierre de obligaciones fiscales desde Documentos › Declaraciones. - Nuevas tablas obligacion_evidencias, obligacion_periodos estados y declaracion_obligaciones. - Nuevo servicio obligacion-evidencias.service.ts y endpoints REST. - Refactor de declaraciones.service.ts para vincular obligaciones y crear evidencias. - Notificaciones por email para evidencias de obligaciones. - Adjuntar PDFs en correo de declaración subida. - Fix drill-down de CFDIs: carga completa al visualizar. - Fix sincronización SAT: tipos P/N, UUID case-insensitive, no reutilizar requestId. - Fix suscripciones pending en /configuracion/planes-despacho. - Fix sugerencias de Clave Producto SAT: importar catálogo y robustecer autocomplete. - Quitar toggle manual de completado en Configuración › Obligaciones fiscales › Tareas. - Scripts de soporte para Demo Ventas y utilerías (change-user-email, resend-welcome, import-clave-prod-serv). - Documentación de cambios en docs/CAMBIOS-2026-05-04.md.
112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import type { Request, Response, NextFunction } from 'express';
|
|
import { prisma } from '../config/database.js';
|
|
|
|
export async function getFormasPago(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catFormaPago.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getMetodosPago(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catMetodoPago.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getUsosCfdi(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catUsoCfdi.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getMonedas(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catMoneda.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getClavesUnidad(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catClaveUnidad.findMany({ orderBy: { descripcion: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
function escapeRegex(str: string): string {
|
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
export async function searchClaveProdServ(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const q = (req.query.q as string || '').trim();
|
|
if (q.length < 2) {
|
|
return res.json([]);
|
|
}
|
|
|
|
// Buscar por clave o descripción
|
|
const data = await prisma.catClaveProdServ.findMany({
|
|
where: {
|
|
OR: [
|
|
{ clave: { startsWith: q, mode: 'insensitive' } },
|
|
{ descripcion: { contains: q, mode: 'insensitive' } },
|
|
],
|
|
},
|
|
take: 20,
|
|
orderBy: { clave: 'asc' },
|
|
});
|
|
|
|
// Si no hay resultados, intentar sin acentos
|
|
if (data.length === 0) {
|
|
const normalized = q.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
if (normalized !== q) {
|
|
const fallback = await prisma.catClaveProdServ.findMany({
|
|
where: { descripcion: { contains: normalized, mode: 'insensitive' } },
|
|
take: 20,
|
|
orderBy: { clave: 'asc' },
|
|
});
|
|
return res.json(fallback);
|
|
}
|
|
|
|
// Buscar con variantes comunes de acentos, escapando caracteres regex primero
|
|
const withAccents = escapeRegex(normalized)
|
|
.replace(/a/gi, '[aá]').replace(/e/gi, '[eé]')
|
|
.replace(/i/gi, '[ií]').replace(/o/gi, '[oó]').replace(/u/gi, '[uú]')
|
|
.replace(/n/gi, '[nñ]');
|
|
|
|
// Usar raw SQL con regex para búsqueda flexible
|
|
const rows: any[] = await prisma.$queryRawUnsafe(
|
|
`SELECT id, clave, descripcion FROM cat_clave_prod_serv WHERE descripcion ~* $1 ORDER BY clave LIMIT 20`,
|
|
withAccents
|
|
);
|
|
return res.json(rows);
|
|
}
|
|
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getObjetosImp(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catObjetoImp.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getTiposRelacion(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catTipoRelacion.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|
|
|
|
export async function getExportaciones(req: Request, res: Response, next: NextFunction) {
|
|
try {
|
|
const data = await prisma.catExportacion.findMany({ orderBy: { clave: 'asc' } });
|
|
res.json(data);
|
|
} catch (error) { next(error); }
|
|
}
|