Skip to content

API Reference

Todo lo público está en el namespace UruFactura (el paquete de EF Core, en UruFactura.EntityFrameworkCore).

El cliente. Es thread-safe: registralo como singleton (como IUruFacturaClient para poder mockearlo).

public UruFacturaClient(
UruFacturaConfig config,
IUruFacturaStore? store = null, // obligatorio en Producción; en Homologación, por defecto InMemoryStore
HttpClient? httpClient = null, // p. ej. de IHttpClientFactory
IDgiClient? dgiClient = null, // transporte propio (tests, proxy)
TimeProvider? reloj = null) // reloj de las fechas fiscales

Valida la configuración (UruFacturaException, también si falta el almacenamiento en Producción) y carga el certificado (FirmaDigitalException). Con un httpClient propio, su handler debe tener el certificado de cliente TLS y la validación del servidor configurados; no se combina con dgiClient.

Método Descripción
Task RegistrarCaeAsync(Cae cae, CancellationToken ct = default) Registra un CAE (o actualiza uno existente: mismo tipo, serie y comienzo de rango). CaeException si los datos no son correctos o el rango se superpone con otro CAE del mismo tipo y serie
Task EliminarCaeAsync(Cae cae, CancellationToken ct = default) Quita un CAE registrado por error que todavía no numeró ningún CFE (CaeException si no existe o ya se usó)
Task<IReadOnlyList<Cae>> ObtenerCaesAsync(CancellationToken ct = default) Los CAE con su último número usado
Task<IReadOnlyList<string>> ObtenerAdvertenciasCaeAsync(int diasAlertaVencimiento = 7, decimal porcentajeAlertaUso = 80m, CancellationToken ct = default) CAE vencidos, agotados, por vencer o con el rango casi usado
IReadOnlyList<ErrorValidacion> Validar(Cfe cfe) Errores que impedirían emitir, con el campo de cada uno (vacía si es válido). No numera
Task<CfeEmitido> EmitirAsync(Cfe cfe, CancellationToken ct = default) Valida (CfeValidationException), toma el próximo número del CAE vigente (CaeException si no hay), genera el XML, lo firma y lo registra junto con el número en una operación atómica: si algo falla, no se consume ningún número. CfeDuplicadoException si ya se emitió uno del mismo tipo con el mismo NroInterno. No envía
Task<AcuseSobre> EnviarAsync(CfeEmitido cfe, CancellationToken ct = default) Envía el CFE en un sobre EnvioCFE. El acuse indica si la DGI recibió el sobre. InvalidOperationException si el CFE ya fue recibido o tiene resultado (no se reenvía)
Task<AcuseSobre> EnviarAsync(IReadOnlyCollection<CfeEmitido> cfes, CancellationToken ct = default) De 1 a 250 CFE en un sobre
Task<EstadoEnvio> ConsultarEnvioAsync(AcuseSobre acuse, CancellationToken ct = default) Resultado de cada comprobante del sobre (por tipo, serie y número o, si la DGI no los informa, por Nro_ordinal); actualiza su EstadoDgi y lo guarda. InvalidOperationException si el sobre no fue recibido
Task<EstadoComprobante> ConsultarCfeAsync(CfeEmitido cfe, CancellationToken ct = default) Estado de un CFE (servicio ws_consultas); actualiza su EstadoDgi y lo guarda
Task<AcuseReporte> EnviarReporteDiarioAsync(DateOnly fecha, int secEnvio = 1, CancellationToken ct = default) Reporte diario firmado con los CFE emitidos en la fecha, tomados del almacenamiento. secEnvio 1 el primero; para corregir uno ya recibido, la secuencia anterior + 1

public sealed record UruFacturaConfig
{
public Emisor Emisor { get; init; }
public Ambiente Ambiente { get; init; } // default: Homologacion
public string? RutaCertificado { get; init; } // o Certificado
public string? PasswordCertificado { get; init; }
public X509Certificate2? Certificado { get; init; } // ya cargado (Key Vault, HSM); prioridad sobre la ruta
public AlgoritmoFirma AlgoritmoFirma { get; init; } // default: Sha256
public string? UrlEnvio { get; init; } // obligatoria en Producción
public string? UrlConsultas { get; init; } // obligatoria en Producción
public TimeSpan Timeout { get; init; } // default: 30 s
public bool OmitirValidacionSsl { get; init; } // default: false; en Producción no es válida
public decimal? ValorUnidadIndexada { get; init; } // activa el tope de 5.000 UI de los e-Tickets
public IReadOnlyList<string> Validar(); // errores (vacía si es válida)
}

