Skip to content

Migrar desde otro proveedor

Esta guía te ayuda a migrar a UruFactura desde diferentes puntos de partida, ya sea que estés usando una solución propietaria, integrando SOAP directamente, o usando otra librería.


Sin dependencias de vendors

100% open-source y MIT. No hay licencias, suscripciones ni lock-in con ningún proveedor.

API idiomática en C#

Objetos tipados, métodos async/await, DI-friendly. No más SOAP ni XML manual.

Mantenido con .NET 10

Soporte para las últimas versiones de .NET. Sin librerías legacy ni WCF.

Todo incluido

XML, firma digital, SOAP, numeración con CAE y reporte diario en un paquete; PDF y base de datos como complementos.


Si actualmente construís los XML y llamadas SOAP a mano:

// ❌ Antes: construir XML manualmente y llamar SOAP
var xml = $@"<?xml version=""1.0"" encoding=""UTF-8""?>
<CFE xmlns=""http://www.dgi.gub.uy/wdgi/CFEv1"">
<eTck>
<TmstFirma>{DateTime.UtcNow:o}</TmstFirma>
<Encabezado>
<IdDoc>
<TipoCFE>101</TipoCFE>
<Serie>A</Serie>
<Nro>1</Nro>
...
</IdDoc>
</Encabezado>
...
</eTck>
</CFE>";
// Firmar XML con XmlDsig... (código extenso)
var soapEnvelope = WrapInSoapEnvelope(signedXml);
var response = await httpClient.PostAsync(dgiEndpoint, soapContent);
// Parsear respuesta SOAP manualmente...
// ✅ Después: describís el comprobante; UruFactura lo valida, numera, firma y envía
var eticket = await client.EmitirAsync(new Cfe(TipoCfe.ETicket)
{
Detalle = [new LineaDetalle { NombreItem = "Servicio", Cantidad = 1, PrecioUnitario = 1000m }],
});
var acuse = await client.EnviarAsync(eticket);

Migración desde una solución ERP propietaria

Section titled “Migración desde una solución ERP propietaria”

Si estás migrando desde un módulo de facturación electrónica de un ERP (Biscuit, Pragma, ContaPyme, etc.):

Tu sistema actual UruFactura
Comprobante / Documento Cfe para describirlo (un solo modelo: new Cfe(TipoCfe.ETicket), …) y CfeEmitido una vez emitido
Línea de artículo / ítem LineaDetalle
Proveedor / Receptor Receptor
Rango CAE / Talonario Cae con RangoDesde / RangoHasta
Acuse del envío AcuseSobre.Recibido + AcuseSobre.Estado (AS = sobre recibido)
Estado del comprobante ConsultarEnvioAsync → CfeEmitido.AceptadoPorDgi (AE = aceptado)
Representación impresa CfePdf.GenerarA4(emitido) / GenerarTermico(emitido) (paquete UruFactura.Pdf)
  1. Mantener ambos sistemas en paralelo

    No apagues el sistema anterior hasta validar el nuevo en homologación.

  2. Mapear los tipos de CFE

    // Mapeo desde tu sistema a TipoCfe
    TipoCfe MapearTipo(string tipoSistemaAnterior) => tipoSistemaAnterior switch
    {
    "TICKET" => TipoCfe.ETicket,
    "FACTURA" => TipoCfe.EFactura,
    "NC_TICKET" => TipoCfe.NotaCreditoETicket,
    "NC_FACTURA" => TipoCfe.NotaCreditoEFactura,
    _ => throw new ArgumentException($"Tipo desconocido: {tipoSistemaAnterior}")
    };
  3. Adaptar la estructura de datos

    // Convertir entidad de tu sistema a LineaDetalle
    static LineaDetalle ConvertirLinea(MiLineaFactura linea) =>
    new LineaDetalle
    {
    NombreItem = linea.DescripcionProducto,
    Cantidad = linea.Cantidad,
    PrecioUnitario = linea.PrecioUnitario,
    IndFactIva = linea.EsExento ? TipoIva.Exento
    : linea.IvaMinimo ? TipoIva.Minimo
    : TipoIva.Basico,
    };
  4. Implementar una capa de servicio

    public class FacturacionService(IUruFacturaClient client)
    {
    public async Task<ResultadoEmision> EmitirAsync(ComprobanteDTO dto)
    {
    var cfe = new Cfe(MapearTipo(dto.Tipo))
    {
    NroInterno = dto.Id, // un reintento no lo emite dos veces (CfeDuplicadoException)
    Receptor = dto.Receptor is null ? null : new Receptor
    {
    Documento = dto.Receptor.Rut,
    RazonSocial = dto.Receptor.Nombre,
    Direccion = dto.Receptor.Direccion,
    Ciudad = dto.Receptor.Ciudad,
    },
    Detalle = [.. dto.Lineas.Select(ConvertirLinea)],
    };
    var emitido = await client.EmitirAsync(cfe); // valida, numera, firma y registra
    // 1) Acuse del sobre (AS = recibido); 2) resultado del comprobante (AE = aceptado)
    var acuse = await client.EnviarAsync(emitido);
    if (acuse.Recibido)
    await client.ConsultarEnvioAsync(acuse);
    return new ResultadoEmision
    {
    Numero = $"{emitido.Serie}-{emitido.Numero}",
    Estado = emitido.EstadoDgi?.ToString() ?? acuse.Estado.ToString(),
    Mensaje = emitido.MensajeDgi ?? acuse.Mensaje,
    PdfA4 = CfePdf.GenerarA4(emitido),
    };
    }
    }
  5. Probar en homologación

    Reproducí los escenarios más comunes de tu operación: ventas simples, notas de crédito, exportación.

  6. Migrar los CAEs

    Solicitá nuevos CAEs en DGI o trasladá los existentes. Registralos con:

    await client.RegistrarCaeAsync(new Cae { NumeroAutorizacion = "…", TipoCfe = TipoCfe.ETicket, Serie = "A", RangoDesde = 1, RangoHasta = 1000, FechaVencimiento = … });
  7. Cambiar a producción

    Ambiente = Ambiente.Produccion

Si estás usando otra librería .NET de facturación electrónica uruguaya:

Operación Librería genérica UruFactura
Crear comprobante new Comprobante(tipo) new Cfe(TipoCfe.ETicket) { … }
Agregar ítems comprobante.AddItem(...) Detalle = [new LineaDetalle { … }, …]
Numerar y firmar cae.Next(), firma.Sign(xml, cert) await client.EmitirAsync(cfe)
Enviar a DGI soap.Send(xml) await client.EnviarAsync(emitido)
Generar PDF pdf.Render(comprobante) CfePdf.GenerarA4(emitido)
Estado CFE soap.ConsultarEstado(id) await client.ConsultarEnvioAsync(acuse) / ConsultarCfeAsync(emitido)

Antes de ir a producción, verificá:

  • Certificado digital cargado y funcionando en el nuevo sistema
  • CAEs registrados para todos los tipos de CFE a emitir
  • Ambiente de homologación testeado con los tipos de CFE que usás
  • Reporte Diario enviado exitosamente en las pruebas
  • PDFs generados y verificados visualmente
  • Manejo de errores implementado (rechazos DGI, errores de red)
  • Archivo de XMLs configurado (almacenamiento durable, 5+ años)
  • Monitoreo de CAEs implementado (alertas de vencimiento/agotamiento)
  • Secretos del certificado en gestión segura (variables de entorno / Key Vault)
  • Ambiente cambiado a Producción antes del go-live