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:
- Panel donde el contador sube el PDF de declaraciones mensuales del cliente (normal + complementarias) y se desactivan los recordatorios automáticamente.
- 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 conpdf_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: reemplazalink_pago TEXTporpdf_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 alertasdecl-<impuesto>-YYYY-MM-*de los impuestos seleccionados. - Tipo
complementaria: resuelvedecl-*ypago-*(la complementaria sustituye a la normal en pago — ya no hay que pagar la normal). - Mapping
Impuesto → prefijo de alerta:IVA→decl-iva+pago-ivaISR→decl-isr+pago-isrIEPS→decl-ieps+pago-iepsSUELDOS→decl-sueldos(no tiene pago)DIOT→diot(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 porfecha_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 elinput[type=file]se reintenta condispatchEvent('click').sat-csf-scraper.ts: el botón "Generar Constancia" vive en un iframe JSF legacy (rfcampc.siat.sat.gob.mx/PTSC/...). IteramosappPage.frames(). 3 rutas de extracción del PDF: download event, popup viewer (blob:/data:/http), response interception.sat-csf-parser.ts: parser PF+PM. Labelskey:valuecon 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 deA 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 portipoVialidad + 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álogoregimenes(clave SAT + descripcion). Normalización: strip "Régimen", "de las/los", NFD+accents, lowercase; matching por===oincludes. Reemplaza toda la listatenant_regimenes_activoscon lo que diga la CSF (solo regímenes sinfechaFin).
El usuario puede sobreescribir manualmente después desde /configuracion — cada consulta de CSF vuelve a pisar.
Disparadores
- Cron mensual día 1 04:00 AM (
0 4 1 * *) — ensat-sync.job.ts, junto con los demás. - Primer upload de FIEL — en
fiel.service.tsse detecta!existingFiel || !existingFiel.isActivey se disparan Opinión + CSF conimport()fire-and-forget. No bloquea la respuesta. - Manual —
POST /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.tsxreescrito conTabsde 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.sqlapps/api/src/migrations/tenant/004_declaraciones_liga_pago_pdf.sqlapps/api/src/migrations/tenant/005_create_constancias_situacion_fiscal.sqlapps/api/src/services/declaraciones.service.tsapps/api/src/services/constancia.service.tsapps/api/src/services/sat/sat-csf-login.tsapps/api/src/services/sat/sat-csf-scraper.tsapps/api/src/services/sat/sat-csf-parser.ts
Frontend:
apps/web/lib/api/declaraciones.tsapps/web/lib/api/constancias.tsapps/web/lib/hooks/use-declaraciones.tsapps/web/lib/hooks/use-constancias.ts
Archivos modificados
apps/api/src/controllers/documentos.controller.ts— endpoints declaraciones + constanciasapps/api/src/routes/documentos.routes.ts— rutas nuevasapps/api/src/jobs/sat-sync.job.ts— cron CSF mensual + purge 5 añosapps/api/src/services/fiel.service.ts— trigger on first uploadapps/web/app/(dashboard)/documentos/page.tsx— tabs + UI completa
Pendientes futuros
- Al detectar cambios en domicilio/régimen/obligaciones entre consultas de CSF, levantar alerta al contador y owner (notificación in-app + email opcional).
- Si el
estatusPadron!= ACTIVO, alerta de alta prioridad (análoga a la de Opinión Negativa). - Eventualmente exponer
actividadesEconomicasen la UI de configuración para que el contador las revise.