Files
HoruxDespachos/docs/plans/2026-04-14-documentos-csf-declaraciones.md
2026-04-27 22:09:36 -06:00

6.7 KiB

Documentos: Declaraciones Provisionales + Constancia de Situación Fiscal

Fecha: 2026-04-14 Autores: Carlos e Ivan (Horux360)

Contexto

/documentos solo tenía Opinión de Cumplimiento. Pedimos:

  1. Panel donde el contador sube el PDF de declaraciones mensuales del cliente (normal + complementarias) y se desactivan los recordatorios automáticamente.
  2. Descarga automática mensual de la Constancia de Situación Fiscal (CSF) del portal SAT, con auto-fill del domicilio fiscal y regímenes activos del tenant.

Alcance

/documentos se reorganizó en 3 pestañas:

  • Opinión de Cumplimiento (pre-existente)
  • Constancia de Situación Fiscal (nueva)
  • Declaraciones Provisionales (nueva)

Declaraciones Provisionales

Schema

  • Migración 003_create_declaraciones_provisionales.sql: tabla con pdf_declaracion BYTEA, pdf_pago BYTEA, impuestos TEXT[] (IVA/ISR/IEPS/SUELDOS/DIOT/OTRO), tipo (normal/complementaria). UNIQUE INDEX parcial para 1 normal por (año, mes).
  • Migración 004_declaraciones_liga_pago_pdf.sql: reemplaza link_pago TEXT por pdf_liga_pago BYTEA + pdf_liga_pago_filename (la liga de pago es PDF de línea de captura, no URL).

Reglas de auto-resolución de alertas

  • Tipo normal: resuelve alertas decl-<impuesto>-YYYY-MM-* de los impuestos seleccionados.
  • Tipo complementaria: resuelve decl-* y pago-* (la complementaria sustituye a la normal en pago — ya no hay que pagar la normal).
  • Mapping Impuesto → prefijo de alerta:
    • IVAdecl-iva + pago-iva
    • ISRdecl-isr + pago-isr
    • IEPSdecl-ieps + pago-ieps
    • SUELDOSdecl-sueldos (no tiene pago)
    • DIOTdiot (solo declaración)
    • OTRO → sin mapping

Subida de comprobante de pago

Endpoint separado POST /documentos/declaraciones/:id/comprobante-pago que solo acepta el PDF. Al subir, se resuelven las alertas pago-* del mes.

Retención

5 años (CFF Art. 30). Purge en cron lifecycle 2:30 AM.

Constancia de Situación Fiscal

Schema

  • Migración 005_create_constancias_situacion_fiscal.sql: pdf BYTEA, datos JSONB (shape completo), rfc, id_cif, razon_social, estatus_padron, fecha_emision. Index DESC por fecha_consulta.

Scraper portado del prototipo

Del proyecto standalone sat-csf-prototype/ (en Downloads):

  • sat-csf-login.ts: navegación portal SAT → popup SERVICIO → login FIEL. Fix crítico: el click sintético a "e.firma" a veces no dispara el handler del SAT, por eso si tras 10s no aparece el input[type=file] se reintenta con dispatchEvent('click').
  • sat-csf-scraper.ts: el botón "Generar Constancia" vive en un iframe JSF legacy (rfcampc.siat.sat.gob.mx/PTSC/...). Iteramos appPage.frames(). 3 rutas de extracción del PDF: download event, popup viewer (blob:/data:/http), response interception.
  • sat-csf-parser.ts: parser PF+PM. Labels key:value con lookahead a la siguiente label o sección. Tablas (actividades/regímenes/obligaciones) agrupadas por "chunk termina en dd/mm/yyyy" + filtro de page-break noise. Obligaciones PM requieren regex extendido (Dentro de, Mensualmente, etc. además de A más tardar).

Auto-fill de domicilio y regímenes

sincronizarDatosFiscales(tenantId, csf) en constancia.service.ts:

  • Domicilio fiscal — actualiza campos tenants: codigo_postal, calle (compuesta por tipoVialidad + nombreVialidad), num_exterior, num_interior (ignora "SIN NUMERO"), colonia, ciudad (← localidad), municipio, estado (← entidadFederativa). Solo actualiza campos cuando el CSF trae valor no-vacío — nunca pisa con null.
  • Regímenes activos — matchea csf.regimenes (nombre libre) contra catálogo regimenes (clave SAT + descripcion). Normalización: strip "Régimen", "de las/los", NFD+accents, lowercase; matching por === o includes. Reemplaza toda la lista tenant_regimenes_activos con lo que diga la CSF (solo regímenes sin fechaFin).

El usuario puede sobreescribir manualmente después desde /configuracion — cada consulta de CSF vuelve a pisar.

Disparadores

  1. Cron mensual día 1 04:00 AM (0 4 1 * *) — en sat-sync.job.ts, junto con los demás.
  2. Primer upload de FIEL — en fiel.service.ts se detecta !existingFiel || !existingFiel.isActive y se disparan Opinión + CSF con import() fire-and-forget. No bloquea la respuesta.
  3. ManualPOST /documentos/constancias/consultar (owner/cfo, rate limit 2/día).

Retención

5 años. Purge en cron lifecycle 2:30 AM junto con declaraciones.

Headless

chromium.launch({ headless: true }) por default (no se ve ventana al usuario). Flag SAT_HEADLESS=false en .env para debug visual temporal. El fix del dispatchEvent hace que headless sea confiable.

UI

  • apps/web/app/(dashboard)/documentos/page.tsx reescrito con Tabs de 3 pestañas.
  • OpinionTab: preserva funcionalidad existente.
  • ConstanciaTab: detalle de último CSF (identificación / domicilio / regímenes / obligaciones) + historial de 12 con detalle expandible por row + descarga PDF + "Consultar ahora".
  • DeclaracionesTab: selector de año, tabla mensual, dialog para subir declaración (multi-select de impuestos, PDF declaración obligatorio, liga de pago y notas opcionales), dialog para subir comprobante de pago, descarga PDFs.

Archivos creados

Backend:

  • apps/api/src/migrations/tenant/003_create_declaraciones_provisionales.sql
  • apps/api/src/migrations/tenant/004_declaraciones_liga_pago_pdf.sql
  • apps/api/src/migrations/tenant/005_create_constancias_situacion_fiscal.sql
  • apps/api/src/services/declaraciones.service.ts
  • apps/api/src/services/constancia.service.ts
  • apps/api/src/services/sat/sat-csf-login.ts
  • apps/api/src/services/sat/sat-csf-scraper.ts
  • apps/api/src/services/sat/sat-csf-parser.ts

Frontend:

  • apps/web/lib/api/declaraciones.ts
  • apps/web/lib/api/constancias.ts
  • apps/web/lib/hooks/use-declaraciones.ts
  • apps/web/lib/hooks/use-constancias.ts

Archivos modificados

  • apps/api/src/controllers/documentos.controller.ts — endpoints declaraciones + constancias
  • apps/api/src/routes/documentos.routes.ts — rutas nuevas
  • apps/api/src/jobs/sat-sync.job.ts — cron CSF mensual + purge 5 años
  • apps/api/src/services/fiel.service.ts — trigger on first upload
  • apps/web/app/(dashboard)/documentos/page.tsx — tabs + UI completa

Pendientes futuros

  1. Al detectar cambios en domicilio/régimen/obligaciones entre consultas de CSF, levantar alerta al contador y owner (notificación in-app + email opcional).
  2. Si el estatusPadron != ACTIVO, alerta de alta prioridad (análoga a la de Opinión Negativa).
  3. Eventualmente exponer actividadesEconomicas en la UI de configuración para que el contador las revise.