Files
HoruxDespachosNuevo/docs/CAMBIOS-2026-05-04.md

37 KiB
Raw Blame History

Resumen de cambios - 4 de mayo de 2026


1. Catálogo de obligaciones fiscales: nuevas obligaciones predefinidas

Fecha: 2026-05-04

Se agregaron 3 obligaciones fiscales predefinidas al catálogo maestro.

Obligaciones agregadas

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
isrtp Impuesto sobre remuneración al trabajo mensual Día 10 del mes siguiente PM y PF Estatal Ninguna No
ish ISH - Impuesto Sobre Hospedaje mensual Día 15 del mes siguiente PM y PF Estatal Ninguna No
sipare SIPARE - Cuotas obrero-patronales mensual Día 15 del mes siguiente PM y PF Seguridad social Con empleados No

Archivo modificado

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Se agregaron las 3 entradas al array OBLIGACIONES_CATALOGO

2. Fix: Suscripciones pending se mostraban como activas en /configuracion/planes-despacho

Fecha: 2026-06-18

Problema

En la página Configuración Planes, las suscripciones con estado pending (primer pago aún no completado) mostraban el banner verde "Suscripción activa" y el badge "Plan actual" en verde, dando la impresión de que el plan estaba pagado y vigente.

Causa

El frontend evaluaba subStatus === 'authorized' || subStatus === 'pending' para mostrar el banner de activa, y consideraba pending como "plan actual pagado" (isCurrentPlanPaid).

Solución

  • Se derivó el estado real de la suscripción con getSubscriptionState() de @horux/shared.
  • El banner "Suscripción activa" ahora solo aparece cuando la suscripción está realmente authorized y dentro de su período.
  • Se agregó un banner amarillo "Suscripción pendiente de pago" para estados pending.
  • El badge del plan actual cambia a amarillo y muestra "Plan actual — pendiente" cuando la suscripción está pendiente.
  • El botón "Cancelar suscripción" ya no se muestra para suscripciones pending.

Archivos modificados

Archivo Cambio
apps/web/app/(dashboard)/configuracion/planes-despacho/page.tsx Lógica de estado de suscripción, banners y badges

3. Fix: Botón "Pagar este plan" fallaba para suscripciones pending

Fecha: 2026-06-18

Problema

Al hacer clic en "Pagar este plan" en una suscripción con estado pending, se mostraba el error:
"No hay suscripción activa para cambiar" en lugar de abrir MercadoPago.

Causa

El flujo handleContratar intentaba crear una nueva suscripción (subscribeMe), pero el backend rechazaba porque ya existía una pending. El frontend entonces caía en upgradeMe y luego changeMyPlan, ambos validan que haya una suscripción authorized o trialpending no califica, por eso el error.

Solución

En handleContratar:

  • Si el usuario selecciona el plan actual y la suscripción está pending, se llama directamente a generatePaymentLink para regenerar el link de pago de MercadoPago.
  • Si el usuario intenta cambiar a otro plan estando pending, se muestra:
    "Completa el pago del plan actual antes de cambiar de plan."

Archivos modificados

Archivo Cambio
apps/web/app/(dashboard)/configuracion/planes-despacho/page.tsx Lógica de handleContratar para estados pending

4. Adjuntar PDFs en el correo de declaración subida

Fecha: 2026-05-04

Cambio

Cuando se sube una declaración provisional (POST /api/documentos/declaraciones), el correo de notificación a owners y supervisor ahora incluye como adjuntos:

  • El acuse de declaración (pdf_declaracion).
  • La liga de pago (pdf_liga_pago), si se subió.

Archivos modificados

Archivo Cambio
packages/core/src/email/transport.ts EmailTransport.send acepta un arreglo opcional de EmailAttachment y lo pasa a nodemailer.sendMail
apps/api/src/services/email/email.service.ts sendEmail y sendDocumentoSubido aceptan y reenvían attachments
apps/api/src/services/notify-upload.service.ts Nueva función buildDeclaracionAttachments que lee los PDFs de declaraciones_provisionales y los pasa al correo
apps/api/src/controllers/documentos.controller.ts Se pasa declaracionId a notifyDocumentoSubido para poder recuperar los PDFs

Notas

  • Los documentos extra (POST /api/documentos/extras) no incluyen adjuntos; solo cambia el flujo de declaraciones.
  • Si los adjuntos superan los 20 MB, se omiten y se deja un aviso en el cuerpo del correo para evitar rechazos por límite de SMTP.

5. Nueva obligación: FONACOT

Fecha: 2026-05-04

Cambio