ToString() no muestra la contraseña. Ver Configuración.

Propiedad Tipo Descripción
Rut string 12 dígitos con dígito verificador válido
RazonSocial string Hasta 150 caracteres
DomicilioFiscal, Ciudad, Departamento string Hasta 70 / 30 / 30 caracteres
NombreComercial, Giro string? Hasta 30 / 100 caracteres
CodigoSucursal int CdgDGISucur (1-9999, default 1)
Sucursal string? EmiSucursal (hasta 20)
Telefonos IReadOnlyList<string> Hasta 2
Correo, InfoAdicional string? Hasta 80 / 150
FechaIncorporacion, UrlVerificacion string? Leyendas de la representación impresa

Records inmutables: se arman con inicializadores y se modifican con with. Las colecciones son IReadOnlyList<T> y se asignan con [ … ]; EmitirAsync las copia, así que cambiar después una lista propia no altera lo emitido.

public sealed record Cfe(TipoCfe Tipo)
Propiedad Tipo Descripción
Tipo TipoCfe Tipo de comprobante
FechaEmision DateOnly? Si se omite, la fecha de Uruguay al emitir
Emisor Emisor? Reemplaza al de la configuración (mismo RUT), p. ej. otra sucursal
Receptor Receptor? Obligatorio en e-Factura, exportación y e-Resguardo
Mandante Mandante? Obligatorio en venta por cuenta ajena
IndTraslado IndTraslado? Obligatorio en e-Remito
ClausulaVenta, ModalidadVenta, ViaTransporte string?, int?, int? Datos de exportación
FormaPago FormaPago Contado (default) o Credito
Moneda / TipoCambio Moneda / decimal? TipoCambio es obligatorio si la moneda no es el peso
NroInterno string? Hasta 50 caracteres. Identifica el comprobante en tu sistema: no se emiten dos del mismo tipo con el mismo valor (CfeDuplicadoException)
InfoAdicional string? Hasta 150 caracteres
FechaVencimiento DateOnly? Vencimiento del pago
Detalle IReadOnlyList<LineaDetalle> Al menos una línea
DescuentosRecargos IReadOnlyList<DescuentoRecargoGlobal> Hasta 20; cambian la base del IVA
MediosPago IReadOnlyList<MedioPago> Hasta 40
Referencias IReadOnlyList<RefCfe> Obligatorias en las notas (hasta 40)

Totales CalcularTotales() calcula los totales (IVA a Totales.TasaIvaMinima y Totales.TasaIvaBasica: 10 % y 22 %) con la precisión del XML.

public sealed record ErrorValidacion(string Campo, string Mensaje); // ToString(): "Campo: Mensaje"

Campo es la ruta de la propiedad dentro del Cfe (Detalle[0].PrecioUnitario, Receptor.Documento, Referencias[1].Serie, …), para mostrar el error junto al dato. Todo CFE sin errores produce un XML válido contra los XSD de la DGI.

Propiedad Tipo Descripción
NombreItem string Hasta 80 caracteres (obligatorio salvo en e-Resguardo)
Cantidad decimal Mayor a cero (3 decimales en el XML; hasta 14 enteros)
PrecioUnitario decimal Sin IVA (6 decimales; hasta 11 enteros). Los remitos no llevan precio
IndFactIva TipoIva Default Basico; en exportación se informa IndFact 10
UnidadMedida string? Hasta 4 caracteres; si se omite, N/A
Descripcion string? Hasta 1000 caracteres
DescuentoMonto, DescuentoPorcentaje, RecargoMonto decimal, decimal?, decimal No negativos; el descuento no supera Cantidad × Precio y el monto tiene prioridad sobre el porcentaje. No aplican a remitos
Codigos IReadOnlyList<CodigoItem> Hasta 5 (Tipo, Codigo)
Retenciones IReadOnlyList<RetencionPercepcion> Solo e-Resguardo, de 1 a 5 (Codigo, Tasa, MontoSujeto, Valor, InfoAdicional)
MontoTotal decimal (calculado) Cantidad × Precio − Descuento + Recargo

Las líneas se numeran por su posición.

