- Backend (cfdi.service.ts): getConceptosList ahora soporta filtro noIdentificacion
via cc.no_identificacion ILIKE
- Frontend API (cfdi.ts): ConceptosFilters incluye noIdentificacion; se envía
como query param
- Frontend página (cfdi/page.tsx):
* Nuevo estado noIdentificacion en conceptosFilters
* Nueva columna 'No. Identificación' en header de tabla con Popover filtro
* Celda no_identificacion renderizada en cada fila
* Export a Excel respeta el nuevo filtro
- Nuevo endpoint GET /reportes/estado-resultados-detallado con cálculo contable:
* Ventas, Devoluciones, Ventas netas, Costo de ventas, Utilidad bruta,
Gastos operativos, Utilidad de la operación
* Fórmula: subtotal_mxn - descuento_mxn (sin impuestos), nómina usa total_mxn
* Excluye anticipos (uso_cfdi=P01 o clave_prod_serv=84111506)
* Filtro por régimen fiscal opcional
* Año anterior calculado automáticamente
- Nuevo endpoint GET /reportes/estado-resultados/drill-down:
* Nivel 1: resumen agrupado por RFC
* Nivel 2: CFDIs individuales filtrados por categoría
* Categorías: ventas, devoluciones, costo-ventas, gastos-operativos
- Nuevo endpoint GET /reportes/estado-resultados/export:
* Genera Excel con formato condicional (verde/rojo, negritas)
- Frontend:
* Tabla vertical con % vertical, año anterior y variación %
* Filas clickeables para drill-down modal de 2 niveles
* Top 10 Clientes/Proveedores mantenidos debajo
* Selector de régimen conectado al reporte
- Fix: NaN en total de drill-down nivel 2 por numeric como string en pg
* Agregado ::float en queries SQL de CFDIs individuales
Backend:
- client-invitations.service.ts: funcion resendInvitation() que
genera nuevo token, actualiza expiresAt y reenvia el email.
- Controller + routes: POST /invitations/client/:id/resend
Frontend:
- API client + hook useResendInvitation con invalidacion de cache.
- Pagina /admin/invitar-cliente: boton 'Reenviar' por cada
invitacion pendiente en la tabla.
Refs: docs/CAMBIOS-2026-05-09.md
Backend:
- Nuevo modelo Prisma ClientInvitation con token unico, expiracion
y estados (pending/accepted/expired).
- Migracion: 20260511213955_add_client_invitations
- Service client-invitations.service.ts: crear invitacion,
validar token, registrar desde invitacion (reutiliza logica
de creacion de tenant + usuario de despacho.service).
- Controller + routes: POST /invitations/client (admin),
GET /invitations/client/validate/:token (publico),
POST /invitations/client/register/:token (publico),
GET /invitations/client (admin).
- Email template client-invitation.ts con link a
/invitacion/registro/{token}.
- Agregado sendClientInvitation a email.service.
Frontend:
- Pagina /invitacion/registro/[token] para que el invitado
complete registro (nombre, password, despacho, RFC, perfil).
- Pagina /admin/invitar-cliente para que admin global envie
invitaciones y vea el historial.
- Hooks useCreateInvitation, useValidateInvitationToken,
useRegisterFromInvitation, useClientInvitations.
- API client lib/api/client-invitations.ts.
Infra:
- PM2 ecosystem.config.js: usa node --import tsx con
kill_timeout aumentado a 15s para evitar EADDRINUSE.
- React Query retry=2 con delay exponencial para resiliencia.
Refs: docs/CAMBIOS-2026-05-09.md
La condicion SQL NOT (metodo_pago = 'PPD' AND ...) producia NULL
cuando metodo_pago era NULL (como en complementos de pago tipo P),
lo que excluia silenciosamente todos los tipo P del listado de
emitidos en conciliacion.
Cambiada a: metodo_pago IS NULL OR metodo_pago != 'PPD' OR
regimen_fiscal_emisor IN ('605','616')
Esto mantiene la intencion original (excluir PPD de regimenes
que no son 605/616) sin afectar a tipo P ni otros sin metodo de pago.
Refs: docs/CAMBIOS-2026-05-09.md seccion 8
- conciliacion.service.ts: filtros y ordenamiento ahora usan
COALESCE(fecha_pago_p, fecha_emision). Los CFDIs tipo P
(complementos de pago) aparecen en el periodo del pago real,
no de la emision del CFDI.
- conciliacion.service.ts: agrega fechaPagoP al SELECT y a la
interfaz ConciliacionCfdi.
- conciliacion/page.tsx: tablas y export Excel usan
fechaPagoP || fechaEmision para mostrar la fecha.
- cfdi-invoice.tsx: para tipo P con fechaPagoP, muestra
'Pago: {fecha}' en el encabezado.
- conciliacion.ts: actualiza interfaz ConciliacionCfdi con
todos los campos que ya devuelve el backend.
Refs: docs/CAMBIOS-2026-05-09.md secciones 7 y 8
- sat-sync.job.ts: cron diario e incremental ahora iteran contribuyentes
por tenant y pasan contribuyenteId a startSync(). Evita que CFDIs
importados del SAT queden con contribuyente_id = NULL.
- sat.service.ts: retryJob() ahora reintenta con job.contribuyenteId.
- conciliacion.service.ts: agrega campos faltantes al SELECT de CFDIs:
status, formaPago, serie, folio, usoCfdi, subtotal, descuento,
moneda, tipoCambio, ivaTraslado, ivaRetencion, isrRetencion,
fechaCertSat. Antes el visor mostraba 'CANCELADO' para todos los
CFDIs (status era undefined) y faltaban datos de forma de pago,
impuestos, serie/folio, etc.
Refs: docs/CAMBIOS-2026-05-09.md secciones 6 y 7
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