Se agregó la obligación fonacot al catálogo maestro de obligaciones fiscales.

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
fonacot Crédito FONACOT Mensual Día 5 del mes siguiente PM/PF Créditos de los trabajadores Con empleados

Archivo modificado

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Se agregó la entrada fonacot en la sección Créditos de los trabajadores

6. Nueva obligación: Aviso de actividades vulnerables

Fecha: 2026-05-04

Cambio

Se agregó la obligación actividades-vulnerables al catálogo maestro.

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
actividades-vulnerables Aviso de actividades vulnerables Mensual Día 17 del mes siguiente PM/PF Federal mensual

Archivo modificado

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Se agregó la entrada actividades-vulnerables en la sección Federales mensuales

7. Nueva obligación: Declaración Informativa de transparencia

Fecha: 2026-05-04

Cambio

Se agregó la obligación declaracion-transparencia al catálogo maestro.

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
declaracion-transparencia Declaración Informativa de transparencia Anual Día 31 de mayo PM Federal anual

Archivo modificado

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Se agregó la entrada declaracion-transparencia en la sección Anuales PM

8. Nueva obligación: Declaración Informativa Múltiple del IEPS (trimestral)

Fecha: 2026-05-04

Cambio

Se agregó la obligación ieps-trimestral al catálogo maestro.

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
ieps-trimestral Declaración Informativa Múltiple del IEPS Trimestral Día 17 de abril, julio, octubre y enero PM/PF Federal trimestral

Archivo modificado

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Se agregó la entrada ieps-trimestral en la nueva sección Federales trimestrales

9. Nueva obligación: SISUB y soporte de frecuencia cuatrimestral

Fecha: 2026-05-04

Cambio

Se agregó la obligación sisub al catálogo y se extendió el sistema para soportar obligaciones con frecuencia cuatrimestral.

ID Nombre Frecuencia Fecha límite Aplica a Categoría Condición Recomendada por defecto
sisub Sistema de Información de Subcontratación Cuatrimestral Día 17 de enero, mayo y septiembre PM/PF Seguridad social Con empleados

Archivos modificados

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Agregada sisub y cuatrimestral al union type de frecuencia
apps/api/src/services/obligaciones.service.ts inferirFrecuencia y appliesTo soportan cuatrimestral
apps/api/src/services/calendario-fiscal.service.ts Generación de eventos para meses cuatrimestrales (1, 5, 9)
apps/api/src/services/alertas-manuales.service.ts appliesToPeriod soporta cuatrimestral
apps/api/src/services/declaraciones.service.ts Periodicidad incluye cuatrimestral
apps/api/src/controllers/documentos.controller.ts Schema de declaraciones acepta cuatrimestral
apps/api/src/migrations/tenant/052_declaraciones_cuatrimestral.sql CHECK de periodicidad permite cuatrimestral
apps/web/app/(dashboard)/configuracion/obligaciones/page.tsx Badge de frecuencia cuatrimestral
apps/web/app/(dashboard)/pendientes/page.tsx Badge de frecuencia cuatrimestral

10. Fix: sincronización SAT — tipos de CFDI, UUID case-insensitive y reutilización de requestIds

Fecha: 2026-05-04

Cambios

  • La verificación de CFDIs incompletos (hasIncompleteCfdis / getOldestIncompleteCfdiDate) ahora incluye los tipos de comprobante P (pago) y N (nómina), además de I (ingreso) y E (egreso).
  • Al guardar/actualizar CFDIs, la comparación de uuid se hace con LOWER() para evitar duplicados por diferencias de mayúsculas/minúsculas.
  • Se desactivó la reutilización de requestId de jobs SAT previos. Reusarlos puede agotar el límite de descargas del SAT y devolver "Máximo de descargas permitidas", bloqueando el recovery.
  • Se exportó runRecoverySyncJob para permitir su invocación manual desde scripts.

Archivos modificados

Archivo Cambio
apps/api/src/jobs/sat-sync.job.ts Incluir P y N en consultas de CFDIs incompletos; exportar runRecoverySyncJob
apps/api/src/services/sat/sat.service.ts Comparación LOWER(uuid); comentar reutilización de requestId

11. Fix: drill-down de CFDIs carga el CFDI completo al visualizar

Fecha: 2026-05-04

Problema

En la vista de drill-down, al hacer clic en el ojo para ver un CFDI se usaba únicamente el objeto resumen de la lista, que no incluye conceptos ni todos los detalles.

Solución

Ahora se llama a getCfdiById(id) para obtener el CFDI completo antes de abrir el visor, y se muestra un estado de carga mientras se resuelve la petición.

Archivo modificado