Propiedad Tipo Descripción
Documento string? En e-Factura, un RUT uruguayo válido
TipoDocumento TipoDocumentoReceptor Default Rut
CodigoPais string ISO alfa-2 de la lista de la DGI (default UY); si no es UY, se informa como documento extranjero
RazonSocial, Direccion, Ciudad, Departamento, Pais string? En e-Remito, razón social, dirección y ciudad son obligatorias
CodigoPostal, InfoAdicional, CompraId, LugarDestinoEntrega string? Opcionales
Modelo Propiedades
RefCfe TipoCfe, Serie, NroCfe (1-9999999), FechaCfe (DateOnly, no posterior a la emisión), MontoCfeRef y MonedaCfeRef (obligatorios en notas), TipoCambioCfeRef, Razon (hasta 90)
Mandante TipoDocumento, CodigoPais (default UY), Documento, Nombre
DescuentoRecargoGlobal Tipo (Descuento/Recargo), Glosa, Monto (sin IVA), IndFact (base de IVA), Codigo
MedioPago Glosa, Valor, Codigo, Orden
Totales MontoNetoExento, MontoNetoSuspendido, MontoNetoMinimo, MontoNetoBasico, IvaMinimo, IvaBasico, MontoExportacion, MontoTotal, MontoRetenido, MontoNeto; constantes TasaIvaMinima (10) y TasaIvaBasica (22)

El resultado de EmitirAsync. Los datos fiscales son inmutables; el estado ante la DGI lo actualiza el cliente al enviar y consultar (desde afuera solo se fija al construirlo, por ejemplo en un almacenamiento propio).

Propiedad Tipo Descripción
Documento Cfe Contenido emitido, con FechaEmision y Emisor resueltos
Tipo, Serie, Numero, FechaEmision, Emisor Identificación
Cae Cae CAE con el que se numeró
Totales Totales Totales informados
Xml string XML firmado (conservarlo durante el plazo legal)
FechaFirma DateTimeOffset TmstFirma
CodigoSeguridad string DigestValue de la firma; los primeros 6 caracteres se imprimen
UrlQr string Contenido del código QR
IdReceptorDgi, TokenDgi string? Sobre en que se envió
EstadoDgi EstadoCfe? Aceptado (AE), Rechazado (BE), Observado (CE) o Reliquidado (FE); null sin resultado
MensajeDgi string? Motivos del estado, o el mensaje del rechazo del sobre
AceptadoPorDgi, Enviado bool EstadoDgi == EstadoCfe.Aceptado / se envió en un sobre recibido
Propiedad Tipo Descripción
NumeroAutorizacion string CAE_ID (hasta 11 dígitos)
TipoCfe, Serie Tipo y serie que autoriza (A, AB o 1A)
RangoDesde, RangoHasta long Rango autorizado (1-9999999)
FechaVencimiento DateOnly
UltimoNroUsado long Lo actualiza el almacenamiento al numerar
Disponibles, PorcentajeUso (calculados)
EsVigenteEn(DateOnly fecha) bool Si sigue vigente en la fecha (en general, HoraUruguay.Hoy())

Tipo Propiedades
AcuseSobre Estado (EstadoSobre: Recibido AS, Rechazado BS, ArchivoRechazado BA), Recibido, Mensaje, Motivos, IdReceptor, Token, ConsultarDesde, Comprobantes (los CfeEmitido del sobre, en orden), XmlRespuesta
EstadoEnvio Comprobantes (EstadoComprobante), Procesado, TodosAceptados, Mensaje, XmlRespuesta
EstadoComprobante NroOrdinal (posición en el sobre), TipoCfe, Serie, Numero (opcionales en el acuse), Estado (EstadoCfe), Aceptado, Motivos
AcuseReporte Estado (EstadoReporte: Recibido AR, Rechazado BR, ArchivoRechazado BA), Recibido, Mensaje, Motivos, IdReceptor, Fecha, CantidadCfe

Ver Códigos DGI.


