# 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`:
  - `IVA` → `decl-iva` + `pago-iva`
  - `ISR` → `decl-isr` + `pago-isr`
  - `IEPS` → `decl-ieps` + `pago-ieps`
  - `SUELDOS` → `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 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. **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.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.