Archivo Cambio
apps/web/app/(dashboard)/drill-down/page.tsx Carga completa del CFDI al hacer clic en "Ver factura"

12. Scripts de soporte: Demo Ventas y operaciones

Fecha: 2026-05-04

Se crearon varios scripts de utilería bajo apps/api/scripts/ para tareas de soporte y configuración de la cuenta Demo Ventas.

Scripts principales

Script Propósito
create-demo-ventas.ts Crea el tenant Demo Ventas, su BD, usuario owner y suscripción custom gratuita
update-demo-ventas.ts Agrega usuarios supervisor/auxiliar/cliente y 5 contribuyentes adicionales a Demo Ventas
seed-demo-obligaciones-tareas.ts Siembra obligaciones fiscales y tareas recurrentes para todos los contribuyentes de Demo Ventas
fix-demo-carteras-asignaciones.ts Crea la subcartera del auxiliar y asigna contribuyentes, obligaciones y tareas de forma válida
reset-demo-asignaciones.ts Deja Demo Ventas en estado "tutorial": elimina subcarteras, asignaciones y relación auxiliar-supervisor
change-user-email.ts Cambia el correo de un usuario, genera contraseña temporal e invalida sesiones
resend-welcome.ts Reenvía el correo de bienvenida a un usuario

Estos scripts no son parte del flujo productivo; se ejecutan manualmente vía npx tsx.

13. Automatización de cierre de obligaciones fiscales

Fecha: 2026-05-04

Cambio

Se automatiza el cierre de todas las obligaciones fiscales desde la sección existente Documentos Declaraciones. Al subir una declaración o su comprobante de pago, el sistema crea automáticamente evidencias en obligacion_evidencias y actualiza el estado de cada obligación fiscal en obligacion_periodos.

Reglas de cierre deterministas

  • requierePago = false (informativas): se marcan completadas al subir la declaración (declaracion).
  • requierePago = true (pago + declaración): la declaración marca declaracion_presentada = true; el periodo se cierra al subir el comprobante de pago (pago).
  • Al subir una declaración con monto $0, se marca el pago como presentado automáticamente.

Nuevas tablas y columnas

Migración Descripción
053_obligacion_evidencias.sql Tabla genérica para evidencias de obligaciones (declaración, pago, acuse, complemento)
054_obligacion_periodos_estados.sql Agrega declaracion_presentada, pago_presentado y evidencia_id a obligacion_periodos
055_declaracion_obligaciones.sql Relaciona declaraciones provisionales con las obligaciones fiscales que cierran

Nuevos endpoints (uso interno / futuro)

Método Endpoint Descripción
GET /api/documentos/obligacion-evidencias Listar evidencias por contribuyente/periodo/obligación
POST /api/documentos/obligacion-evidencias Subir nueva evidencia
GET /api/documentos/obligacion-evidencias/:id/pdf Descargar PDF de evidencia
DELETE /api/documentos/obligacion-evidencias/:id Eliminar evidencia y recalcular estado del periodo

Archivos creados

Archivo Cambio
apps/api/src/services/obligacion-evidencias.service.ts Servicio para crear/listar/descargar/eliminar evidencias y actualizar obligacion_periodos
apps/api/src/migrations/tenant/053_obligacion_evidencias.sql Tabla obligacion_evidencias
apps/api/src/migrations/tenant/054_obligacion_periodos_estados.sql Columnas de estado en obligacion_periodos
apps/api/src/migrations/tenant/055_declaracion_obligaciones.sql Relación declaración ↔ obligación
apps/web/lib/api/obligaciones.ts Cliente API para obtener obligaciones por periodo

Archivos modificados

Archivo Cambio
apps/api/src/constants/obligaciones-fiscales.ts Campo requierePago en todas las obligaciones del catálogo
apps/api/src/services/declaraciones.service.ts Crea evidencias en las obligaciones seleccionadas; vincula declaración con obligaciones; mantiene fallback legacy por impuestos
apps/api/src/services/obligaciones.service.ts getObligacionesPorPeriodo devuelve requierePago, declaracionPresentada, pagoPresentado
apps/api/src/services/notify-upload.service.ts Soporte para notificaciones de obligacion_evidencia
apps/api/src/services/email/templates/documento-subido.ts Template para evidencias de obligación
apps/api/src/controllers/documentos.controller.ts Schema de declaraciones acepta obligacionesIds
apps/api/src/routes/documentos.routes.ts Rutas de evidencias
apps/web/lib/api/declaraciones.ts CreateDeclaracionData acepta obligacionesIds
apps/web/app/(dashboard)/documentos/page.tsx Diálogo de subida reemplaza “Impuestos cubiertos” por selector de obligaciones fiscales del periodo

