feat: facturación primer pago, fixes SAT/MP, autocompletado RFCs/conceptos

Backend:
- Notificación email al admin cuando llega primer pago aprobado (sin factura auto)
- Endpoints GET /pagos-sin-factura y POST /emitir-factura-pago para admin global
- Fix vinculación org Facturapi Horux 360 (69f23a5a242e0af47a41fa0d)
- Fix webhook MP: validación defensiva de x-signature header
- Fix autocompleto RFCs: eliminado filtro por contribuyenteId
- Fix autocompleto conceptos: eliminado filtro por contribuyenteId
- SAT fixes: anti-bot CSF scraper, request reuse, date range fix, stale job thresholds
- SAT sync request reuse across jobs para evitar agotar cuota diaria
- Typo fix MP_ACCESS_TOKEN en .env
- Trial invitations system backend

Frontend:
- Nueva página /admin/facturas-pendientes con tabla y emisión manual
- Métrica 'Facturas pendientes' en /clientes (clickable)
- Navegación onboarding FIEL/CSD corregida
- Sidebar themes sincronizados
- Fix SAT portal migration scraper (NetIQ)
- Trial invitation acceptance pages
This commit is contained in:
Horux Dev
2026-05-09 21:56:42 +00:00
parent b00b677c54
commit 9f11a0ba39
70 changed files with 2801 additions and 609 deletions

View File

@@ -5,11 +5,8 @@ import * as ctrl from '../controllers/despacho-stats.controller.js';
const router: IRouter = Router();
router.use(authenticate);
router.use(tenantMiddleware);
router.get('/contribuyentes-stats', ctrl.getContribuyentesStats);
router.get('/mis-asignados', ctrl.getMisAsignados);
router.get('/equipo-stats', ctrl.getEquipoStats);
router.get('/contribuyentes-stats', authenticate, tenantMiddleware, ctrl.getContribuyentesStats);
router.get('/mis-asignados', authenticate, tenantMiddleware, ctrl.getMisAsignados);
router.get('/equipo-stats', authenticate, tenantMiddleware, ctrl.getEquipoStats);
export { router as despachoStatsRoutes };

View File

@@ -61,4 +61,8 @@ router.get('/cfdis-ppd', facturacionController.getCfdisPpdPendientes);
// CFDIs emitidos por el contribuyente al receptor (para sección "CFDIs relacionados")
router.get('/cfdis-relacionables', facturacionController.getCfdisRelacionables);
// Admin global: pagos de suscripción sin factura + emisión manual
router.get('/pagos-sin-factura', facturacionController.getPagosSinFactura);
router.post('/emitir-factura-pago/:paymentId', strictLimit, facturacionController.emitirFacturaPago);
export { router as facturacionRoutes };

View File

@@ -0,0 +1,22 @@
import { Router, type IRouter } from 'express';
import { authenticate } from '../middlewares/auth.middleware.js';
import * as trialInvitationsController from '../controllers/trial-invitations.controller.js';
const router: IRouter = Router();
// Public endpoint: anyone can view invitation details by token (no auth required)
router.get('/token/:token', trialInvitationsController.getInvitationByToken);
// Authenticated endpoints
router.use(authenticate);
// Global admin endpoints
router.post('/', trialInvitationsController.createInvitation);
router.get('/', trialInvitationsController.getAllInvitations);
router.post('/:id/cancel', trialInvitationsController.cancelInvitation);
// Self-serve endpoints (autenticado, owner validation in controller)
router.get('/pending', trialInvitationsController.getMyPendingInvitation);
router.post('/:token/accept', trialInvitationsController.acceptInvitation);
export { router as trialInvitationRoutes };