Reportes R21 y RAMCI
GM Fiscal genera dos reportes fiscales oficiales a partir de los datos de una conciliación completada: el R21 (declaración mensual de IVA) y el RAMCI (reporte anual de movimientos contables y de inversión). Ambos se producen en formato Excel compatible con el SAT y se entregan al cliente de forma asincrónica mediante WebSocket.
Modelo de generación asincrónica
El endpoint POST /api/reporte persiste el reporte con estado Pendiente y responde 202 Accepted de forma inmediata. El procesamiento real ocurre en el ReporteWorker:
sequenceDiagram
participant C as Contador
participant API as API REST
participant DB as PostgreSQL
participant W as ReporteWorker
participant AZ as Azure Blob
participant WS as WebSocket
C->>API: POST /api/reporte
API->>DB: Guarda reporte (Pendiente)
API-->>C: 202 Accepted
loop Ticker cada 10 segundos
W->>DB: Consulta reportes Pendientes
W->>W: R21Processor + RAMCIProcessor
W->>W: excelize → Excel
W->>AZ: Sube archivo
W->>DB: Actualiza estatus → Completado
W->>WS: Notifica al RFC
end
WS-->>C: Reporte disponible (URL descarga)
El worker procesa hasta 3 reportes simultáneos mediante un semáforo (chan struct{}{}). Los reportes que superan ese límite esperan en cola hasta que se libere un slot.
R21 — Declaración mensual de IVA
El R21 es el reporte de declaración de IVA que la empresa presenta al SAT mensualmente. El R21Processor (src/domain/reporte/r21_processor.go) aplica la clasificación de cada CFDI según el rol del contribuyente en la transacción.
Clasificación por tipo de CFDI
Tipo I (Ingreso): Emisor = contribuyente → IVA Causado Receptor = contribuyente → IVA Acreditable
Tipo E (Egreso / nota de crédito): Emisor = contribuyente → IVA Causado (nota crédito emitida) Receptor = contribuyente → IVA Acreditable
Tipo N (Nómina): Emisor = contribuyente → ISR Retenido (Nómina)
Tipo P (Pago): SOLO receptor = contribuyente contribuye al R21 El emisor de un CFDI tipo P NO contribuye al reporteEstructura del reporte R21
R21 — Declaración mensual de IVA├── IVA Causado (contribuyente como emisor: tipos I y E)│ ├── Tasa 16%: base imponible + IVA│ ├── Tasa 8%: base imponible + IVA (zona fronteriza)│ └── Tasa 0%: base imponible (exportaciones, alimentos, medicinas)├── IVA Acreditable (contribuyente como receptor: tipos I, E y P-receptor)│ ├── Tasa 16%: base imponible + IVA│ ├── Tasa 8%: base imponible + IVA│ └── Tasa 0%: base imponible├── IVA Retenido (retenido por clientes al contribuyente emisor)├── ISR Retenido (nómina: ISR pagado a empleados, CFDIs tipo N)└── IVA a Pagar / Favor: causado − acreditable − retenidoTasas de IVA válidas (SAT 2024)
| Tasa | Código | Aplicación |
|---|---|---|
0.160000 | 002 | IVA general (16%) |
0.080000 | 002 | IVA zona fronteriza norte (8%) |
0.000000 | 002 | IVA tasa cero: exportaciones, alimentos, medicinas |
Los códigos de impuesto SAT son:
| Código | Impuesto |
|---|---|
001 | ISR |
002 | IVA |
003 | IEPS |
Exclusiones obligatorias del R21
Los CFDIs cancelados (EstatusERP = "CANCELADO") nunca se incluyen en el cálculo del R21. Incluirlos inflaría el IVA declarado y generaría diferencias frente al SAT.
RAMCI — Reporte anual de movimientos
El RAMCI es el reporte anual que clasifica todos los comprobantes fiscales de la empresa en cinco categorías según el tipo de CFDI y el rol del contribuyente.
Categorías RAMCI
| Categoría | Descripción | Condición |
|---|---|---|
R | Ingresos | CFDI tipo I, contribuyente es emisor |
A | Egresos | CFDI tipo E, contribuyente es emisor |
M | Nómina | CFDI tipo N, contribuyente es emisor |
C | Notas de Crédito | CFDI tipo E, contribuyente es receptor |
I | Ingresos recibidos | CFDI tipo I, contribuyente es receptor |
Exclusiones obligatorias del RAMCI
Dos categorías nunca deben aparecer en el RAMCI:
- Categoría
"General"— se excluye siempre. - CFDIs tipo
P(Pagos) — los pagos no forman parte del reporte anual de movimientos.
// Excluir antes de procesar:if categoria == "General" || cfdi.TipoDeComprobante == "P" { continue // no incluir en RAMCI}Reclasificación por retenciones
Si un CFDI de tipo Egreso (E) contiene retenciones de ISR significativas, el RAMCIProcessor puede reclasificarlo como Nómina (M). Esta reclasificación se aplica automáticamente durante el procesamiento.
Contenido de cada categoría
| Categoría | Campos incluidos |
|---|---|
| R / I | Subtotal + IVA + Total por comprobante |
| A / C | Subtotal + IVA + Total por comprobante |
| M | Percepciones + Deducciones + ISR Retenido |
Helpers de impuestos del dominio
El paquete src/domain/factura/impuestos.go provee funciones puras para operar sobre listas de impuestos:
SumarIVATrasladado(impuestos []Impuesto) float64// Suma IVA trasladado (código "002", tipo "Tasa")
SumarIVARetenido(impuestos []Impuesto) float64// Suma IVA retenido (código "002", tipo "Retencion")
SumarISRRetenido(impuestos []Impuesto) float64// Suma ISR retenido (código "001")
TasaIVAPredominante(impuestos []Impuesto) float64// Devuelve 0.16, 0.08 o 0.00 según la tasa mayoritariaEl uso correcto preserva el origen de cada array:
ivaTotal := factura.SumarIVATrasladado(fc.ImpuestosProdigia)isrTotal := factura.SumarISRRetenido(fc.ImpuestosERP)Utilidades matemáticas R21MathUtils
La precisión de los cálculos fiscales está centralizada en la interfaz R21MathUtils del dominio. Todos los montos del R21 y el RAMCI deben calcularse exclusivamente con estos métodos:
type R21MathUtils interface { RedondearMonto(x float64) float64 // Half-up según SAT (0.005 → arriba) CompararMontos(a, b float64) bool // Igualdad con tolerancia 0.005 CalcularBaseIVA16(iva float64) float64 // iva / 0.16 CalcularBaseIVA8(iva float64) float64 // iva / 0.08 AcumularMontos(montos []float64) float64 // Suma con redondeo correcto}Las funciones nativas de Go producen resultados incorrectos para cumplimiento fiscal:
| Operación | Método correcto | Método prohibido |
|---|---|---|
| Redondear | R21MathUtils.RedondearMonto(x) | math.Round(x) |
| Comparar montos | R21MathUtils.CompararMontos(a, b) | a == b |
| Formatear | R21MathUtils.RedondearMonto(x) | fmt.Sprintf("%.2f") |
Consulta y descarga de reportes
POST /api/reporte # Solicitar generaciónGET /api/reporte/{uuid} # Estado del reporteGET /api/reporte/rfc/{rfc} # Reportes por empresaGET /api/reporte/solicitud/{uuid} # Reporte de una conciliaciónDELETE /api/reporte/{uuid} # Eliminar reporteGET /api/reporte/validation # Validar estructura del reporteResumen
- El R21 clasifica las facturas por tipo de CFDI (I, E, N, P) y rol del contribuyente (emisor o receptor) para calcular IVA Causado, Acreditable, Retenido e ISR Retenido.
- El RAMCI clasifica comprobantes en cinco categorías (R/A/M/C/I) y excluye explícitamente la categoría “General” y los CFDIs tipo P.
- Los reportes se generan de forma asincrónica:
POST /api/reporteresponde 202, elReporteWorkerprocesa en segundo plano con semáforo de 3 simultáneos. - Todos los cálculos de montos usan
R21MathUtilscon redondeo half-up SAT. El uso demath.Roundofmt.Sprintf("%.2f")está prohibido. - Los CFDIs cancelados nunca se incluyen en el R21; hacerlo inflaría el IVA declarado.