15. Fix: quitar toggle de completado en Configuración Obligaciones fiscales Tareas

Fecha: 2026-06-22

Problema

En Configuración Obligaciones fiscales Tareas seguía apareciendo el botón para marcar tareas como completadas/pendientes manualmente, pero el estado de las obligaciones fiscales ahora se actualiza automáticamente desde Documentos Declaraciones.

Solución

  • Se convirtió el icono de check/círculo en un indicador visual de estado (completada, pendiente, atrasada) sin interacción.
  • Se eliminaron las mutaciones de completar/descompletar periodo del frontend.

Archivo modificado

Archivo Cambio
apps/web/components/obligaciones/tareas-tab.tsx Icono de estado estático; eliminados completarMutation y descompletarMutation

14. Fix: sugerencias de Clave Producto SAT en facturación

Fecha: 2026-06-22

Problema

En Facturación Conceptos, el campo Clave Producto SAT no mostraba sugerencias al escribir.

Causa

La tabla cat_clave_prod_serv de la BD central estaba vacía; el catálogo nunca se había importado.

Solución

  • Se importó el catálogo oficial CFDI 4.0 (c_ClaveProdServ) desde los recursos de phpcfdi/resources-sat-catalogs (52,513 registros).
  • Se creó el script apps/api/scripts/import-clave-prod-serv.ts para importaciones futuras.
  • Se hizo más robusto el autocomplete del campo:
    • AbortController para cancelar búsquedas anteriores.
    • Manejo de errores y autoComplete="off".
  • Se sanitizó el fallback regex en el backend para evitar errores con caracteres especiales.

Archivos creados

Archivo Cambio
apps/api/scripts/import-clave-prod-serv.ts Importa el catálogo desde CSV a PostgreSQL

Archivos modificados

Archivo Cambio
apps/api/src/controllers/catalogos.controller.ts Escapa regex en búsqueda fallback; búsqueda por clave insensible a mayúsculas
apps/web/lib/api/catalogos.ts searchClaveProdServ acepta AbortSignal
apps/web/app/(dashboard)/facturacion/page.tsx handleSearchProduct con AbortController, try/catch y autoComplete="off"

16. Fix: CSF retry, backoff, delays entre tenants y timeouts aumentados

Fecha: 2026-06-01

Cambios

  • Se agregó retry con backoff en la descarga de Constancias de Situación Fiscal.
  • Se agregaron delays entre tenants para no saturar el portal del SAT.
  • Se aumentaron los timeouts del proceso de constancia.

Archivos modificados

Archivo Cambio
apps/api/src/services/constancia.service.ts Retry con backoff y manejo de errores
apps/api/src/services/sat/sat-csf-login.ts Timeouts y delays ajustados
apps/api/src/jobs/sat-sync.job.ts Delay entre ejecuciones de CSF por tenant

17. Fix: múltiples fixes de producción en SAT, pagos y admin

Fecha: 2026-06-10

Cambios

  • Ajustes en el pool de conexiones a tenants (database.ts).
  • Webhooks de MercadoPago más robustos ante eventos inesperados.
  • Mejoras en admin-clientes.service.ts e invoicing.service.ts.
  • Sweep de jobs SAT stale ajustado.

Archivos modificados

Archivo Cambio
apps/api/src/config/database.ts Configuración de pool
apps/api/src/controllers/webhook.controller.ts Robustez en webhooks MP
apps/api/src/jobs/sat-sync.job.ts Fixes en recovery y scheduling
apps/api/src/services/admin-clientes.service.ts Fixes de admin
apps/api/src/services/payment/invoicing.service.ts Fixes de facturación
apps/api/src/services/sat/sat-client.service.ts Fixes de cliente SAT
apps/api/src/services/sat/sweep-stale-jobs.service.ts Ajuste de stale jobs

18. Feat: scorecards de notas de crédito en dashboard

Fecha: 2026-06-13

Cambios

  • Se agregaron scorecards de notas de crédito emitidas y recibidas.
  • Se reordenaron las tarjetas del dashboard.
  • Se ajustó el cálculo de utilidad neta considerando notas de crédito.

Archivos modificados

Archivo Cambio
apps/api/src/services/dashboard.service.ts Nuevos campos de notas de crédito
apps/web/app/(dashboard)/dashboard/page.tsx Scorecards y utilidad ajustada
packages/shared/src/types/dashboard.ts Tipos actualizados

19. Feat: cron de recuperación SAT diario a las 10:00 AM

Fecha: 2026-06-14

