Initial commit - Horux Despachos NL

This commit is contained in:
2026-05-03 16:47:53 -06:00
commit b00b677c54
647 changed files with 133843 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
# 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.