- sat-sync.job.ts: watchdog a 4h que limpia jobs huérfanos y retoma
satRequestId existente antes de crear nuevo job (evita desperdiciar
consultas SAT). Fallback a startSync si no se puede retomar.
- sat.service.ts: nueva función resumeSatSync() para verificar y
descargar paquetes de un job huérfano usando su satRequestId.
- impuestos.service.ts: reescritura completa del cálculo ISR con
modelo de caja y 3 buckets:
* Ingresos: I PUE emitidas + P recibidos
* Deducciones: I PUE recibidas + P emitidos - E PUE recibidas
* Corregidos nombres de columnas type/status vs tipo/estado
- tenants.service.ts: integración Metabase (register/delete db)
SAT sync enhancements:
- Filter active (vigente) CFDIs only via DocumentStatus to avoid SAT
rejecting recibidos with "No se permite descarga de XML cancelados"
- Reclassify CFDIs at save time: tipo='ingreso' received by tenant
becomes 'egreso' based on RFC (emisor vs receptor)
- Fix pool cleanup bug during long syncs: refresh getPool() on each
saveCfdis call instead of holding stale reference for 45+ minutes
- Add X-View-Tenant support to SAT controller via viewingTenantId
- Add tenantMiddleware to SAT routes for global admin impersonation
Cron jobs:
- Add separate every-6-hours schedule for specific RFCs
- ROEM691011EZ4 configured for frequent sync (00, 06, 12, 18 MX time)
XML filesystem export:
- Write .xml files to /var/horux/xml/<RFC>/YYYY/MM/UUID.xml
- Activated per-RFC via XML_EXPORT_RFCS allowlist
- Organized by year/month for browsability
Auth improvements:
- Send welcome + admin-notification emails on /auth/register
(previously only /tenants createTenant flow sent emails)
- Set role='contador' for self-registered users (not admin) to prevent
new tenants from accessing cross-tenant data
Infrastructure:
- Set express trust proxy=1 to accept X-Forwarded-For from Nginx
(fixes ERR_ERL_UNEXPECTED_X_FORWARDED_FOR from rate limiter)
Operational scripts:
- setup-horux360-tenant.ts: Provision Horux 360 tenant manually
- send-welcome-aaron.ts: Resend welcome email for Aaron (registered
before welcome-on-register was added)
- export-xmls-roem.ts: Backfill filesystem XMLs from DB for ROEM
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CRITICAL fixes:
- Restrict X-View-Tenant impersonation to global admin only (was any admin)
- Add authorization to subscription endpoints (was open to any user)
- Make webhook signature verification mandatory (was skippable)
- Remove databaseName from JWT payload (resolve server-side with cache)
- Reduce body size limit from 1GB to 10MB (50MB for bulk CFDI)
- Restrict .env file permissions to 600
HIGH fixes:
- Add authorization to SAT cron endpoints (global admin only)
- Add Content-Security-Policy and Permissions-Policy headers
- Centralize isGlobalAdmin() utility with caching
- Add rate limiting on auth endpoints (express-rate-limit)
- Require authentication on logout endpoint
MEDIUM fixes:
- Replace Math.random() with crypto.randomBytes for temp passwords
- Remove console.log of temporary passwords in production
- Remove DB credentials from admin notification email
- Add escapeHtml() to email templates (prevent HTML injection)
- Add file size validation on FIEL upload (50KB max)
- Require TLS for SMTP connections
- Normalize email to lowercase before uniqueness check
- Remove hardcoded default for FIEL_ENCRYPTION_KEY
Also includes:
- Complete production deployment documentation
- API reference documentation
- Security audit report with remediation details
- Updated README with v0.5.0 changelog
- New client admin email template
- Utility scripts (create-carlos, test-emails)
- PM2 ecosystem config updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "Pagar ahora" button generates MercadoPago link and opens in new tab
- Billing period card shows start/end dates and days until next payment
- Warning banners: expired (red), expiring soon (yellow), pending payment
- Improved payment history with icons and translated payment methods
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add plan field to UserInfo shared type
- Subscription API client and React Query hooks
- Client subscription page with status + payment history
- Sidebar navigation filtered by tenant plan features
- Subscription link added to navigation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- checkPlanLimits: blocks writes when subscription inactive
- checkCfdiLimit: enforces per-plan CFDI count limits
- requireFeature: gates reportes/alertas/calendario by plan tier
- All cached with 5-min TTL, invalidated via PM2 messaging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MercadoPago PreApproval integration for recurring subscriptions
- Subscription service with caching, manual payment, payment history
- Webhook handler with HMAC-SHA256 signature verification
- Admin endpoints for subscription management and payment links
- Email notifications on payment success/failure/cancellation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EmailService with mock fallback when SMTP not configured.
Templates: welcome, fiel-notification, payment-confirmed,
payment-failed, subscription-expiring, subscription-cancelled.
Uses Google Workspace SMTP (STARTTLS).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Decrypts .cer and .key from FIEL_STORAGE_PATH/<RFC>/ to /tmp with
30-minute auto-cleanup for security.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Save encrypted .cer, .key, and metadata to FIEL_STORAGE_PATH alongside
the existing DB storage. Each file has separate .iv and .tag sidecar files.
Filesystem failure is non-blocking (logs warning, DB remains primary).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete schema-manager.ts (replaced by TenantConnectionManager).
Remove deprecated tenantSchema from Express Request interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Close all tenant DB pools on SIGTERM/SIGINT for clean restarts.
Support PM2 cluster invalidate-tenant-cache messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Prisma raw queries with pg.Pool for all tenant-scoped services:
cfdi, dashboard, impuestos, alertas, calendario, reportes, export, and SAT.
Controllers now pass req.tenantPool instead of req.tenantSchema.
Fixes SQL injection in calendario.service.ts (parameterized interval).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace inline schema SQL with tenantDb.provisionDatabase
- Delete now soft-deletes DB (rename) and invalidates pool
- Use PLANS config for default limits per plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace createTenantSchema with tenantDb.provisionDatabase
- JWT payload now includes databaseName (already renamed from schemaName)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Resolve tenant DB via TenantConnectionManager instead of SET search_path
- Add tenantPool to Express Request for direct pool queries
- Keep tenantSchema as backward compat until all services are migrated
- Support admin impersonation via X-View-Tenant header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Adds pg dependency for direct PostgreSQL connections to tenant DBs
- TenantConnectionManager: singleton managing Map<tenantId, Pool>
- provisionDatabase: creates new DB with tables and indexes
- deprovisionDatabase: soft-deletes by renaming DB
- Automatic idle pool cleanup every 60s (5min threshold)
- Max 3 connections per pool (6/tenant with 2 PM2 workers)
- Graceful shutdown support for all pools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename Tenant.schemaName to databaseName across all services
- Add Subscription and Payment models to Prisma schema
- Update FielCredential to per-component IV/tag encryption columns
- Switch FIEL encryption key from JWT_SECRET to FIEL_ENCRYPTION_KEY
- Add Subscription and Payment shared types
- Update JWTPayload to use databaseName
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix metadata.json shown as unencrypted in tree (now .enc)
- Fix admin bypass order in checkPlanLimits (moved before status check)
- Add PM2 cross-worker cache invalidation via process messaging
- Fix fiel_credentials "no changes" contradiction with per-component IV
- Backup all tenant DBs regardless of active status
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add export to Excel button with xlsx library for filtered data
- Add keyboard shortcuts (Esc to close popovers/forms)
- Add print button to invoice viewer modal with optimized print styles
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace "Cargando..." text with animated skeleton rows
- Mimics table structure while loading
- Improves perceived loading speed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Database optimizations:
- Add indexes on fecha_emision, tipo, estado, rfc_emisor, rfc_receptor
- Add trigram indexes for fast ILIKE searches on nombre fields
- Combine COUNT with main query using window function (1 query instead of 2)
Frontend optimizations:
- Add 300ms debounce to autocomplete searches
- Add staleTime (30s) and gcTime (5min) to useCfdis hook
- Reduce unnecessary API calls on every keystroke
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add /cfdi/emisores and /cfdi/receptores API endpoints
- Search by RFC or nombre with ILIKE
- Show suggestions dropdown while typing (min 2 chars)
- Click suggestion to select and populate filter input
- Show loading state while searching
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ::date cast to fechaInicio filter
- Add ::date cast and +1 day interval to fechaFin to include full day
- Fixes "operator does not exist: timestamp >= text" error
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Set z-index to 9999 to ensure popover appears above all elements
- Add explicit white background for better visibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create Popover component using Radix UI
- Add filter icon next to Fecha, Emisor, Receptor headers
- Each icon opens a popover with filter inputs
- Show active filters as badges in card header
- Filter icons highlight when filter is active
- Apply filters on Enter or click Apply button
- Remove filters individually with X on badge
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add emisor and receptor filters to CfdiFilters type
- Update backend service to filter by emisor/receptor (RFC or nombre)
- Update controller and API client to pass new filters
- Add toggle button to show/hide column filters in table
- Add date range inputs for fecha filter
- Add text inputs for emisor and receptor filters
- Apply filters on Enter key or search button click
- Add clear filters button when filters are active
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add gradient header with emisor info and prominent serie/folio
- Improve status badges with pill design
- Add receptor section with left accent border
- Show complete uso CFDI descriptions
- Create card grid for payment method, forma pago, moneda
- Improve conceptos table with zebra striping and SAT keys
- Add elegant totals box with blue footer
- Enhance timbre fiscal section with QR placeholder and SAT URL
- Add update-cfdi-xml.js script for bulk XML import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PostgreSQL requires explicit type cast when comparing UUID columns
with text parameters in raw queries.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Eye button to table rows to view invoice
- Add loading state while fetching CFDI details
- Integrate CfdiViewerModal component
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Detailed step-by-step implementation plan for:
- PDF-like invoice visualization
- PDF download via html2pdf.js
- XML download endpoint
- Modal integration in CFDI page
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Design for PDF-like invoice visualization with:
- Modal viewer with invoice preview
- PDF download via html2pdf.js
- XML download from stored data
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add explicit IRouter type to all route files
- Add explicit Express type to app.ts
- Fix env.ts by moving getCorsOrigins after parsing
- Fix token.ts SignOptions type for expiresIn
- Cast req.params.id to String() in controllers
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add authentication check using useAuthStore
- Redirect unauthenticated users to /login
- Show loading state while auth store hydrates
- Remove "Demo UI sin backend" text from production
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Wrap token refresh logic in Prisma transaction
- Use deleteMany instead of delete to handle race conditions gracefully
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>