Cambio

Se agregó un cron diario a las 10:00 AM (America/Mexico_City) que ejecuta el recovery sync para contribuyentes con FIEL activa y sync incompleto.

Archivo modificado

Archivo Cambio
apps/api/src/jobs/sat-sync.job.ts Nuevo runRecoverySyncJob y scheduling

20. Fix: pagar plan actual en trial_expired y planes > $10k

Fecha: 2026-06-16

Problema

  • Los tenants con suscripción trial_expired no podían pagar el plan actual.
  • Planes Business Control/Enterprise superiores a $10,000 no podían usar preapproval de MercadoPago.

Solución

  • Se permite pagar el plan actual incluso en estado trial_expired.
  • Planes caros usan una Preference one-off anual de MercadoPago en lugar de preapproval.
  • Se agregó el campo mpPreferenceId a subscriptions para trackear la preference.
  • El webhook de MP maneja tanto preapproval como preference.

Archivos modificados

Archivo Cambio
apps/api/src/controllers/despacho.controller.ts Lógica de pago de plan actual
apps/api/src/controllers/subscription.controller.ts Soporte de preference
apps/api/src/controllers/webhook.controller.ts Webhook para preference
apps/api/src/services/payment/mercadopago.service.ts Creación de preference anual
apps/api/src/services/payment/subscription.service.ts Pago de plan actual trial_expired
apps/web/app/(dashboard)/configuracion/planes-despacho/page.tsx UI para pagar plan actual vencido
apps/api/prisma/schema.prisma Campo mpPreferenceId

21. Feat: configuración de notificaciones por rol

Fecha: 2026-06-17

Cambio

Se reemplazó el sistema de preferencias de notificación por contribuyente por uno por rol:

  • owner, supervisor, auxiliar y cliente pueden activar/desactivar tipos de correo.
  • Tipos: documento_subido, weekly_update, subscription_expiring, recordatorio_fiscal, alertas_nuevas, recordatorio_proximo.
  • La tabla notification_role_preferences almacena la configuración por tenant.

Archivos creados

Archivo Cambio
apps/api/src/migrations/tenant/051_notification_role_preferences.sql Tabla de preferencias por rol

Archivos modificados

Archivo Cambio
apps/api/src/services/notification-preferences.service.ts Nuevo modelo por rol
apps/api/src/controllers/notification-preferences.controller.ts CRUD por rol
apps/api/src/services/notifications.service.ts Filtrado por rol
apps/api/src/services/notify-upload.service.ts Usa preferencias por rol
apps/api/src/services/payment/subscription.service.ts Usa preferencias por rol
apps/api/src/jobs/weekly-update.job.ts Filtrado por rol
apps/web/app/(dashboard)/configuracion/notificaciones/page.tsx UI de configuración

22. Fix: quitar badge "Próximamente" de notificaciones ya existentes

Fecha: 2026-06-16

Cambio

Se quitó el badge "Próximamente" de los tipos de notificación que ya estaban implementados.

Archivos modificados

Archivo Cambio
apps/api/src/services/notification-preferences.service.ts Marcado de implementados
apps/web/app/(dashboard)/configuracion/notificaciones/page.tsx Badges actualizados

23. Feat: drill-down en pestaña nueva, rol Vendedor y scripts demo

Fecha: 2026-06-22

Cambios

  • El drill-down de impuestos ahora se abre en una pestaña nueva (/drill-down).
  • Se agregó el rol Vendedor con acceso limitado a invitaciones trial y dashboard.
  • Se crearon scripts de soporte para demos:
    • add-demo-cfdis.ts
    • add-demo-notas-credito.ts
    • create-vendedor-fernando.ts

Archivos creados

Archivo Cambio
apps/api/scripts/add-demo-cfdis.ts CFDIs de demo
apps/api/scripts/add-demo-notas-credito.ts Notas de crédito de demo
apps/api/scripts/create-vendedor-fernando.ts Crea usuario vendedor de demo

Archivos modificados

Archivo Cambio
apps/web/app/(dashboard)/drill-down/page.tsx Página de drill-down
apps/web/app/(dashboard)/impuestos/page.tsx Link a drill-down en pestaña nueva
apps/web/app/(dashboard)/dashboard/page.tsx Integración con drill-down
apps/web/app/(dashboard)/admin/staff/page.tsx Rol Vendedor en staff
apps/web/components/layouts/sidebar.tsx Menú para Vendedor
packages/shared-ui/src/charts/kpi-card.tsx Soporte de link externo

24. Fix: evita logout al cambiar de tenant

Fecha: 2026-06-22

Problema

