- Procesa hasta SAT_CONCURRENT_CONTRIBUYENTES=10 RFCs en paralelo
- Scheduler daily e incremental trabajan por unidad de sync (contribuyente/tenant legacy)
- Agrega SAT_CONCURRENT_CONTRIBUYENTES a .env.example
- Actualiza docs/SAT-SYNC-IMPLEMENTATION.md con sección de proxies
- Usa proxyManager.createNextAgent() para obtener un proxy del pool.
- Pasa el agente al HttpsWebClient parcheado.
- Log indica si se usa proxy o IP directa.
- Comportamiento actual sin cambios si SAT_PROXY_LIST está vacío.
SAT:
- Cron diario 6-10 AM con grupos de tenants (~20% por hora).
- Retries fijos de daily sync a 9 AM y 4 PM CDMX.
- Filtrado de contribuyentes con FIEL vigente en cron.
- Timeout de 5 min en cliente HTTP del SAT.
- Manejo defensivo de 404, EmptyResult (5004) y solicitudes agotadas.
- Fechas formateadas en zona horaria America/Mexico_City.
- Patch a @nodecfdi/sat-ws-descarga-masiva para evitar getResponse crash.
- Sweep de jobs stale con thresholds ajustados.
Auth/Web:
- Primer pago de suscripción define periodo activo en webhook.
- Rate limit de login: 25 intentos / 15 min.
- Recuperación de contraseña: 24h de validez.
- Soporte viewingTenantId en contribuyentes.
- Timeout y estado de carga al crear organización en Facturapi.
- Filtros por cliente y cartera en Mis Asignados.
- Orden alfabético en selector de contribuyente.
DB:
- Migración 057: unique index de declaraciones incluye impuestos.
- processDailySync ejecuta metadata histórica únicamente los domingos en CDMX.
- Errores 404 en daily se registran en error_message sin abortar el job.
- Timeouts/transitorios reales siguen lanzándose para reintento.
- Polling reducido a 9 intentos máximos cada 5 minutos por solicitud.
- Actualiza docs/SAT-SYNC-IMPLEMENTATION.md con arquitectura, crons, manejo de errores y comandos.
- Monitor de sincronización SAT (sat-sync-monitor.job + alerta por correo).
- Scraper de CSF más robusto (iframes, blobs, popups, validación PDF).
- Reactivación de contribuyentes desactivados y limpieza al desactivar.
- Timeout de constancia aumentado a 5 min.
- Variables de entorno SAT en .env.example y env.ts.
- 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.
- Nueva tabla tenant notification_role_preferences para guardar (email_type, role, enabled).
- Migración 051 aplicada a todos los tenants.
- Backend expone endpoint /notificaciones con matriz de preferencias por rol.
- Filtrado por rol en documento_subido, weekly_update, subscription_expiring,
alertas_nuevas y recordatorio_proximo.
- Frontend rediseñado como tabla notificación × rol con toggles inmediatos.
- weekly_update y subscription_expiring están implementadas; se marcan como active.
- Se indica con badge 'A nivel despacho' cuando una notificación no se puede
desactivar por contribuyente y se deshabilita el toggle.
- Expone subscription trial_expired en /despachos/me/plan e incluye planPrice.
- Para Business Control/Enterprise (>$10k) genera pago anual único con MP Preference
en lugar de preapproval recurrente; el webhook activa 1 año de suscripción.
- Muestra CTA de pago en UI cuando la suscripción está trial/trial_expired.
- Agrega campo mp_preference_id a subscriptions y mejora mensajes de error MP.
- Revisa si el sync diario falló o si hay CFDIs vigentes sin xml_original.
- Si detecta facturas incompletas, lanza un sync initial con rango extendido
(desde un mes antes de la factura incompleta más antigua hasta ayer).
- Corre secuencialmente por contribuyente para no saturar al SAT.
- Incluye soporte para tenants legacy sin contribuyentes.
- Extiende KpiData con ncsEmitidas, ncsEmitidasPorRegimen, ncsRecibidas y ncsRecibidasPorRegimen.
- En getKpis se reutilizan calcularNcsEmitidasPorRegimen y calcularNcsRecibidasPorRegimen en paralelo.
- En el dashboard se agregan dos KpiCard y su desglose por régimen.
- sat sweep-stale-jobs: increase initial/custom sync threshold 8h→24h to prevent watchdog killing long historical syncs
- sat-client: fix formatDateForSat same-day rejection by auto-adjusting fechaFin
- sat-sync job: check fiel_contribuyente in addition to fiel_credentials for cron eligibility
- database: extend pool idle cleanup from 5min to 12h to prevent pool closure during long syncs
- webhook controller: auto-extend currentPeriodEnd on recurring MercadoPago payments
- invoicing service: auto-send FacturAPI invoice by email after creation
- admin-clientes: fix no-renovaciones detection to include expired trials and deleted subscriptions
- Backend inviteUsuario: permite owner, cfo y supervisor
- Backend valida que supervisor solo pueda invitar rol cliente
- Backend addClienteAcceso: supervisor solo puede asignar contribuyentes
que tenga visibles (getEntidadesVisibles)
- Frontend: supervisor ve botón Invitar Usuario y solo puede seleccionar
rol Cliente en el dropdown
- Backend: getSupervisor devuelve supervisorNombre desde Prisma
- Frontend: usa SelectTrigger con renderizado manual del label seleccionado
en lugar de depender de SelectValue, que no siempre encontraba el texto
del SelectItem cuando el supervisor no estaba en la lista de carteras
- Backend getSupervisor ahora devuelve supervisorNombre buscando en Prisma
- Frontend usa supervisorNombre para mostrar en Select cuando el supervisor
no está en la lista de carteras/supervisores
- Agrega migración 050 con columnas de aprobación de cliente
(requiere_aprobacion_cliente, estado_cliente, aprobado_por_cliente, etc.)
- Backend: endpoints /aprobar-cliente y /rechazar-cliente con validación de permisos
- Backend: list/download permiten acceso a clientes filtrando por entidades visibles
- Backend: notificación por email a clientes cuando se les solicita aprobación
- Frontend: checkbox independiente para solicitar aprobación del cliente
- Frontend: badge de estado combinado (owner + cliente)
- Frontend: botones de aprobar/rechazar para clientes en su propio flujo
- 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
- Agrega helper withJitOff en impuestos.service.ts
- Ejecuta getResumenIva, getIvaMensual y readResumenIvaFromCache con SET LOCAL jit = off
- Evita compilación JIT de ~17s en queries con costo estimado alto
feat(contribuyentes): auto-asignar a cartera del supervisor
- Al crear contribuyente con supervisorUserId, se agrega automáticamente
a todas las carteras top-level del supervisor
feat(permisos): restricciones de UI por rol en contribuyentes
- Oculta botón Add-ons para roles distintos de owner/cfo
- Oculta botón Eliminar contribuyente para no-owner
- Oculta botón Agregar RFC para auxiliar/visor/cliente/contador
feat(cfdi): ver CFDI desde conceptos y forma de pago en Excel
- Agrega botón Ver CFDI en cada fila de la tabla de Conceptos
- Agrega columna Forma de Pago en export Excel de CFDIs
- Agrega columna Forma de Pago en export individual de CFDI
chore(migraciones): índices GIN para relaciones de activos
- 048: índices btree parciales para activos
- 049: índices GIN para cfdis_relacionados y uuid_relacionado
- Obligaciones: las obligaciones activas sin registro en obligacion_periodos
para el periodo actual ahora se cuentan como pendientes (antes daban 0)
- Tareas: se materializan los periodos antes de contar para que las tareas
sin registro previo aparezcan como pendientes
- Usa CTEs separadas para obligaciones y tareas evitando producto cartesiano
- Backend: agrega 'supervisor' a ROLES_UPLOAD en documentos.controller.ts
- Frontend: agrega 'supervisor' a ROLES_UPLOAD y ROLES_UPLOAD_EXTRA en
documentos/page.tsx para habilitar botones de subir declaración,
comprobante de pago, eliminar y subir PDFs extra
- Backend: agrega 'supervisor' a authorize() de rutas:
- POST/DELETE /contribuyentes/:id/fiel
- POST /contribuyentes/:id/facturapi/csd
- POST/DELETE /contribuyentes/:id/obligaciones/*
- Frontend: muestra tarjeta 'Obligaciones Fiscales' en /configuracion
para rol supervisor
- Cambia la opción 'SUELDOS' por 'ISN' (Impuesto Sobre Nómina)
- Agrega nueva opción 'ISH' (Impuesto Sobre Hospedaje)
- ISH no cierra alertas ni obligaciones (aún no hay flujo definido)
- ISN mantiene keywords de sueldos/salarios/nómina + agrega 'isn'
- Migración 047: actualiza declaraciones históricas SUELDOS→ISN en BD
- Backend: POST /cfdi/download-xmls acepta CfdiFilters, usa getXmlsByFilters con LIMIT 1000
- Frontend: eliminados checkboxes y estado selectedIds; botón Descargar XMLs usa filtros activos
- Si >1000 resultados, muestra confirm() de advertencia pero permite proceder
- Agregada documentación técnica y changelog
- Migracion 046: tablas obligacion_asignaciones y tarea_asignaciones
- Servicio y controller de asignaciones (CRUD + listados)
- Fix: enviar correo welcome al invitar usuario nuevo
- Fix: quitar JOIN users de queries tenant (usar Prisma en BD central)
- Fix: req.params.obligacionId correcto en asignaciones controller
- Fix: orden rutas estaticas antes de dinamicas en cartera.routes
- Fix: owner/cfo ven todas las asignaciones en getAsignacionesPorSupervisor
- Fix: validar que entidad pertenezca a cartera padre en subcartera
- Nuevo endpoint GET /carteras/asignaciones/sin-asignar
- Nuevo endpoint GET /tareas/mis-tareas
- Frontend: muestra input 'No. Cuenta Predial' en sección 'Datos del Inmueble'
cuando el régimen del emisor es 606 (Arrendamiento), antes de Conceptos
- Frontend: incluye cuentaPredial en payload; se resetea al cambiar contribuyente
- Backend: pasa property_tax_account a nivel de cada item en Facturapi
para facturapi.service.ts y contribuyente-facturapi.service.ts
- Build y deploy exitosos
- Cambia label de 'Precio Unitario (IVA incluido)' a 'Precio Unitario (sin IVA)'
- Elimina división interna price/(1+iva) en calcConcepto; ahora price es la base
- Cambia taxIncluded: true → false en payload enviado a backend
- Backend: tax_included default false en facturapi.service.ts y
contribuyente-facturapi.service.ts
- Build y deploy exitosos
- Frontend: input datetime-local visible solo para tipos I, E, T
(no P). Default al día actual a las 12:00. Se resetea al cambiar tipo.
- Frontend: validación en handleSubmit: fecha ≤ ahora y ≥ ahora-72h
- Backend controller: validación idéntica antes de consumir timbre
- Backend servicios: pasa campo 'date' al payload de Facturapi
cuando viene 'fechaEmision' en el body
- Build y deploy exitosos
Factura Global & fecha_efectiva:
- Migracion 045_factura_global.sql: periodicidad, meses_global, año_global, fecha_efectiva
- sat-parser.service.ts: extrae InformacionGlobal del XML
- sat.service.ts: calcFechaEfectiva con soporte bimestral (periodicidad 05)
- metricas-compute, dashboard, impuestos, cfdi, export, conciliacion, alertas:
reemplaza fecha_emision-1h por COALESCE(fecha_efectiva, fecha_emision-1h)
- Script recalc-metricas.ts para recalculo manual
Fallback datos fiscales tenant → contribuyente:
- contribuyente.service.ts: fetchTenantFiscalData + mergeContribuyenteWithTenant
rellena regimenFiscal, codigoPostal y domicilio cuando el contribuyente
tiene el mismo RFC que el tenant y sus campos estan vacios
- contribuyente.controller.ts y contribuyente-config.controller.ts:
pasan req.user!.tenantId al servicio
Fix critico SAT sync:
- sat.service.ts: anio_global → año_global en INSERT/UPDATE de CFDIs
(la migracion creo 'año_global' con tilde; el codigo usaba 'anio_global',
causando fallo en 100% de inserciones de CFDI)
- determineChunkMonths: salta sondeo si existe job previo con requestIds
- MAX_POLL_ATTEMPTS: 45 → 500 (~8h) para syncs iniciales grandes
Docs:
- docs/sessions/2026-05-22-factura-global-contribuyente-fallback.md
- Inicializar saldo_pendiente_mxn al emitir facturas I/PPD vía Facturapi
(antes quedaba NULL y no aparecían en complemento de pago)
- Validar ownership en cancelación: backend rechaza 403 si el caller
intenta cancelar una factura de otro contribuyente
- Frontend: ocultar botón cancelar si no se es el emisor de la factura
- Frontend: enviar contribuyenteId en la petición de cancelación
El controller ahora devuelve 504 (gateway timeout) con mensaje claro
en vez de 500 genérico cuando el scraper del SAT excede el tiempo.
Anteriormente solo capturaba errores con 'FIEL' en el mensaje;
los timeouts de page.waitForURL se escapaban como 500.