Skip to content

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 reporte

Estructura 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 − retenido

Tasas de IVA válidas (SAT 2024)

TasaCódigoAplicación
0.160000002IVA general (16%)
0.080000002IVA zona fronteriza norte (8%)
0.000000002IVA tasa cero: exportaciones, alimentos, medicinas

Los códigos de impuesto SAT son:

CódigoImpuesto
001ISR
002IVA
003IEPS

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íaDescripciónCondición
RIngresosCFDI tipo I, contribuyente es emisor
AEgresosCFDI tipo E, contribuyente es emisor
MNóminaCFDI tipo N, contribuyente es emisor
CNotas de CréditoCFDI tipo E, contribuyente es receptor
IIngresos recibidosCFDI 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.
src/domain/reporte/ramci_processor.go
// 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íaCampos incluidos
R / ISubtotal + IVA + Total por comprobante
A / CSubtotal + IVA + Total por comprobante
MPercepciones + 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 mayoritaria

El 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:

src/domain/reporte/r21_math.go
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ónMétodo correctoMétodo prohibido
RedondearR21MathUtils.RedondearMonto(x)math.Round(x)
Comparar montosR21MathUtils.CompararMontos(a, b)a == b
FormatearR21MathUtils.RedondearMonto(x)fmt.Sprintf("%.2f")

Consulta y descarga de reportes

POST /api/reporte # Solicitar generación
GET /api/reporte/{uuid} # Estado del reporte
GET /api/reporte/rfc/{rfc} # Reportes por empresa
GET /api/reporte/solicitud/{uuid} # Reporte de una conciliación
DELETE /api/reporte/{uuid} # Eliminar reporte
GET /api/reporte/validation # Validar estructura del reporte

Resumen

  • 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/reporte responde 202, el ReporteWorker procesa en segundo plano con semáforo de 3 simultáneos.
  • Todos los cálculos de montos usan R21MathUtils con redondeo half-up SAT. El uso de math.Round o fmt.Sprintf("%.2f") está prohibido.
  • Los CFDIs cancelados nunca se incluyen en el R21; hacerlo inflaría el IVA declarado.