Al cambiar de empresa con el selector de tenants, múltiples requests en vuelo con el token viejo intentaban refrescar simultáneamente, causando logout o 401.

Solución

  • Se agregó un mutex de refresh token en el interceptor de Axios.
  • Se marca el estado isSwitching en membership-switcher y mis-empresas para evitar clicks múltiples.
  • El backend invalida el refresh token anterior al hacer switch.

Archivos modificados

Archivo Cambio
apps/web/lib/api/client.ts Mutex de refresh
apps/web/components/membership-switcher.tsx Flag isSwitching
apps/web/app/(dashboard)/mis-empresas/page.tsx Flag isSwitching
apps/api/src/services/auth.service.ts Invalidación de refresh token anterior

25. Fix: Vendedor accede a invitaciones trial

Fecha: 2026-06-22

Cambio

El rol Vendedor puede ver y compartir invitaciones trial, pero no puede invitar clientes directamente.

Archivos modificados

Archivo Cambio
apps/api/src/controllers/client-invitations.controller.ts Permisos para vendedor
apps/api/src/controllers/tenants.controller.ts Permisos para vendedor
apps/web/app/(dashboard)/admin/staff/page.tsx UI de permisos
apps/web/components/layouts/sidebar.tsx Menú visible para vendedor

26. Feat: monitor de sincronización SAT

Fecha: 2026-06-22

Cambio

Se agregó un cron de monitoreo de sincronizaciones SAT (cada 2 horas por defecto). Detecta:

  • Jobs fallidos en las últimas N horas.
  • Jobs pending sin atender.
  • Jobs running atascados.
  • Contribuyentes con FIEL activa pero sin sync inicial.

Envía un correo de alerta a SAT_ALERT_EMAIL (o ADMIN_EMAIL si no está configurado).

Variables de entorno

Variable Default Descripción
SAT_ALERT_EMAIL ADMIN_EMAIL Destino de alertas SAT
SAT_MONITOR_SCHEDULE 0 */2 * * * Cron del monitor
SAT_STUCK_RUNNING_HOURS 2 Horas para considerar un job running como atorado
SAT_FAILED_LOOKBACK_HOURS 24 Ventana hacia atrás para reportar jobs fallidos

Archivos creados

Archivo Cambio
apps/api/src/jobs/sat-sync-monitor.job.ts Lógica del monitor
apps/api/src/services/email/templates/sat-sync-alert.ts Template de alerta

Archivos modificados

Archivo Cambio
apps/api/src/index.ts Arranca el monitor
apps/api/src/config/env.ts Validación de variables
apps/api/.env.example Documentación de variables
apps/api/src/services/email/email.service.ts sendSatSyncAlert

27. Feat: reactivación de contribuyentes y limpieza al desactivar

Fecha: 2026-06-22

Cambios

  • Al crear un contribuyente cuyo RFC ya existía desactivado, se reactiva la entidad, se actualizan sus datos y se recupera su historial (CFDIs, FIEL, tareas, etc.).
  • Al desactivar un contribuyente, se limpia de cartera_entidades, cliente_accesos y se desactiva su FIEL para que no siga sincronizándose.
  • El timeout del proceso de constancia se aumentó a 5 minutos.

Archivos modificados

Archivo Cambio
apps/api/src/services/contribuyente.service.ts Reactivación y limpieza al desactivar
apps/api/src/controllers/contribuyente.controller.ts Retorna reactivated y status 200/201
apps/api/src/services/constancia.service.ts Timeout 5 min
apps/web/app/(dashboard)/contribuyentes/page.tsx Alerta al reactivar
apps/web/lib/api/contribuyentes.ts Tipo reactivated

28. Fix: scraper de Constancia de Situación Fiscal más robusto

Fecha: 2026-06-22

Cambio

Se reescribió el scraper de CSF para soportar los múltiples flujos del portal SAT:

  • Búsqueda de PDF en frames, iframes, embeds, links blob:/data: y popups.
  • Validación de que el buffer descargado comienza con %PDF-.
  • Fallback de click por dispatchEvent si el primer click no dispara el handler JSF.

Archivo modificado

Archivo Cambio
apps/api/src/services/sat/sat-csf-scraper.ts Reescritura del scraper

29. Actualización de precios: Business Control, Enterprise, RFCs adicionales y MSI

Fecha: 2026-06-25

Cambio

Se actualizaron los precios de los planes anuales de despacho y la política de cobro por RFC adicional.

Planes anuales

Plan Precio anterior Nuevo precio (primer año / renovación)
Business Control (business_control) $25,850 $30,850
Enterprise (business_cloud) $43,000 $68,850