Interfaz Implementaciones Para qué
IUruFacturaClient UruFacturaClient Inyección y mocks en tu aplicación
IUruFacturaStore InMemoryStore (default en Homologación), EfUruFacturaStore<TContext> CAE, numeración atómica, número de envío y CFE emitidos
IDgiClient SOAP con WS-Security (interno) Transporte hacia la DGI (tests, proxy)
ValueTask GuardarCaeAsync(Cae cae, CancellationToken ct = default); // upsert; UltimoNroUsado nunca retrocede
ValueTask<IReadOnlyList<Cae>> ListarCaesAsync(CancellationToken ct = default);
ValueTask<bool> EliminarCaeAsync(Cae cae, CancellationToken ct = default); // solo si no numeró nada
ValueTask<bool> RegistrarEmisionAsync(CfeEmitido emitido, long ultimoNroUsadoLeido, CancellationToken ct = default);
ValueTask<long> ReservarIdEnvioAsync(CancellationToken ct = default); // creciente y único entre instancias
ValueTask GuardarCfesAsync(IReadOnlyCollection<CfeEmitido> cfes, CancellationToken ct = default); // estado ante la DGI
ValueTask<IReadOnlyList<CfeEmitido>> BuscarCfesAsync(string nroInterno, CancellationToken ct = default);
IAsyncEnumerable<CfeEmitido> ListarCfesAsync(DateOnly fechaEmision, CancellationToken ct = default); // de a uno

RegistrarEmisionAsync es la numeración: avanza el CAE del CFE a su número y guarda el CFE en una sola operación atómica, siempre que el CAE siga con el UltimoNroUsado leído; si otra emisión lo usó antes, devuelve false sin guardar nada y el cliente vuelve a numerar. ListarCfesAsync lanza UruFacturaException si el almacenamiento no tiene completos los CFE de la fecha (para no enviar un reporte diario incompleto).

InMemoryStore(TimeProvider? reloj = null) conserva los CFE de los últimos DiasRetencionCfe días (default 7, contando desde hoy) y no tiene los anteriores a su creación. EfUruFacturaStore hace el UPDATE condicional (token de concurrencia) y el INSERT en la misma transacción; MaxIntentos (default 100) acota la reserva del número de envío. Sus entidades (CaeEntity, CfeEntity con ToCfeEmitido()) son públicas para consultas propias.


Miembro Descripción
CfePdf.GenerarA4(emitido), GenerarTermico(emitido), GenerarQr(emitido) Representación impresa (paquete UruFactura.Pdf)
FirmaDigital.VerificarFirma(string xml) Verifica la firma de un CFE (por ejemplo, de un proveedor) y devuelve el certificado firmante, o null si no es válida. Comprobá que el certificado sea del emisor: la firma sola no lo prueba
moneda.CodigoIso() Código ISO 4217 de la moneda (UYU, USD, EUR)
Rut.EsValido(string? rut) 12 dígitos y dígito verificador (módulo 11) correcto
HoraUruguay.Hoy() / Ahora() Fecha y hora de Uruguay (UTC−03:00): usalas en lugar de DateTime.Today / Now

Enum Valores
TipoCfe 101-103, 111-113, 121-124, 131-133, 141-143, 181, 182 y las contingencias 2xx (ver Tipos de CFE)
TipoIva Exento (1), Minimo (2, 10 %), Basico (3, 22 %), Suspendido (4)
TipoDocumentoReceptor Rut (2), CedulaIdentidad (3), Otro (4), Pasaporte (5), Dni (6), Nife (7)
Moneda PesoUruguayo, DolarAmericano, Euro
EstadoCfe Aceptado (AE), Rechazado (BE), Observado (CE), Reliquidado (FE)
EstadoSobre Recibido (AS), Rechazado (BS), ArchivoRechazado (BA)
EstadoReporte Recibido (AR), Rechazado (BR), ArchivoRechazado (BA)
FormaPago Contado, Credito
IndTraslado TrasladoPropio, TrasladoEnComision, Devolucion, TrasladoPorVenta, TrasladoEnConsignacion, TrasladoPorExposicion
TipoMovimientoGlobal Descuento, Recargo
AlgoritmoFirma Sha256 (default), Sha1
Ambiente Homologacion, Produccion

Excepción Cuándo
UruFacturaException Base; configuración inválida, Producción sin almacenamiento, sobre fuera de 1-250 CFE, reporte de una fecha futura o incompleta
CfeValidationException El CFE no es válido (Errores, de tipo ErrorValidacion); no se consumió ningún número
CfeDuplicadoException Ya se emitió un CFE del mismo tipo con el mismo NroInterno que la DGI no rechazó (Existente)
CaeException CAE inválido o superpuesto al registrarlo, CAE que no se puede eliminar, o no hay CAE vigente con números
FirmaDigitalException Certificado inexistente, contraseña incorrecta, sin clave privada; falla de firma
DgiCommunicationException Red, timeout, SOAP Fault o HTTP sin acuse (CodigoRespuesta, Respuesta)
PdfGenerationException Falla del PDF (UruFactura.Pdf)