Los cambios se aplican en la tabla despacho_plan_prices y afectan nuevas suscripciones y renovaciones futuras generadas por el sistema.

RFCs adicionales (overage)

El precio mensual por cada contribuyente extra (más allá de los 100 incluidos) ahora depende del plan:

Plan Precio por RFC adicional
Business Control $25/mes
Enterprise $60/mes

Se modificó adjustDespachoOverage para leer el precio desde el plan de la suscripción activa en lugar de usar un valor único de catálogo.

Meses sin intereses (MSI)

Se habilitó el pago a hasta 12 meses en el checkout de MercadoPago para:

  • Pago anual de Business Control / Enterprise (createSubscriptionPreference).
  • Prorrateos de upgrade entre planes anuales (createProrationPreference).

Nota: para que las cuotas aparezcan sin intereses, es necesario contar con promociones MSI activas en la cuenta de vendedor de MercadoPago. Sin promociones, el checkout mostrará meses con intereses.

Archivos modificados

Archivo Cambio
apps/api/src/services/payment/addon.service.ts Precio de overage por plan ($25 / $60)
apps/api/src/services/payment/mercadopago.service.ts payment_methods.installments: 12 en preferences anuales y prorrateos
apps/web/app/(dashboard)/configuracion/planes-despacho/page.tsx Tarjetas de precios mostradas en UI: Business Control $30,850 y Enterprise $68,850; overage $25/$60
apps/api/prisma/seed (implícito) Valores actualizados en despacho_plan_prices vía SQL

Deploy

Build y reload de API para limpiar el cache de despacho_plan_prices:

cd /root/HoruxDespachosNuevo
pnpm --filter api build
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso


30. Supervisor puede invitar Clientes y Auxiliares

Fecha: 2026-06-25

Cambio

Antes los supervisores solo podían invitar usuarios con rol Cliente. Ahora también pueden invitar Auxiliares, siempre asignados a su propia supervisión.

Backend

Archivo Cambio
apps/api/src/controllers/usuarios.controller.ts Permite a supervisor invitar cliente y auxiliar; al invitar un auxiliar se asigna a sí mismo por defecto y no permite asignar a otro supervisor
apps/api/src/routes/cartera.routes.ts /carteras/supervisores ahora acepta owner y supervisor
apps/api/src/controllers/cartera.controller.ts getSupervisores filtra para que un supervisor solo se vea a sí mismo en el dropdown

Frontend

Archivo Cambio
apps/web/app/(dashboard)/usuarios/page.tsx El dropdown de roles para supervisor incluye Auxiliar; al seleccionar auxiliar se preselecciona al supervisor logueado como responsable

Deploy

cd /root/HoruxDespachosNuevo
pnpm --filter api build
pnpm --filter web build
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso


31. Owner puede editar nombre y rol de usuarios

Fecha: 2026-06-25

Cambio

En la página Configuración Usuarios, el owner ahora puede editar el nombre y el rol de cualquier usuario del tenant desde un modal.

Detalles

  • Se agregó el botón Editar en cada fila de usuario (solo visible para owner).
  • El modal permite cambiar:
    • Nombre
    • Rol (los roles disponibles dependen de si es tenant despacho o legacy)
  • Un owner no puede cambiar su propio rol (solo su nombre).
  • El backend ya tenía el endpoint PATCH /usuarios/:id; se ajustó la UI para exponerlo.

Archivos modificados

Archivo Cambio
apps/web/app/(dashboard)/usuarios/page.tsx Botón Editar, modal de edición, validación de rol propio

Deploy

cd /root/HoruxDespachosNuevo
pnpm --filter web build
pm2 reload horux-web

Estado: Exitoso


32. Recordatorios periódicos en calendario

Fecha: 2026-06-25

Cambio

Los recordatorios custom del calendario ahora pueden ser periódicos: mensual, bimestral, trimestral o anual. Se mantienen activos indefinidamente hasta que el usuario cancele la serie.

Modelo

Se reutiliza la tabla recordatorios con metadatos de serie:

  • Maestro: serie_id IS NULL y recurrencia <> 'unica'.
  • Instancias: filas con serie_id = maestro.id, una por cada ocurrencia generada.

Esto permite completar una instancia sin afectar las demás, y el cron de emails de recordatorios próximos sigue funcionando sin cambios mayores.

Funcionalidad

  • Al crear un recordatorio periódico se generan instancias para un horizonte de 24 meses.
  • Al editar una instancia se actualizan el maestro y todas las ocurrencias futuras no completadas.
  • Al eliminar una instancia periódica se cancela toda la serie: se desactiva el maestro y se borran las instancias futuras no completadas.
  • Un cron diario a las 6:00 AM extiende automáticamente las series activas para mantener el horizonte futuro.

Frecuencias soportadas

  • mensual
  • bimestral
  • trimestral
  • anual

Archivos modificados

Archivo Cambio
apps/api/src/migrations/tenant/056_recordatorios_periodicos.sql Nuevas columnas recurrencia, serie_id, activo, fecha_inicio, fecha_fin e índices
apps/api/src/services/recordatorios.service.ts Lógica de creación, edición, eliminación y regeneración de series periódicas
apps/api/src/services/notifications.service.ts Excluir maestros del envío de recordatorios próximos
apps/api/src/controllers/calendario.controller.ts Schemas aceptan recurrencia y fechaFin
apps/api/src/jobs/recordatorios-periodicos.job.ts Nuevo cron de extensión de series
apps/api/src/index.ts Registro del nuevo cron
apps/web/app/(dashboard)/calendario/page.tsx Selector de recurrencia, fecha fin, indicador de serie y confirmación al cancelar

Deploy

cd /root/HoruxDespachosNuevo
pnpm --filter api build
pnpm --filter web build
pnpm --filter @horux/api db:migrate-tenants
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso


33. Filtros de Conceptos con Aplicar/Limpiar

Fecha: 2026-06-30

Cambio

En CFDIs Conceptos, los filtros de encabezado de tabla (UUID, Clave SAT, Descripción, No. Identificación) ahora se aplican únicamente al hacer clic en Aplicar, en lugar de disparar la búsqueda en cada cambio de teclado.

Antes

Los inputs usaban onChange para actualizar conceptosFilters, lo que provocaba que useQuery re-lanzara la petición por cada letra escrita.

Ahora

  • Se separó el estado en dos:
    • conceptosDraftFilters: valores que el usuario escribe en los popovers.
    • appliedConceptosFilters: valores realmente aplicados a la query.
  • Aplicar: copia el draft al estado aplicado, resetea paginación y cierra el popover.
  • Limpiar: borra ese campo, aplica el cambio y resetea paginación.
  • El ordenamiento por importe sigue funcionando con un clic en el encabezado.

Archivos modificados

Archivo Cambio
apps/web/app/(dashboard)/cfdi/page.tsx Separación de draft/applied filters y nuevos handlers applyConceptosFilters / clearConceptosFilter
apps/api/src/controllers/cfdi.controller.ts El endpoint /cfdi/conceptos ahora recibe y pasa noIdentificacion al servicio

Deploy

cd /root/HoruxDespachosNuevo
pnpm --filter api build
pnpm --filter web build
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso


34. Filtro de fechas en CFDIs usa fecha de emisión

Fecha: 2026-06-30

Cambio

El filtro de fechas en el listado y export de CFDIs ahora filtra por fecha_emision en lugar de fecha_efectiva.

Antes

El where clause usaba:

COALESCE(fecha_efectiva, fecha_emision - interval '1 hour') BETWEEN fechaInicio AND fechaFin

Esto provocaba que facturas emitidas en meses anteriores pero pagadas/ejercidas en el rango seleccionado aparecieran en el resultado (por ejemplo, facturas de febrero con fecha_efectiva en junio).

Ahora

El filtro usa directamente:

fecha_emision::date BETWEEN fechaInicio AND fechaFin

El export Excel hereda el mismo filtro, así que tabla y Excel son consistentes.

Archivos modificados

Archivo Cambio
apps/api/src/services/cfdi.service.ts Filtros de fecha en getCfdis, getConceptosList y downloadXmlsZip usan fecha_emision::date

Deploy

cd /root/HoruxDespachosNuevo
pnpm --filter api build
pnpm --filter web build
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso


Deploy histórico

Preparación

  1. Asegurarse de que apps/api/.env tenga las nuevas variables (tienen defaults, pero conviene declararlas):
    SAT_ALERT_EMAIL=carlos@horuxfin.com
    SAT_MONITOR_SCHEDULE=0 */2 * * *
    SAT_STUCK_RUNNING_HOURS=2
    SAT_FAILED_LOOKBACK_HOURS=24
    
  2. Si hay scripts de debug antiguos en apps/api/scripts/, limpiar los que no sean necesarios antes de commitear.

Comandos

cd /root/HoruxDespachosNuevo
pnpm --filter @horux/core build
pnpm --filter api build
pnpm --filter web build
npx tsx apps/api/scripts/migrate-tenants.ts
pm2 reload horux-api
pm2 reload horux-web

Estado: Exitoso