jueves, 15 de febrero de 2018

Conectar tu aplicacion con AFIP sin morir en el intento


Muchas veces se me ocurrio intentar conectar mi aplicacion de gestion de empresa con el servicio electronico de AFIP (Ente regulador de la argentino) para por ejemplo realizar la facturacion electronica.
Resumen, es una mala idea. Este es mi segundo intento de realizar la conexion. En el primer intento fracase abruptamente por falta de documentacion, ejemplos que no funcionaban y otras cosas que no ayudaron a completar el proyecto con exito.
Esta vez en cambio, encontre un poco mas de ayuda en linea, estudie un poco mejor la legislacion y la documentacion del sistema, con lo que pude completar con exito.
Entonces ahora te cuento como hacerlo sin morir en el intento

PD: hay un glosario al fondo.

Pre-requisitos
Tener un certificado de seguridad de autorizado por afip


Procedimiento General

1. Nos logeamos con el servidor de AFIP mediante el WSAA
2. Obtenemos el TA para poder realizar transacciones con el resto de los servicios
3. Preguntamos el ultimo Codigo usado
4. Mandamos a crear una nueva factura
5. Pedimos el PDF



Primero veremos como conseguir el certificado digital necesario para comunicarnos con los servidores de AFIP. Esto lo veremos en el siguiente post dedicado a eso:

Obtener Certificado de AFIP para Facturacion Electronica
https://exgetmessageaux.blogspot.com.ar/2018/02/obtener-certificado-de-afip-para.html


Ahora veremos como logearnos con el servidor de AFIP para conseguir un TA que nos permita realizar operaciones en los servidores de AFIP.

Los servidores de AFIP para las fechas utilizan el formato yyyyMMdd, es decir para la fecha 21 de enero del 2017, utilizaremos el string "20170121" (2017-01-21)

Estos, utilizan un web service SOAP, que se maneja a travez de XML para la comunicacion, es decir, armamos un XML que explique que queremos hacer, y todos los datos necesarios, y el WS hace lo que puede y nos devuelve otro XML con un resumen de lo que pudo hacer y lo que no.

Con esto veremos ahora en contexto como seria el XML que le vamos a enviar al WSAA para logearnos:


<loginTicketRequest>
 <header>
  <uniqueId>1</uniqueId>      
  <generationTime>2017-09-08T08:25:56</generationTime>  
  <expirationTime>2017-09-08T08:45:56</expirationTime>  
 </header>
 <service>wsfe</service> 
</loginTicketRequest>
Donde generationTime es la fecha de creacion del TA o solicitud, y el expirationTime hasta cuando va a estar activo. Y muy importante, service es el servicio para el cual nos estamos logeando, este TA solo va a servir para ese servicio, si lo intentamos usar para otro servicio, nos va a dar error.

Ahora, el WSAA es el unico que te pide que le envies el XML, el WSFE lo hace a travez de su WSDL, lo que es parecido pero en codigo nativo, sin tocar XML.
Para esto puedo recomendar hacer un template del XML basico y despues modificarlo con cada llamada.
Con motivo de simplificar el tutorial, vamos a hacerlo directamente en un string, que pueda convertir en un objeto XMLDocument y manipularlo con eso.

Si todo sale bien, deberíamos recibir algo de este estilo

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<loginTicketResponse version="1">
    <header>
        <source>CN=wsaahomo, O=AFIP, C=AR, SERIALNUMBER=CUIT 33693450239</source>
        <destination>SERIALNUMBER=CUIT 20375124685, CN=gestionpersonal</destination>
        <uniqueId>3594694368</uniqueId>
        <generationTime>2017-09-08T08:35:53.544-03:00</generationTime>
        <expirationTime>2017-09-08T20:35:53.544-03:00</expirationTime>
    </header>
    <credentials>
        <token>  A very long string </token>
        <sign>  A no so long string </sign>
    </credentials>
</loginTicketResponse>

Donde lo que nos importa es lo que viene en token y en sign, en estos deberían venir dos cadenas de caracteres que nos van a dar el permiso para la sesion.

Bueno, veamos algo de codigo.
Primero creamos un proyecto .NET (en mi caso VB .NET y WinForm) y le agregamos algo para agregar el certificado que vamos a usar, la clave del certificado, el servicio que vamos a usar (todo esto, en nuestro producto final, junto con el CUIT del responsable, deberían estar en una ventana de configuraciones), Un boton de login, y otro de WSFE (para hacer la factura). Podemos agregarle otras cosas como ver lo que estamos enviando y reciviendo (crudo) y un selector de testing y homologacion. La idea de esto es hacer un prototipo de conexion, y que ustedes rescaten lo que les sirve y lo apliquen a su programa.

El proyecto del prototipo esta aca:

GitHub AFIP WSFE WSAA Prototype


La primera ventana nos quedara algo así:

y el código que importa es el que esta atrás del Login:

Private l As LoginClass
Private url As String = "https://wsaahomo.afip.gov.ar/ws/services/LoginCms"
Private Sub LoginBtn_Click(sender As Object, e As EventArgs) Handles LoginBtn.Click
    l = New LoginClass(ServicioTX.Text, url, CertificadoTX.Text, ClaveTX.Text)
    l.hacerLogin()
End Sub


Donde LoginClass es la que hace la magia de la conexion. Esta la podemos encontrar aca:
GitHub LoginClass


Pero antes de meternos de lleno en esto, vamos a agregar los WS a travez de sus WSDL (El WSDL describe lo mejor que puede el WS para que directamente Visual Studio u otro, sepa como comunicarse con este)

Para esto seguiremos este post separado

Agregar referencia a WS SOAP AFIP a Visual Studio

https://exgetmessageaux.blogspot.com.ar/2018/02/agregar-referencia-ws-soap-afip-visual.html

Ahora si podemos ver/agregar la clase LoginClass
En el contructor no hay nada en particular, solo toma los datos de Servicio, la URL del servidor que vamos a usar (Homo o Produccion), la ubicacion del Certificado y la clave.
Lo importante esta en metodo hacerLogin()

En la primera parte:

'Preparo el XML Request
XmlLoginTicketRequest = New XmlDocument
XMLLoader.loadTemplate(XmlLoginTicketRequest, "LoginTemplate")

uniqueIdNode = XmlLoginTicketRequest.SelectSingleNode("//uniqueId")
generationTimeNode = XmlLoginTicketRequest.SelectSingleNode("//generationTime")
ExpirationTimeNode = XmlLoginTicketRequest.SelectSingleNode("//expirationTime")
ServiceNode = XmlLoginTicketRequest.SelectSingleNode("//service")
generationTimeNode.InnerText = DateTime.Now.AddMinutes(-10).ToString("s")
ExpirationTimeNode.InnerText = DateTime.Now.AddMinutes(+10).ToString("s")
uniqueIdNode.InnerText = CStr(_globalId)
ServiceNode.InnerText = serv

Simplemente traemos el template del XML (el que hablamos antes) que vamos a enviar y le seteamos todos los campos (UniqueId, GenerationTime, ExpirationTime y Service)
PD: Para traer el Template use una pequeña clase que use en otros proyectos, que esta en el fondo de la misma clase, la misma se llama XMLLoader.

Acá se vuelve un poco mas complicado:

'Obtenemos el Cert
certificado = New X509Certificate2
If clave.IsReadOnly Then
    certificado.Import(File.ReadAllBytes(cert_path), clave, X509KeyStorageFlags.PersistKeySet)
Else
    certificado.Import(File.ReadAllBytes(cert_path))
End If

Dim msgBytes As Byte() = Encoding.UTF8.GetBytes(XmlLoginTicketRequest.OuterXml)
En esta parte creamos el certificado X509Certificate2 y le importamos el certificado que creamos en los pasos anteriores, acordarse que el certificado de homologacion solo funciona con el servidor de homologación e igual con produccion. Y convertimos el XML que creamos a Bytes para poder cifrar.

Ahora puede ocurrir un problema, si el sistema no nos deja crear el Certificado, osea que no nos reconoce la clase X509Certificate2 vamos a tener que agregar la referencia a Seguridad de .NET de la siguiente forma:
Vamos a My Project en el Explorador de Soluciones

 Ahora, en las pestañas izquierdas vamos a donde dice Referencias

De ahí vamos a abajo donde dice Agregar

Ahora buscamos en el panel lateral, en la sección donde dice Ensablados, y dentro de este buscamos el que dice Framework, cuando lo seleccionemos nos actualizara la lista de librerías, ahora buscaremos en el panel central la librería de System.Security , chequeamos el checkbox que nos aparece en el nombre y le damos a Aceptar.

Y luego nos aseguramos de tener el siguiente import en nuestra clase:

Imports System.Security.Cryptography.X509Certificates


Ahora si, volviendo al LoginClass, la siguiente parte es firmar el mensaje:

'Firmamos
Dim infoContenido As New ContentInfo(msgBytes)
Dim cmsFirmado As New SignedCms(infoContenido)

Dim cmsFirmante As New CmsSigner(certificado)
cmsFirmante.IncludeOption = X509IncludeOption.EndCertOnly

cmsFirmado.ComputeSignature(cmsFirmante)

cmsFirmadoBase64 = Convert.ToBase64String(cmsFirmado.Encode())

Ahora tenemos el XML Firmado y codificado en la variable cmsFirmadoBase64 que es la que vamos a enviar a la AFIP para que nos autorice.
Ya ahora enviamos la info:

'Hago el login
Dim servicio As New WSAA.LoginCMSService
servicio.Url = url

loginTicketResponse = servicio.loginCms(cmsFirmadoBase64)

Si todo salio bien en la variable loginTicketResponse vamos a tener el XML con la respuesta en forma de String, El siguiente paso, es convertirlo a XML Document (Object) y analizarlo, a ver que nos responde el servidor:

Analizamos la respuesta
XmlLoginTicketResponse = New XmlDocument
XmlLoginTicketResponse.LoadXml(loginTicketResponse)

_Token = XmlLoginTicketResponse.SelectSingleNode("//token").InnerText
_Sign = XmlLoginTicketResponse.SelectSingleNode("//sign").InnerText

Dim exStr = XmlLoginTicketResponse.SelectSingleNode("//expirationTime").InnerText
Dim genStr = XmlLoginTicketResponse.SelectSingleNode("//generationTime").InnerText
ExpirationTime = DateTime.Parse(exStr)
GenerationTime = DateTime.Parse(genStr)

MsgBox("Exito")

Ok veamos, como después quiero recuperar las cosas después, la mayoría de las cosas las guarde en variables globales de la clase. El documento entero en XmlLoginTicketResponse, El Token en _Token, el Sign o firma en _Sign, y las fechas de generacion y expiracion en las variables ExpirationTime y GenerationTime. Con esto ya tenemos el TA

Entonces cuando hagamos debug, si salio todo bien, deveriamos ver algo como esto:

Si tenemos eso ya estamos logeados contra del servidor de AFIP.

Ahora lo que nos queda es pasarle el informe al servidor de facturación electrónica.
Importante! Este servidor, no necesita que le pasemos los detalles de cada item de la factura, sino un resumen por cada alícuota, es decir, cuanto es el total con 21% de IVA, cuanto con el 10.5%, etc.

Entonces, igual que antes le tenemos que enviar una especie de XML con los datos, a continuación te muestro el XML de muestra, digo especie, porque con el WSDL te lo traduce para que lo manejes como una especie de objetos.

Este seria el XML que tenemos que enviar, ejecutando el método FECAESolicitar (Solicitar CAE de Fact Electronica):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ar="http://ar.gov.afip.dif.fev1/">
 <soapenv:Header/> 
 <soapenv:Body> 
  <FECAESolicitar> 
   <Auth>        <!-- Autenticacion -->
    <Token>PD94.....</Token>  <!-- TA TOKEN -->
    <Sign>tYft0........</Sign>  <!-- TA Sign -->
    <Cuit>33693450239</Cuit>  <!-- Mi CUIT -->
   </Auth> 
   <FeCAEReq> 
    <FeCabReq> 
     <CantReg>1</CantReg>  <!-- Cant de Facturas -->
     <PtoVta>12</PtoVta>  <!-- Punto de Venta -->
     <CbteTipo>1</CbteTipo>  <!-- Factura A --> 
    </FeCabReq> 
    <FeDetReq> 
     <FEDetRequest> 
      <Concepto>1</Concepto>    <!-- 1: Productos, 2: Servicios -->
      <DocTipo>80</DocTipo>    <!-- Tipo Doc 80: CUIT -->
      <DocNro>20111111112</DocNro>  <!-- Doc del facturado -->
      <CbteDesde>1</CbteDesde>   <!-- Nro Factura -->
      <CbteHasta>1</CbteHasta>   <!-- Tambien Nro Factura -->
      <CbteFch>20100903</CbteFch>  <!-- Fecha del Comprobante, yyyymmdd-->
      <ImpTotal>184.05</ImpTotal>  <!-- Total con impuestos -->
      <ImpTotConc>0</ImpTotConc>   <!-- Importe neto no gravado -->
      <ImpNeto>200</ImpNeto>    <!-- Importe neto gravado Total sin impuestos -->
      <ImpOpEx>0</ImpOpEx>    <!-- Importe Exento --> 
      <ImpTrib>0</ImpTrib>    <!-- Suma de Tributos -->
      <ImpIVA>26.25</ImpIVA>    <!-- Suma de IVAs -->
      <FchServDesde></FchServDesde>  <!-- Solo Servicios -->
      <FchServHasta></FchServHasta>  <!-- Solo Servicios -->
      <FchVtoPago></FchVtoPago> 
      <MonId>PES</MonId>     <!-- Codigo de Moenda -->
      <MonCotiz>1</MonCotiz>    <!-- Cotizacion, para pesos: 1-->
      <!-- <Tributos>  Por si retiene tributos
       <Tributo> 
        <Id>99</Id> 
        <Desc>Impuesto Municipal Matanza</Desc> 
        <BaseImp>150</BaseImp> 
        <Alic>5.2</Alic> 
        <Importe>7.8</Importe> 
       </Tributo> 
      </Tributos>  -->
      <Iva> 
       <AlicIva> 
        <Id>5</Id>     <!-- Id segun tabla de IVAs (5: 21%)-->
        <BaseImp>100</BaseImp>  <!-- Importe total para esa alicuota -->
        <Importe>21</Importe>  <!-- Monto total del IVA (100 * 0.21) -->
       </AlicIva>
       <AlicIva>
        <Id>4</Id>     <!-- 4: 10.5 -->
        <BaseImp>50</BaseImp>
        <Importe>5.25</Importe>
       </AlicIva>
      </Iva>
     </FEDetRequest>
    </FeDetReq>
   </FeCAEReq>
  </FECAESolicitar>
 </soapenv:Body>
</soapenv:Envelope>

Como ven en ningún lado nos piden que items tiene la factura. Entonces eso es responsabilidad de ustedes, del lado de su base de datos de guardar la factura. Incluso, se pueden hacer varias facturas, por ejemplo diciendo que desde la factura 5 a la 8 hay un total de $1210 con $210 de IVA 21%, sin especificarle la cantidad de intems ni cuanto es el total por cada comprobante, eso si, después lo tenemos que separar bien en cada factura que vayamos a entregar.

Como tambien se ve, hay muchos campos que requieren un Id de un elemento. Por ejemplo, En Tipo de Documento, necesita el Id del CUIT (80), o la moneda, que esta el Id de la moneda Peso (PES), u otros. Estos son medianamente fijos, el tema es que pueden cambiarlo sin avisar, o pueden agregar nuevos, Para obtener estos campos se puede hacer a travez del mismo WS.

Primero hacemos una ventana como la del programa, recuerdo que esta es de prueba. para que vean que info se necesita para enviar a la AFIP, ustedes tendrán que incorporarlo a su sistema.

Entonces aca tengo un par de CombosBox, estos los rellenaremos con lo que nos da AFIP.

Rellenamos los Cmbs través del botón de Cargar, así que veamos bien que hace:

 Private Sub CargaBtn_Click(sender As Object, e As EventArgs) Handles CargaBtn.Click
        Try
            authRequest = New FEAuthRequest()
            authRequest.Cuit = MyCuitTX.Text
            authRequest.Sign = Login.Sign
            authRequest.Token = Login.Token

            Dim service As WSFEHOMO.Service
            service.Url = url

            service.ClientCertificates.Add(Login.certificado)

            puntosventa = service.FEParamGetPtosVenta(authRequest)
            ptos_venta_cm.DataSource = puntosventa.ResultGet

            TiposComprobantes = service.FEParamGetTiposCbte(authRequest)
            TiposComprobantesCMB.DataSource = TiposComprobantes.ResultGet

            TipoConceptos = service.FEParamGetTiposConcepto(authRequest)
            TipoConcepto.DataSource = TipoConceptos.ResultGet

            TipoDoc = service.FEParamGetTiposDoc(authRequest)
            TipoDocCMB.DataSource = TipoDoc.ResultGet

            Monedas = service.FEParamGetTiposMonedas(authRequest)
            MonedaCMB.DataSource = Monedas.ResultGet

            TiposIVA = service.FEParamGetTiposIva(authRequest)
            TipoIVACmb.DataSource = TiposIVA.ResultGet

            Dim lastCbteObj = service.FECompUltimoAutorizado(authRequest, 4, TiposComprobantes.ResultGet(0).Id)
            NroCbteTX.Text = lastCbteObj.CbteNro + 1

            opcionales = service.FEParamGetTiposOpcional(authRequest)
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub
Si a ustedes les falla es porque yo tengo importado es espacio de nombres del WS (WSFEHOMO)
 Entonces vemos que en las primeras lineas completamos el campo Auth del XML, donde le ingresamos el Cuit propio y el Login y Sign del TA que conseguimos en la Autenticación.

En la linea service.ClientCertificates.Add(Login.certificado) Nos encargamos de agregar el certificado que nos fue asignado para trabajar con esto, el mismo que usamos antes. Si se dan cuenta van a ver que me traje el objeto Login del formulario anterior con todo lo necesario.

Luego por cada item que llenemos vamos a tener un par de lineas

TiposComprobantes = service.FEParamGetTiposCbte(authRequest)
TiposComprobantesCMB.DataSource = TiposComprobantes.ResultGet
Donde la primera es la que llama al servicio y obtiene un objeto con el resultado de la consulta.
Y la segunda es la que asigna el set de resultado al ComboBox. Asegúrense de tener seteado el atributo DisplayMember de cada Combobox, sino mostrara cualquier cosa.
Esto es para las opciones parametrizadas, como el tipo de comprobante, tipo de moneda, tipo de iva, etc

Y con eso se nos debería llenar todos los CMB, quedándonos algo así.

Para este ejemplo usaremos un solo IVA (suponiendo que todos los items dentro de la factura tienen la misma alícuota. Entonces para este caso, y como la mayoría los precios ya incluyen el IVA, les puse las dos posibilidades, la de ingresar el Total Neto (Sin Iva) y la Alícuota y que te calcule el total, o al revés, Ingresando el Total y la alícuota, te calcule el Total Neto.

No voy a ponerme a explicar todo el código, es bastante simple, y si tienen alguna duda pueden consultarme por mail o en la caja de comentarios.

Ahora paso a explicar como hacer la conexión a la AFIP para pesarla el resumen de la compra, haciendo esto, ellos nos devuelven el Código de Autorización Electrónico (CAE) y su Vencimiento, elementos necesario para que la factura que imprimamos sea valida.

Para eso veremos el código llamado al hacer clic en el botón "Registrar"

Primero en esta parte

Dim service As WSFEHOMO.Service = getServicio()
service.ClientCertificates.Add(Login.certificado)
Dim punto_seleccionado As PtoVenta = ptos_venta_cm.SelectedItem

Dim cm As CbteTipo = TiposComprobantesCMB.SelectedItem

Dim req As New FECAERequest
Dim cab As New FECAECabRequest
Dim det As New FECAEDetRequest

cab.CantReg = 1
cab.PtoVta = punto_seleccionado.Nro
cab.CbteTipo = cm.Id
req.FeCabReq = cab
Inicializamos el servicio y le seteamos el certificado que trajimos del login.
Luego obtenemos el punto de venta seleccionado al igual que el tipo de comprobante.
Luego empezamos a armar el Request. Primero inicializamos El Request (FECAERequest), la cabecera (FECAECabRequest) y el cuerpo (FECAEDetRequest) vacíos.

Como ya dije, en este procedimiento podemos enviar mas de una factura a la vez, por ahora enviaremos de a una a la vez. Entonces seteamos el campo Cantidad de Registros (CantReg) de la cabecera en 1. Seteamos el campo Pto de Venta y Tipo de Comprobante (PtoVta, CbteTipo) con los parámetros seleccionados. Y no nos olvidemos de setear la cabecera del Request (FeCabReq).

Dim concepto As ConceptoTipo = TipoConcepto.SelectedItem
det.Concepto = concepto.Id
Dim doctipo As DocTipo = TipoDocCMB.SelectedItem
det.DocTipo = doctipo.Id
det.DocNro = Long.Parse(DocTX.Text)
Acá empezamos a armar el Detalle del Request. Le seteamos el Concepto, Tipo de Comprobante y el Nro de CUIT.

Dim este_cbte = ultimo_nro + 1
det.CbteDesde = este_cbte
det.CbteHasta = este_cbte

det.CbteFch = FechaDTP.Value.ToString("yyyyMMdd")

det.ImpNeto = NetoTX.Text
det.ImpIVA = ImpIvaTx.Text
det.ImpTotal = TotalTx.Text

det.ImpTotConc = 0
det.ImpOpEx = 0
det.ImpTrib = 0
Acá establecemos el numero de comprobante, como es un solo comprobante, el CbteDesde y Hasta serán iguales. Seteamos la fecha con el formato requerido. Seteamos los Totales (Neto, Iva y Total) según lo que calculamos anteriormente.
Ponemos en cero los totales no gravados (ImpTotConc), Excentos (ImpOpEx) y Tributos (ImpTrib) ya que solo incluiremos Iva 21%  sin recaudar tributos.


Dim mon As Moneda = MonedaCMB.SelectedItem
det.MonId = mon.Id
det.MonCotiz = 1

Dim alicuota As New AlicIva
Dim ivat As IvaTipo = TipoIVACmb.SelectedItem
alicuota.Id = ivat.Id
alicuota.BaseImp = NetoTX.Text
alicuota.Importe = ImpIvaTx.Text

det.Iva = {alicuota}


req.FeDetReq = {det}
Igual que antes seteamos el parámetro de moneda. La cotización de la moneda tiene que ser 1 (uno) si la moneda es Peso Argentino. Como en este ejemplo no trabajo con otra moneda, quedo forzado en el código.
Ahora creamos un objeto AlicoutaIva y le setamos los parámetros de IVA según lo seleccionado, la base imponible (a cuanto se le aplica el IVA) y el Importe de IVA.
Seteamos el parámetro IVA del detalle (el cual es un array), si tuviéramos mas de una alícuota de IVA el array IVA tendría que tener mas de un elemento como en el ejemplo.
Por ultimo seteamos el Detalle del Request (El cual tambien es un array) con el objeto que estuvimos configurando. Si tuviéramos mas de un detalle, el array de detalle tendría mas de un objeto.
Con esto ya queda configurado el Request, listo para enviar a la AFIP.

Dim respuesta As FECAEResponse = service.FECAESolicitar(authRequest, req)

Dim m As String = "Estado: " & respuesta.FeCabResp.Resultado & vbCrLf
m &= "Estado Esp: " & respuesta.FeDetResp(0).Resultado
m &= vbCrLf
m &= "CAE: " & respuesta.FeDetResp(0).CAE
m &= vbCrLf
m &= "Vto: " & respuesta.FeDetResp(0).CAEFchVto
m &= vbCrLf
m &= "Desde-Hasta: " & respuesta.FeDetResp(0).CbteDesde & "-" & respuesta.FeDetResp(0).CbteHasta
m &= vbCrLf

If respuesta.FeDetResp(0).Observaciones IsNot Nothing Then
    For Each o In respuesta.FeDetResp(0).Observaciones
        m &= String.Format("Obs: {0} - {1}", o.Code, o.Msg) & vbCrLf
    Next
End If
m &= vbCrLf

If respuesta.Errors IsNot Nothing Then
    For Each er As Err In respuesta.Errors
        m &= String.Format("Err: {0} - {1}", er.Code, er.Msg)
    Next
End If
m &= vbCrLf

If respuesta.Events IsNot Nothing Then
    For Each ev As Evt In respuesta.Events
        m &= String.Format("Evt: {0} - {1}", ev.Code, ev.Msg)
    Next
End If

Resultado.Text = m
En la primera linea enviamos lo solicitud a la AFIP a través del servicio Web.
Seguido, armamos un resumen re la respuesta para mostrar.
Igual que antes, la respuesta va a tener tantos detalles como detalles tenga la solicitud.
Como solo tenemos un detalle en la solicitud, esperamos un solo detalle en la respuesta.
Cualquier observación particular se va a encontrar en el array Observaciones de cada  detalle.
Las observaciones generales sobre la solicitud se encontraran en el Array Events de la respuesta (respuesta.Events) y los Errores del proceso en la variable Errors de la respuesta (respuesta.Errors).

Si todo salio bien. El CAE se va a encontrar en el detalle correspondiente, junto con su vencimiento.
Podemos verificar rápidamente el resultado del proceso a través del atributo Resultado de la cabecera (resultado general del proceso) o de cada detalle (Particular de ese detalle), en donde R significa Rechazado y A Aprobado.

Ya con eso queda registrada la factura con AFIP. Queda como responsabilidad nuestra entonces guardar en nuestra base de datos el detalle de cada factura emitida.
Cada factura que enviemos a la AFIP y este aprueba no solo queda guardado en sus servidores, sino que queda a nuestra entera disposición para consultar si así lo necesitáramos.
Así podemos hacer un sistema de comprobación de integridad, comparando lo que tenemos en nuestra base de datos contra lo que tienen ellos, según lo que nosotros enviamos.

Una vez que completan la parte de homologacion simplemente tienen que cambiar el link al Service de Produccion o tenerlos los dos y usarlo según se quiera.

Hasta acá voy a explicar. Son libres de hurgar por el código del prototipo.


Espero les sirva. Y cualquier duda esta la caja de comentarios.

Bibliografia
https://drive.google.com/drive/folders/0B5Xpb7ydUT4XLTU0VjBFQ3FVTEE?resourcekey=0-NvZkDioiFyg_B_HN0mZ4bQ&usp=sharing


Glosario
AFIP - Administración Federal de Ingresos Públicos
CAE - Codigo de Autorizacion Electronico
CAEA - CAE Anticipado
CSR - Certificate Singin Request, Requisito de Certificado de Firma
FE - Facturacion Electronica
Sign - Firma que autoriza el Token
TA - Ticket de Autenticación
WS - Web Service
WSAA - Web Service de Autenticación y Autorización
WSDL - Web Service Description Lenguage (Lenguaje de descripción de WS)
WSFE - Web Service de Facturacion Electronica
WSN - Web Services de Negocios





219 comentarios :

  1. Excelente tutorial, se agradece el aporte!

    ResponderEliminar
  2. Buenas! Che ahi en el codigo que indicas, que solo es necesario madar solo un item, y no discriminado.
    Consulta, por casualidad, tenes el codigo fuente en el caso que se requiera mandar todos los items?
    Desde ya gracias.
    Slds.

    ResponderEliminar
    Respuestas
    1. A la afip no le interesa el detalle, tenes que mandar el total neto y las deferentes totales de cada alicuota. Vos tenes que almacenar los detalles de la factura

      Eliminar
  3. Hola Buenas Noches , podrías explicar como reutilizar el TA
    desde ya muchas gracias
    Excelente Tutorial

    ResponderEliminar
    Respuestas
    1. El TA no es necesario reutilizarlo en Homologacion. En produccion si. Para eso vas a necesitar armar una tabla en tu base con los TA que obtengas y su fecha de expiracion. Recuperarlo es facil, con SQL como el siguiente: << Select * form TA where expiration_date > now() >>.
      Si eso devuelve algo significa que hay un TA sin vencer. De lo contrario, si no devuelve ningun registro, no hay ningun TA sin vencer. Vas a tener que generar uno neuvo y registrarlo en la base.

      Eliminar
    2. Guillermo Marcel, consulta, tendrias el codigo fuente para mandar todos los items de una factura en caso que la misma tenga mas de un item.

      Eliminar
    3. Sebastian. AFIP no requiere que informemos los detalles de la factura. Lo unico que tenemos que informar es el resumen de la misma, es decir, de cada tipo de iva, cuanto es el subtotal, el monto de IVA, y el monto total de la factura. Y de ser necesario, retenciones y tributos que se pagen.

      Eliminar
    4. Buenas, una consulta... estoy haciendo pruebas, anexando este codigo a mi sistema... ya hice el tema de la reutilizacion del TA, igual ahora estoy con un certificado de prueba, queria saber, en caso de que me salga la respuesta "El CEE ya posee un TA valido para el acceso al WSN solicitado", luego en la facturacion, como cargaria los combos? Porque en el boton "CARGAR"de este ejemplo, los resultGet no me completan nada, es decir, si no hago el login (Si no aparece exito en la ventana principal) luego no funciona la carga de los datos en el formulario de facturacion...en resumen... si ya tengo un TA valido que tengo que hacer para que luego pueda obtener los resultados del WS?

      Eliminar
    5. Perdon ahi vi... creo que ademas del generationTime y ExpirationTime tengo que guardar el WSAA.Token y el WSAA.Sign... que eso es lo que usa el formulario de facturacion...ahora lo veo pero creo que ahi esta el tema.
      Saludos.

      Eliminar
    6. Este comentario ha sido eliminado por el autor.

      Eliminar
  4. Muchas gracias por la explicacion, muy completa y didáctica.

    ResponderEliminar
  5. Estimado. Gracias por el aporte! muy bueno ya que la documentacion es muy escasa. Estoy intentando consumir el WS de Ventanilla Electronica y me estoy volviendo loco. Algun link o aporte sobre ese WS?!
    Saludos

    ResponderEliminar
    Respuestas
    1. No he tenido el agrado de conectarme con otro WS de ellos por suerte.
      Pero para esto tendrías que leer a full toda la documentación disponible y hacer mucha investigación propia.
      Con algo de perseverancia se puede.
      Saludos

      Eliminar
  6. Hola Guillermo,
    Te comento que he seguido el tutorial al pie de la letra y he podido conectarme al WSAA. Pero me he trabado en un problema es que en la carga del punto de venta en el ComboBox (simplemente se mantiene vacío, mientras que los demás campos sí se llenan de datos).
    ¿Podrías darme un empujoncito para seguir avanzando con esto?

    Muchas gracias por el tutorial que hiciste, y también por el tiempo que te tomaste en leerme.

    Un abrazo!

    ResponderEliminar
    Respuestas
    1. Necesito que hagas un debug, te fijes si lo que esta vacio es el campo que se muestra o el contenido de la lista. Puede que tengas mal puesto el DisplayMember, entonces no sabria que propiedad del objeto mostrar, entonces no mostraria nada. Es muy distinto a que los objetos no esten.

      Eliminar
    2. Buenas, hoy empece con las pruebas y ver como funciona todo... hice un certificado con mi cuit (me di de baja al monotributo hace rato), calculo que es por eso que cuando di de alta el pto de venta en AFIP hoy, solo me dio la opcion de REMITO... no aparece otra opcion... y calculo que es por eso en ese combobox no me aparece nada... voy a generar otro certificado de testing con otro cuit para ver si es eso... pero me paso lo mismo, despues aviso.

      Eliminar
    3. IMPORTANTE: para los que no se les llena el combobox ptodeventa... verificar que tengan un punto de vta tipo RECE habilitado aca les desjo el link
      https://www.sos-contador.com/2017/08/02/en-51-pasos-como-obtener-certificado-digital-e-incorporarse-a-factura-electronica-version-ago2017/
      saludos brian de dmz-store.net

      Eliminar
    4. Esto se soluciono? xq tengo el mismo problema, los puntos de venta estan generados con RECE y no puedo cargar la lista...

      Eliminar
    5. Podes usa esta sentencia que va bien, "Dim cm As CbteTipo = TiposComprobantesCMB.SelectedItem".
      suerte

      Eliminar
    6. En homologación(entorno de desarrollo no carga los puntos de venta.. Tenes que generar 1 punto ficticio)

      Tenes agregar este fragmento de código en la clase FacturaForm en el metodo CargaBtn_clik()
      con esto se crea un punto de venta que es ficticio pero sirve igualmente para recuperar comprobantes, asi que si
      usas el pto de venta 5, después tenes que usar el mismo para recuperar lo comprobantes que generaste con ese punto de venta.(ACLARO QUE ESTO ES SOLO EN HOMOLOGACION -----NO------ EN PRODUCCION)

      If produccion_rb.Checked = CheckState.Checked Then
      puntosventa = service.FEParamGetPtosVenta(authRequest)
      ptos_venta_cm.DataSource = puntosventa.ResultGet
      Else
      Dim ptventa As New PtoVenta With {.Bloqueado = "N", .EmisionTipo = "CAE - Monotributo", .FchBaja = "", .Nro = 5}
      Dim lista = New List(Of PtoVenta)
      lista.Add(ptventa)
      ptos_venta_cm.DataSource = lista
      End If

      Eliminar
  7. Excelente Tutorial. Pero me quede igual que la persona que posteo ultimo. punto de venta en el ComboBox (simplemente se mantiene vacío, mientras que los demás campos sí se llenan de datos). Como se puede solucionar? Saludos

    ResponderEliminar
    Respuestas
    1. En Homologacion no te recupera nada del punto de Venta. Tendras que setearle un valor ficticio, ej: ptoventaCMB.text = "0233", yo tuve el mismo problema y me mate leyendo foros.

      Eliminar
    2. Realizando el seteo de la manera que especificas, a mi no me funciono!! algunos dicen que se hace por base de datos!! podrías darme un poco mas de info por favor?

      Eliminar
  8. Hola Guillermo, muy buen tutorial. Quería consultarte sobre la impresión de la factura. Tengo entendido que cuando se registra la venta desde la página web de la AFIP, los comprobantes se generan solos, es decir: se crea una factura con su encabezado correspondiente, tipo de factura, etc. y ya se puede imprimir. Es posible que eso ocurra acá? O esto que vos planteas solo sirve para REGISTRAR la venta en la AFIP? Debería diseñar mi propia factura en, por ejemplo, un reporte e imprimir desde ahí? Gracias!

    ResponderEliminar
    Respuestas
    1. La factura como documento no es mas que una representacion visual de la informacion de la transaccion, es decir, esto es para registrar la facturacion de una transaccion, para obtener un documento imprimible lo tenes que diseñar y armar vos. La aplicacion de AFIP para facturacion electronica si te genera un documento imprimible. Tu aplicacion deberia tener alguna forma de generar los comprobantes imprimibles, por lo menos en formato PDF, siguiendo la reglamentacion corrrespondiente. (Debe incorporar el CAE, Vencimiento de CAE, Codigo de Barra de CAE, Encabezado, etc...)

      Eliminar
    2. Perfecto! Mi única duda es cómo generar el código de barras, me podrás ayudar?

      Eliminar
    3. El codigo de barra esta codificado con INTERLEAVED25 (un tipo de codigo de barra como EAN-13 u otros).
      La mejor solucion es usar un componente externo que genere codigos de barra. Algunos son pagos, otros son gratuitos. Yo uso BarCodeLib http://www.barcodelib.com/
      La informacion a codificar es la siguiente:

      *Clave Única de Identificación Tributaria (C.U.I.T.) del emisor de la factura (11 caracteres)
      *Código de tipo de comprobante (3 caracteres)
      *Punto de venta (5 caracteres)
      *Código de Autorización de Impresión (C.A.I.) (14 caracteres)
      *Fecha de vencimiento (8 caracteres)
      *Dígito verificador (1 carácter)

      Esa informacion la incertas en el generador de codigo de barra para que te devuelva el codigo de barra que imprimiras en la factura.

      Recomiendo que leas la siguiente fuente:
      https://www.afip.gob.ar/genericos/guiavirtual/directorio_subcategoria.aspx?id_nivel1=562&id_nivel2=599

      Eliminar
  9. Muy Bueno.
    Lo Probe y funciona ok.
    Mucha gracia

    ResponderEliminar
  10. Me alegra mucho que les haya sido de ayuda!

    ResponderEliminar
  11. Hola Guillermo, excelente me ayudo de mucho!
    Consulta hay una forma que debemos seguir para guardar los duplicados de lo que vamos generando?
    Gracias!

    ResponderEliminar
    Respuestas
    1. Hola.
      Tienes la obligación de almacenar en detalle cada comprobante, ya que lo que se envia, es simplemente un resumen de los impuestos.
      Aparte puedes usarlo como un doble control de los datos para mantener la coherencia e integridad de los datos.
      Recomiendo que cuando tengas los datos listos para enviar la request al WS, primero almacenes los datos en la DB (Data Base), seguido envíes la request al WS, en caso que la transacción sea exitosa, actualizas los datos en la DB con la respuesta del WS. Caso contrario, remueves el registro de la DB o reintentas la misma transacción.
      Esto te servirá para mantener tu propio registro aparte de la AFIP. También para protegerte ante fallas de comunicación con los servidores.
      Con esto, puedes crear un script que revise la integridad de ambas bases de datos y encuentre incoherencias entre los registros.

      Espero que te sirva.
      Atte. Guillermo Marcel

      Eliminar
  12. En que casos debe usarse el web service wsmtxca en el que se envía el detalle de los ítems?
    Muchas Gracias.

    ResponderEliminar
    Respuestas
    1. Solamente cunado la AFIP te obligue a informar los detalles. Son pocos casos, como inmoliliarias, exportaciones u otras cosas. Te lo va a decir tu contador. Pero la gran mayoria no informa los detalles, lo que se manejas por el regimen general.

      Eliminar
  13. Hola! una pregunta, en la primer pantalla donde pones la ruta del certificado, que pones en Clave? me sale "Contraseña de red especificada no es válida" , lo estoy probando desde el mismo código que compartiste en este post. Muchas gracias!

    ResponderEliminar
    Respuestas
    1. Cuando haces el certificado, exactamente cunado haces la ultima conversion de formato, te pregunta si le queres poner clave o no. Esa clave es la que te pide ahi

      Eliminar
    2. Lo cree de nuevo al certificado usando tu otro tutorial, ahora sí! funciona! muchas gracias Guillermo, hace mese que estoy luchando con esto.

      Eliminar
    3. Buenas! Necesito revivir esto, me dice contraseña incorrecta, el tema es que no le puse contraseña.. alguna sugerencia?

      Eliminar
  14. Hola Guillermo, te hago una consulta, no puedo mandar mas de una alicuota por factura, en el ejemplo dice que es un array pero no entiendo como hacerlo. Desde ya muchas gracias!!!

    ResponderEliminar
    Respuestas
    1. Deberías insertar multiples items AlicIva dentro del bloque IVA, y sumarizarlos dentro del total IMPIVA.
      De esta forma

      Dim alicuota1 As New AlicIva
      Dim ivat As IvaTipo = TipoIVACmb.SelectedItem
      alicuota.Id = ivat.Id
      alicuota.BaseImp = NetoTX.Text
      alicuota.Importe = ImpIvaTx.Text

      Dim alicuota2 As New AlicIva
      Dim ivat As IvaTipo = TipoIVACmb.SelectedItem
      alicuota.Id = ivat.Id
      alicuota.BaseImp = NetoTX.Text
      alicuota.Importe = ImpIvaTx.Text


      det.Iva = {alicuota1, alicuota2}

      A forma de ejemplo

      Eliminar
  15. Hola recién me encuentro con esta maravilla, todavía no la implemente, pero por lo que estuve viendo debería funcionar, sos muy crack y no quería mas de darte las gracias por anticipado! capo! gracias luego comento si pude implementarlo. saludos.

    ResponderEliminar
  16. Buenos dias, estoy viendo esto ya que quiero implementar la factura electronica... la diferencia de esta forma es que deberia armar yo el reporte de la factura no? Y el otro metodo de afip que ya devuelve el PDF de la factura electronica se puede implementar en .NET?

    ResponderEliminar
    Respuestas
    1. Hola, el documento de la factura lo vas a tener que armar por tu cuenta, podes usar Algún complemento para trabajar con pdf, o usar el impresor común de .NET y usar la impresora virtual de Windows. En conclusión, la implementación de AFIP es bastante limitada, y te conviene usar tu propia implementación aunque la tengas que hacer uno mismo.

      Recuerdo que vas a necesitar por obligación incorporar el código de barra de la factura que incluye los codigos de CAE y otra información, te podes guiar en el modelo de afip para armar el tuyo.

      Eliminar
  17. Hola Muchas gracias, la verdad una muy buena explicación de todo sin tu aporte hubiera sido imposible
    generar los servicios web q manejen factura electronica

    ResponderEliminar
  18. Podrias generar un codigo para ws CTG estaria barbaro

    ResponderEliminar
    Respuestas
    1. Jaja no trabajo con eso... ya suficiente tortura fue hacer esto. Pero si algun dia lo hago lo subire!

      Eliminar
    2. Yo lo acabo de generar a todos los metodos

      Eliminar
  19. Guillermo muchísimas gracias por compartir tus conocimientos, la verdad te pasaste! mil gracias capo! Me funciono muy bien en homologación. Ahora me surgió una duda cuando lo estoy empezando a probar contra los servidores de producción.
    Te pregunto: cuando desde el proyecto de .Net agregas las referencias a los WS de AFIP de WSAA y de WSFE ahí supuestamente indicas los servidores de homologación pero según el ejemplo el WSAA esta haciendo referencia al de producción (https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl) para homologación no debería ser https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl ? o como después desde el código le asignas la url de homologación (en “Private url As String = https://wsaahomo.afip.gov.ar/ws/services/LoginCms” y después servicio.Url = url )no hay problema y le pega directamente al WS de homologación?
    Es decir, mientras después desde el código le asigne la URL de homologación es indistinto la referencia que agregue en el proyecto? Espero haber sido claro con mi consulta, de nuevo mil gracias por tu tiempo!

    ResponderEliminar
    Respuestas
    1. Buenas, si, excelente todo, ya hice todo en HOMOLOGACION, pero ahora en produccion, ademas de la url del servicio, que hay que cambiar??
      En la declaracion del servicio (Dim service As New WSFEHOMO.Service) hay que modificarlo con Dim service As New WSAA.LoginCMSService??? Porque si es asi el procedimiento service.FECompUltimoAutorizado no existe... no se como obtener el ultimo comprobante como lo hago con HOMOLOGACION... intente solo cambiar la URL y en service.FECompUltimoAutorizado me devuelve siempre 0.

      Eliminar
    2. Hola para hacer el login la url de produccion sería: "https://wsaa.afip.gov.ar/ws/services/LoginCms"
      y para hacer la factura la url a utilizar es "https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL"
      Obviamente a parte de utilizr el certificado generado para producción.
      Si te devuelve siempre 0 es porque nunca hiciste una factura en producción con el web service.

      Eliminar
    3. Gracias Javier! Justo me di cuenta hace un rato... tenia mal la url del WS de produccion... en este ejemplo siempre la llama url... entonces ahi estaba la confusion... hice 2 variables diferentes para no confundirme y quedo ok!! No era que me devolvia 0... en depuracion vi que daba un error de SOAP... al llamar a obtener ultimo comprobante...ahora si en produccion me devuelve 0 y obviamente porque en el punto de venta nuevo que cree para facturacion electronica, es el primero!

      Eliminar
  20. Guillermo muchísimas gracias por compartir tus conocimientos en la en el wsfe lo pude implementar y funciona perfecto,
    no tendrías un ejemplo para el Wsmtxca

    ResponderEliminar
  21. Hola Guillermo, te hago una consulta, no puedo mandar mas de una alicuota por factura, en el ejemplo dice que es un array pero no entiendo como hacerlo. Desde ya muchas gracias!

    ResponderEliminar
    Respuestas
    1. Hola, yo lo resolvi asi:
      En la parte final donde declara el FECAEDetRequest,
      Dim det As New FECAEDetRequest
      With det
      .Concepto = 1 'Productos
      Select Case cboTipo.Text
      Case "A"
      .ImpNeto = neto21 + neto105
      .ImpIVA = iva1 + iva2
      Dim alicuota1 As New AlicIva

      alicuota1.Id = 5
      alicuota1.BaseImp = neto21
      alicuota1.Importe = iva1

      Dim alicuota2 As New AlicIva

      alicuota2.Id = 4
      alicuota2.BaseImp = neto105
      alicuota2.Importe = iva2

      If neto21 > 0 And neto105 > 0 Then
      .Iva = {alicuota1, alicuota2}
      ElseIf neto21 > 0 Then
      .Iva = {alicuota1}
      Else
      .Iva = {alicuota2}
      End If

      .DocTipo = 80
      .DocNro = cuit

      Algo así sería, espero que te ayude.

      Eliminar
    2. Este comentario ha sido eliminado por el autor.

      Eliminar
  22. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  23. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  24. Hola! una pregunta, se puede re utilizar el ticket de acceso? Por ejemplo, ya tengo un ticket de acceso, y se me cierra la aplicacion, cuando vuelvo a acceder me sale este cartel "el cee ya posee un ta valido para el acceso al wsn solicitado" y tengo que esperar como 10 min, para loguearme de nuevo y que pueda tener un nuevo ticket de acceso. Hay alguna manera de volver a solicitar o utilizar el ticket que generé al principio? Ya implemente todo lo que pusiste, y funciona muy bien, salvo este detalle. Gracias!

    ResponderEliminar
    Respuestas
    1. Hola Javier, fijate que tenes una variable que te dice cuando vence el TA llamada ExpirationTime o UltimoExpirationTime, yo en mi sistema pregunto si es mayor o menor a la fecha y hora actual y en base a la respuesta pido o no un nuevo TA. Suerte!

      Eliminar
    2. Justamente como dice dkforward, en Homologación tenes la libertad de crear otro TA teniendo otro vigente. En producción no. Tienes solamente un TA valido a la vez. Por lo que deberías almacenar el TA y usarlo hasta que esté vencido. En mi código lo que hago es, antes de establecer la conexión con sus servidores es revisar el último TA, ser si sigue vigente, de ser necesario obtener uno nuevo y recien ahi establecer la conexión.

      Eliminar
  25. Hola buenas noches antes que nada agradezco enormemente que hayas compartido tus conocimientos es un excelente tutorial. Quería consultar sobre un inconveniente que me surgió y que sucedió lo mismo a otro usuario. El problema es que no se está cargando el combobox de los puntos de ventas. Hice el debug y verifiqué que no está trayendo los datos de los puntos de ventas para luego poder cargarlos en el combobox pero si trae los otros datos de los demás combobox. Gracias por tu tiempo!

    ResponderEliminar
    Respuestas
    1. Deverias verificar que tenes el Punto de Venta en el registro de afip
      https://www.sos-contador.com/2017/08/02/en-51-pasos-como-obtener-certificado-digital-e-incorporarse-a-factura-electronica-version-ago2017/

      Eliminar
  26. Buenas... no se porque al postear aparezco como Unknown... cerre sesion y volvi a entrar y sigo saliendo igual

    ResponderEliminar
  27. YO LO RESOLVI INTRODUCIENDO MANUALMENTE EL PUNTO DE VENTA, GUARDADO EN LA BASE DE DATOS, TOTAL ESE DATO NO CAMBIA

    ResponderEliminar
    Respuestas
    1. Deverias verificar que tenes el Punto de venta como RECE
      https://www.sos-contador.com/2017/08/02/en-51-pasos-como-obtener-certificado-digital-e-incorporarse-a-factura-electronica-version-ago2017/

      Eliminar
    2. Como dice guillermo y Henry, yo lo tengo agregado al punto de venta en el RECE y no me aparece, como pudieron de esta manera introducir el punto de venta? me podrian ayudar por favor? porque no se como realizar esto. el punto de venta me queda vacio, aunque halla colocado correctamente el certificado digital y el punto de venta agregado

      Eliminar
  28. La verdad que esto para mi fue la gloria y funciona sin problemas, ahora me están pidiendo generar los archivo de citi venta rg3685 alguien tiene algo avanzado de esto para no empezar de cero a lidiar?

    ResponderEliminar
    Respuestas
    1. Creo que se a que te referis. Los hice medio hardcodeado, Es un archivo de texto plano, pero es un embole hacerlo. Si necesitas ayuda contactame a guille.marcel04@gmail.com

      Eliminar
  29. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  30. Henry como andas, te consulto, ya pude generar un certificado y conectarme al servidor TESTING... lo del combobox de pto ventas le agregue a mano tambien y pude registrar comprobantes. Ahora, queria saber una vez que pase todo el codigo a mi sistema... que es lo que deberia hacer en AFIP para pasar de un certificado de TESTING a uno de PRODUCCION?? Porque ahora obviamente al querer loguearme me dice "CERTIFICADO NO EMITIDO POR AC DE CONFIANZA". Gracias.

    ResponderEliminar
    Respuestas
    1. Mas arriba esta en la seccion de "Obtener certificado de AFIP..." en la seccion de Produccion. Al comienzo del post

      Eliminar
    2. Si, gracias Guillermo, ya tengo la app funcionando y facturando sin problemas! Un abrazo.

      Eliminar
  31. Hola maestro fijate arriba de todo hay unos links donde explican como obtener los certificados de prueba y los de produccion, una vez que tenga el de produccion lo unico que te toca hacer es cambiar la web a donde busca en el enlace, si te fijas hay 2 web una para cada tipo una dice entro otras cosasa homo y la otra services. cambiando simplemente eso mas el certificado, ya estarias emitiendo comprobantes electronicos.

    ResponderEliminar
    Respuestas
    1. Exactamente. Con el certificado de producción y el link del servidor de producción ya esta funcionando.

      Eliminar
  32. Hola, muchas gracias por el tutorial antes que nada, me está siendo de gran ayuda. Tengo una duda, si quisiera consumir el WS para hacer facturación eléctronica desde una web app, deberia utilizar alguna otra libreria? o si o si tiene que ser implementado en .NET?

    ResponderEliminar
    Respuestas
    1. Aca implemente particularmente .NET, pero les deje concretamente lo que tienen que enviar en forma de XML. Pueden o usar algun complemento para manejar SOAP, o armar los XML a mano y enviarlos como String.

      Eliminar
  33. Hola a ver is alguien me puede dar una mano, ya pude poner a facturar un par de pc sin problemas, pero las ultimas dos e tiran el mismo cartel de error"computador no autorizado a acceder al servicio" sin duda algo estoy haciendo mal, por ahi alguien me puede dar una mano le voy a agradecer saludos.

    ResponderEliminar
    Respuestas
    1. Yo estoy justo terminando... recien pude hacer las funciones para genrerar el codigo de barras...ahora tengo que cambiar la url para produccion y probar todo... tengo que generar el certificado de produccion para reemplazarlo al que estoy usando... despues te comento a ver si me pasa algo parecido.

      Eliminar
    2. Buenas Henry, ahi hice todo lo de PRODUCCION... y me salio lo mismo que a vos, computador no autorizado.. .fui a AFIP y luego a ADMINISTRADOR DE RELACIONES DE CLAVE FISCAL--> NUEVA RELACION--> BUSCAS EN AFIP/WEBSERVICE/FACTURACION ELECTRONICA y ahi en REPRESENTANTE le das BUSCAR y elegis el COMPUTADOR FISCAL..
      y bueno ahi me dejo hacer el login con el certificado de produccion... pero ahora al ir a facturacion, el procedimiento service.FECompUltimoAutorizado me devuelve error.... no puedo obtener el ultimo comprobante autorizado de cada tipo de comprobante...

      Eliminar
    3. Buenas, ya lo pude solucionar, tenia mal la url de produccion.
      Consulta... alguno implemento el Webservice para obtener los datos de la persona? Porque en este excelente ejemplo esta agregado WSPSA4 pero no esta implementado.

      Eliminar
    4. Mmm por lo que lei creo que no se pueden obtener esos datos, los usa AFIP en facturacion WEB, es asi? Queria usarlo para verificar el alta nada mas, espero comentarios.

      Eliminar
  34. Hola como estas, no tendrías un ejemplo con FACTURAS de CRÉDITO ELECTRÓNICA MiPymes ?
    saludos y gracias

    ResponderEliminar
  35. Consulta alguien sabe algo del tema remito electrónico, me están empezandoa pedir eso y me parece que esta relacionado algo con todo esto? no se me pueden dar una mano

    ResponderEliminar
    Respuestas
    1. Debe ser un servicio SOAP, del mismo estilo que este servicio. No pongo mis manos en el fuego por los servicios de AFIP pero creo que debe ser muy similar.

      Eliminar
  36. IMPORTANTE: para los que no se les llena el combobox ptodeventa... verificar que tengan un punto de vta tipo RECE habilitado aca les desjo el link
    https://www.sos-contador.com/2017/08/02/en-51-pasos-como-obtener-certificado-digital-e-incorporarse-a-factura-electronica-version-ago2017/

    ResponderEliminar
    Respuestas
    1. Muchas Gracias! Evidentemente me olvide del paso de que tienen que tener creado el Punto de Venta.

      Eliminar
  37. Hola Gente, estoy implementado la factura con detalles con el WsMtxca lo pude hacer funcionar a medias, con un Ítem todo bien cuando tengo que mandar mas de uno ahí se me complica con el ArrayItems no le encuentro la vuelta para mandar mas de uno si alguno me puede echar una mano
    Saludos y mil Gracias

    ResponderEliminar
    Respuestas
    1. Daniel tendras la url de test para la WsMtxca ?, Por que el publicado en la documentacion de AFIP no da ping https://fwshomo.afip.gov.ar/wsmtxca/services/MTXCAService?wsdl

      Eliminar
    2. Lo lamento, yo particularmente no he trabajado con eso.
      Pero te recuerdo que para la gran mayoria de los comerciantes, la AFIP no requiere el detalle de las facturas, solamente la sumarizacion del mismo y el detalle de las alicuotas nomas

      Eliminar
  38. Hola, excelente aporte! Muchas gracias por compartir! Una consulta, el Nro del comprobante se tiene que tomar de algún rango de números autorizados para el punto de venta o de donde sale ese dato?

    ResponderEliminar
    Respuestas
    1. El Número de Comprobantes debe ser el Secuencialmente Siguiente del último. Es decir, si el ultimo fue 1, en número actual es 2, y así. Por esto, recomiendo ir a buscar antes el último comprobante antes de enviar la factura. Asi sabras cual es el último número y con eso sabras que numero corresponde a este.

      Eliminar
    2. Muchas gracias Guillermo por tu pronta respuesta.
      Saludos
      Pablo

      Eliminar
  39. buenas, en el login, en clave que va ahi?

    ResponderEliminar
    Respuestas
    1. Cuando exportas el Certificado de formato pk10 a pk14, tienes la opción de agregarle una clave al certificado. Esa clave es la que va ahi. Si no le pusiste clave al certificado, el campo clave debería ir vacío.

      Eliminar
  40. hola
    como calculaste el iva seleccionando desde el combobox si es 0 o 16 te agradeceria tu respuesta

    ResponderEliminar
  41. Buenas!! Desde ayer no puedo acceder a los servicios web de testing que ofrece afip. Sabes algo?
    https://wswhomo.afip.gov.ar/wsfev1/service.asmx?wsdl

    ResponderEliminar
    Respuestas
    1. O quien consultar o avisar que esto sucede.. no encontre nada en la web de afip

      Eliminar
  42. Genio, despues de romperme la cabeza, pude lograr realizar el ticket de acceso, ahora cuando realizo el request para enviar la factura, al realizar el detalle, lo cargo todo bien pero cuando cargo el arrays me salta error y no se que hago mal, quizas en tu experiencia podrias ayudarme. Me base en tu ejemplo mas arriba. Gracias y saludos
    Dim alicuota1 As New AlicIva

    alicuota1.Id = 5
    alicuota1.BaseImp = subtotal21 'el importe gravado sin iva
    alicuota1.Importe = subtotal21 + acumular21


    det.Iva(0).Id = alicuota1.Id().ToString
    det.Iva(0).BaseImp = Convert.ToDouble(alicuota1.BaseImp)
    det.Iva(0).Importe = Convert.ToDouble(alicuota1.Importe)


    '10.5%
    Dim alicuota2 As New AlicIva
    alicuota2.Id = 4
    alicuota2.BaseImp = subtotal10
    alicuota2.Importe = subtotal10 + acumular10

    det.Iva(1).Id = alicuota2.Id
    det.Iva(1).BaseImp = Convert.ToDouble(alicuota2.BaseImp)
    det.Iva(1).Importe = Convert.ToDouble(alicuota2.Importe)

    ResponderEliminar
    Respuestas
    1. Perdon no puse el error, en det.Iva(0).id salta "referencia a objeto no establecida como instancia de un objeto"

      Eliminar
  43. Muy buen aporte Guillermo! llegue mas lejos de lo que pude llegar solo en menor tiempo. Funciona perfecto y lo tengo implementado en vb. Ahora se me presenta una situación y lo estoy pasando a C# el cual me genera un problema en el paso fin al (obtener la aprobación), ¿ de casualidad alguien lo implemento en ese lenguaje que me pueda dar una mano?

    ResponderEliminar
  44. Buenas tardes, antes que nada infinitas gracias por el GRAN APORTE!!! Realmente tu manera de explicar es genial incluso para los menos experimentados (como es mi caso). Ademas de agradecerte queria pedirte un poco de ayuda, ya que, me trabe con algo y estoy perdido por donde viene el error.
    Estoy trabajando en produccion, e hice todo al pie de la letra pero en la parte de obtener el certificado : "certificado.Import(File.ReadAllBytes(cert_path), clave, X509KeyStorageFlags.PersistKeySet)" se me genera el error "La matriz no puede estar vacía o tener un valor null.Nombre del parámetro: rawData".
    Voy a tratar de ser claro con lo que hice a ver si con tu generosidad encontras mi falla:
    Cuando agregue la refencia, le puse de nombre afip.wsaa y la ruta "https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl"
    Tuve que cambiar en 'Hago el login: Dim servicio As New afip.wsaa.LoginCMSService
    En el Login, cambie la url para que apunte a produccion : Private url As String = "https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl"
    y paso los datos por string: l = New CLogin("wsaa", url, "D:\AFIP\CERTIFICADOS\FE\certificado.pfx", "")
    A mi se me ocurre que hay un problema con el certificado, o que este faltando alguna referncia o que estoy haciendo algo mal en AFIP.
    Con respecto al certificado, hice los pasos que tenes en el otro post y salio todo de 10.
    Con respecto a las referencias, agregue System.Security y System.Web.Services, ademas de todos los "Imports" de la Clase LoginClass
    Con respecto a AFIP, tengo el cerificado "Valido", el cual descargue y es el que estoy utilizando.

    Bueno, te agradezco nuevamente, y me seria de mucha ayuda que me orientes por donde buscar la solucion.
    Saludos

    ResponderEliminar
  45. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  46. Excelente trabajo, muchas gracias por compartir tus conocimientos en forma desinteresada.

    ResponderEliminar
  47. Buenas... alguien pudo generar el codigo de barras??? Probe varios metodos, con INT2of5, ambos con los ejemplos que busque me quedaron bien en teoria.. ahora... voy a probarlos con el lector de codigos de barra y no me da pelota.... lo raro es que imprimi una factura de AFIP y tampoco lo leo! El lector es nuevo y se que lee esa codificacion.... que podras ser?? Probe tambien generar el string en CODE128C y lo mismo... no lo puedo leer... ahora voy a leer cualquier boludez y el lector me la lee... alguien sabe que puede suceder???

    ResponderEliminar
  48. Hola! excelente aporte y muy bien explicado, gracias! voy a probar tu librería de conexión en un caso real ya que utilizo archivos.dll para conexión con afip pero en C#, en cuanto al código de barras vi controladores fiscales nuevos imprimiendo códigos QR, es fácil de adaptar en caso que alguien lo necesite, descargar desde Nuget el paquete Zxing para generar el QR, Gracias!!!

    ResponderEliminar

  49. Muchas gracias por el aporte, Ya estoy conectado a afip listo para pedir el CAE. Pero cuando pulso en Registrar me da error de conversion de variable tipo double. es que soy monotributista. y no tengo que descriminar IVA por la opcion importe neto e importe total me da el mismo error. me podrias echar una mano en donde y como puedo convertir la variable double que manda el nerto o el total?. gracias

    ResponderEliminar
  50. Hola. ya solucione el problema para facturas C. le elimine todo lo concerniente a IVA para que no diera ese error. porque no puedo discriminar en factura C, asi que no se puede enviar una cadena vacia en IVA. lo comento por si a alguien le hace falta.

    ResponderEliminar
  51. Excelente Tutorial, excelente trabajo. Ya pude facturar electronicamente en modo Produccion. Te agradezo muchisimo el aporte que hiciste. Solo me queda una inquietud en la fase final, en la presentacion de la factura en crystal reposts. Todo funciona perfecto, solo me falta como ingresar el codigo de barras. No se como ingresar la libreria BarcodeLib.Barcode.WinForms en el crystal reports. Tenes una idea de como tengo que hacer para que me salga el codigo de barras en la factura? Muchas gracias

    ResponderEliminar
  52. Hola!! exelente tutorial, muy bueno, y la verdad muy bien información, lo unico que falta es el punto de venta, yo ya tengo el certificado hecho correctamente, y el punto de venta dado de alta con RECE en afip, el problema es que me queda vacio, muchos le sucede lo mismo, He intentado rellenar el campo desde la base de datos como algunos alegan pero no entiendo como se realiza, podria alguien especificar esto? por favor espero alguien que me sepa ayudar, y comparta esta información, desde ya muchass gracias

    ResponderEliminar
  53. Buenisimo una guia invaluable. y funciona a la perfeccion. lo pude adaptar a mi proyecto perfectamente. Consulta general: tenes una guia para consulta padron de contribuyentes A5 ?? gracias

    ResponderEliminar
  54. Hola. Muchas gracias por tu trabajo. Lo estoy convirtiendo a C#.
    Tengo una pregunta. Cual puede ser la razon por la que no me carga ningun punto de venta? (En el programa de muestra)

    ResponderEliminar
    Respuestas
    1. Lei que varios usuarios tienen el mismo problema, estara relacionado con algun delay en la alta del punto de venta RECE?

      Eliminar
    2. en la práctica a mi me paso que el punto de venta en realidad necesito informarselo yo desde distintas computadoras. saludos.

      Eliminar
    3. En el entorno de homologacion no los trae, en el entorno de PRODUCCION si los trae, siempre y cuando el CUIT emisor haya ingresado previamente con su clave fiscal y haya creado el punto de venta para facturacion onlines (se llama de una forma para responsable inscriptos y de otra para monotributistas) una vez que este punto de venta fue creado en la pagina de la afip, recien los traerá desde el ws. SALUDOS

      Eliminar
  55. Hola, buenisimo el tutorial y me ayudo un monton, tengo una duda, cuando quiero emitir una Factura B, tengo entendido que no necesito enviar el DocNro, pero el programa me exige ingresar un valor sino me tira una exepcion, tienes alguna idea de como se haria en este caso? Muchisimas gracias de antemano

    ResponderEliminar
    Respuestas
    1. si es menos de 5000 en efectivo no te pide dni podes poner varios ceros si es menos de 10 mil idem para ventas electronica (mercadopago, tarjeta, transferencia, etc) lo mismo, si los importes superan esos valores segun la forma de pago del cliente tenes que mandar dni y nombre y apellido

      Eliminar
  56. Buenas Guillermo,

    Tenes implementado el consumo de Ventanilla Electronica? Podria delegarte este trabajo. espero tus comentarios. Te dejo mi cel: 115-103-6034

    ResponderEliminar
  57. Hola venia bien al principio, luego por error mio del sistema y trabajar apurado, me mande varias macanas, ahora quisiera saber si es posible, hacer una consulta de una factura y extrar el iva que se cargo normalmente vendo con iva de 10.5 y 21 deberia saber todos los datos, iva 21 y 10.5 neto, grabado y subtotal, si alguien me pueda dar una mano voy a agradecerles, saludos HErnan.

    ResponderEliminar
    Respuestas
    1. finalemente lo pude hacer sin problemas si alguien necesita una mano me avisa, me sirvio para generar reportes de iva ventas! saludos

      Eliminar
    2. Hola Henry, no puedo lograr resolverlo, como lo hiciste? Trabajo en VB.net muchas gracias

      Eliminar
  58. Hay que agradecer a gente como vos, sólo entré para chusmear.. pero hiciste un gran aporte.
    Te mando un gran saludo!

    ResponderEliminar
  59. Hola Guillermo. Muchas gracias por tu aporte, es muy valioso. Mi consulta tiene que ver con el TA. Debido a que el mismo tiene una duración de 12 hs, que sucede si mi sistema no alcanza a almacenar el SIGN y TOKEN del mensaje, porque supongamos, se corta la luz justo cuando AFIP ya autorizó el TA y yo no le podido recibir? Tengo forma de recuperarlo? El sistema quedaría inaccesible por 12 hs? En algunos sistemas, enviando el mismo UNIQUEID devuelve el mismo Resultado original, pero veo que este no es el caso, dado que en mi primer intento, no tenía diseñado el almacenamiento y tuve que esperar 12 horas hasta que se venciera el primer ticket. Gracias

    ResponderEliminar
  60. Hola. Desde donde se puede obtener la letra que identifica el comprobante? (A,B,C,X....)

    ResponderEliminar
  61. Hola, muchas gracias por el aporte, realmente clarisimo. Te consulto tendrias idea como se informan en la factura las percepciones de ingresos brutos y de iva que lleva la misma si el vendedor es agente de percepcion?

    ResponderEliminar
  62. Muy buena la explicación, tendras código de ejemplo? Saludos

    ResponderEliminar
  63. Buenas! alguien está intentando hacer la conexion a WS de afip con PHP? si alguien tiene info avise por fa!

    ResponderEliminar
    Respuestas
    1. vi algo en gitgub, si no recuerdo mal era algo de afipSDK algo asi

      Eliminar
  64. Hola muchas gracias por el aporte lo seguí y he logrado avances importantes, ahora te hago una pregunta, si es un Monotributista y hace facturas "C" únicamente y no discrimina IVA, al finalizar y a pesar de colocar alícuota "0" y el neto = bruto me da un error "La cadena de entrada no tiene el formato correcto" alguna sugerencia otra vez Muchgas Gracias. Enrique

    ResponderEliminar
  65. Estoy teniendo un problema a la hora de agregar tributos. ¿Cómo se agregan los tributos?

    ResponderEliminar
    Respuestas
    1. Después de probar finalmente descubrí como se hace. Si van a agregar tributos al objeto Tributos se le asigna asi: .Tributos = {tributo}. Se crean un objeto Tributo, le agregan los valores a los atributos y lo asignan de esa forma. Si tienen mas de un tributo los van agregando así: .Tributos = {tributo1, tributo2, tributo3}. Recuerden sumar los importes de cada tributo y agregarlo al .ImpTrib

      Eliminar
  66. Hola. Estoy teniendo una duda en como recuperar el TA. Yo ya logre grabar en la una tabla el token, el sign y las fechas. Compruebo si esta vencido o no. Ahora mi duda, como armo el TA con eso datos, porque veo q cuando quiero enviar pide el certificado ya realizado anteriormente y si se cerro el programa no lo tengo. Alguien me puede guiar en eso. La verdad me perdi donde usar el token y el sign recuperados.

    ResponderEliminar
    Respuestas
    1. Buen dia, yo lo solucioné modificando el codigo y exporto en txt datos del certificado, entonces si se cerro el programa da la excepcion y lee el txt, si lo necesitas te lo envio a tu correo.

      Eliminar
  67. Excelente explicación. Muchas gracias por compartir tu conocimiento y dedicarnos tu tiempo.

    ResponderEliminar
  68. Estoy usando mi sistema de ventas con la incorporacion de esta guia/script desde el mes de enero sin problemas, ayer quise sacar junio y me salta "no es pósible comunicarse con el servidor de afip" y cuando consulto ultimos comprobantes sale "no se puede crear un canal seguro SSL/TLS". nada se toco en programacion. y nada cambio en la afip. alguien le paso algo parecido ?? saludos

    ResponderEliminar
  69. por si alguien le sirve. logre solucionar el inconveniente agregando antes del login, en loginclass.

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

    con eso esta operativo todo de nuevo.
    saludos

    ResponderEliminar
    Respuestas
    1. Hola. También tengo ese problema desde hace unos días y el certificado según el sitio de AFIP esta ok. Dónde pusiste esa línea?. Que es servicepointmanager?. Gracias

      Eliminar
    2. genial! yo lo puse dentro de hacer login, al principio

      Eliminar
  70. Dit is echt een handige lijst voor mij! Ik moet toegeven dat je een van de beste bloggers bent die ik ooit heb gezien. Bedankt voor dit bericht over Groepenkast kopen.

    ResponderEliminar
  71. Excelente aporte, muy bueno, felicitaciones, no es fácil encontrar algo tan complejo y tan bien explicado y desarrollado.

    ResponderEliminar
  72. Muchas Gracias por compartir tu conocimiento! Siguiendo este tutorial logré la facturación electrónica evitando la instalacion de una impresora fiscal en mi negocio. Deberías poner tu CBU para transferir una donación como agradecimiento.

    ResponderEliminar
    Respuestas
    1. Apoyo eso... con lo que me ayudo esto!

      Eliminar
    2. Que me pase el alias que ya hago transferencia, creo que son si ayuda no hubiera llegado a hacerlo. Por más gente como vos!

      Eliminar
  73. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  74. Buen dia, te puedo consultar si es posible, gracias a esto que desarrollaste pude realizar la conexion a FE, uchas gracias por eso, ahora bien, mi consulta es sobre la emision de Tique Factura, comprobantes de afip 081, 082 y 083 respectivamente sobre la tabla de afip, como es eso, en realidad necesito imprimir en comandera,se imprime ese tique, o estoy confundido y la salida por comandera de la FE, es la misma factura que emitis de los items 01, 06, 011, me gustaria saber si me podes aclarar eso. Gracias desde ya

    ResponderEliminar
  75. Cm ago para q me pueda avilitara la costancia d AFIP ya preste ase 3 días es d una coperativa q se llama unidos en Fontana

    ResponderEliminar
  76. Buenas, alguien implemento lo del QR en .NET? No entiendo de donde se saca el codigo de autorizacion....

    ResponderEliminar
  77. Buenas te adjunto el código que utilice para generar el JSON Serializado en base64.
    Tenes que tener en cuenta que todavía no esta funcional desde el lado de afip, asi que cuando
    ingreses a la URL generada, te derivará a https://www.afip.gob.ar/fe/qr/conceptos-generales.asp

    Espero les sea de ayuda!

    public class QR_DATOS_MODEL
    {
    public string ver { get; set; }
    public string fecha { get; set; }
    public string cuit { get; set; }
    public string ptoVta { get; set; }
    public string tipoCmp { get; set; }
    public string nroCmp { get; set; }
    public string importe { get; set; }
    public string moneda { get; set; }
    public string ctz { get; set; }
    public string tipoDocRec { get; set; }
    public string nroDocRec { get; set; }
    public string tipoCodAut { get; set; }
    public string codAut { get; set; }
    }

    public static string QR_GET(DateTime FECHA_EMISION, string EMISOR_CUIT, int COMPROBANTE_ID, string COMPROBANTE_NUMERO, decimal D_IMPORTE, string MONEDA, int RECEPTOR_DOCUMENTO, string RECEPTOR_DOCUMENTO_NUMERO, string CAE)
    {
    string WS_QR_URL = "https://www.afip.gob.ar/fe/qr/";
    string URL = CGLOBAL.WS_QR_URL + "?p=";

    string IMPORTE = D_IMPORTE.ToString("N2");
    int ENTERO = int.Parse(IMPORTE.Split(',')[0]);
    float DECIMAL = float.Parse(IMPORTE.Split(',')[1]);
    IMPORTE = DT.CompletarConCerosAlPrincipio(ENTERO.ToString(), 13);
    IMPORTE += DT.CompletarConCerosAlPrincipio(DECIMAL.ToString(), 2);

    QR_DATOS_MODEL DATOS_Q = new QR_DATOS_MODEL()
    {
    ver = "1",
    fecha = FECHA_EMISION.ToString("yyyy-MM-dd"),
    cuit = DT.CompletarConCerosAlPrincipio(CGLOBAL.WS_EMISOR_CUIT, 11),
    ptoVta = DT.CompletarConCerosAlPrincipio(CGLOBAL.WS_PV, 5),
    tipoCmp = DT.CompletarConCerosAlPrincipio(COMPROBANTE_ID.ToString(), 3),
    nroCmp = DT.CompletarConCerosAlPrincipio(COMPROBANTE_NUMERO, 8),
    importe = IMPORTE,
    moneda = MONEDA,
    ctz = DT.CompletarConCerosAlPrincipio("1", 13) + "000000",
    tipoDocRec = DT.CompletarConCerosAlPrincipio(RECEPTOR_DOCUMENTO.ToString(), 2),
    nroDocRec = DT.CompletarConCerosAlPrincipio(RECEPTOR_DOCUMENTO_NUMERO.ToString(), 20),
    tipoCodAut = "E",
    codAut = DT.CompletarConCerosAlPrincipio(CAE.ToString(), 14)
    };

    //SERIALIZAR JSON
    var account = DATOS_Q;
    string json = JsonConvert.SerializeObject(account);
    string base64EncodedExternalAccount = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
    return URL + base64EncodedExternalAccount;
    }

    ResponderEliminar
    Respuestas
    1. Half, usas algún paquete de json para generarlo y convertirlo?

      Eliminar
    2. Perdón la demora en contestar, mucho trabajo estas semanas.

      Para serializar o deszerializar el JSON uso la clase Newtonsoft.Json

      Para generar el codigo QR instalo desde el NUGET "QRCoder", en mi caso para ahorrar espacio en la base de datos no guardo la imagen en la base de datos, sino que guardo el código completo, y dinámicamente cuando la necesito genero la imagen que uso en la impresión.


      Esta es la función que uso para la creación del QR, pasando como parámetro la URL+base64EncodedExternalAccount anteriormente obtenidos en la función que puse arriba.

      public static byte[] QR_CREAR(string DATOS)
      {
      QRCodeGenerator qrGenerator = new QRCodeGenerator();
      QRCodeData qrCodeData = qrGenerator.CreateQrCode(DATOS, QRCodeGenerator.ECCLevel.Q);
      BitmapByteQRCode qrCode = new BitmapByteQRCode(qrCodeData);
      byte[] qrCodeAsBitmapByteArr = qrCode.GetGraphic(20);
      return qrCodeAsBitmapByteArr;
      }

      El byte[] que devuelve la función, directamente lo paso al REPORTE, y en el reporte lo asigno al campo imagen.
      Espero les sea útil.
      Saludos

      Eliminar
    3. hola Half, gracias por el código, justo necesito el QR.
      Una vez que generás el array de bytes, veo que decís que lo asignás al campo IMAGEN de REPORTE.
      ¿A qué llamás REPORTE, o sea qué clase del código de ejemplo que ponés?
      Mil gracias!

      Eliminar
    4. no dije nada! ya me di cuenta como hacerlo, grax!

      Eliminar
  78. hola HALF, yo uso VB pero obviamente entiendo tu codigo, ahi el retorno es un string no? como lo convertis a la imagen QR propiamente dicha?

    ResponderEliminar
    Respuestas
    1. Usando una clase Qrcode que podes bajar del nuget

      Eliminar
    2. Buenos dias, ya logre hacer todo, use la libreria de esta web

      https://www.ajpdsoft.com/modules.php?d_op=viewdownloaddetails&lid=298&name=Downloads

      Eliminar
    3. Gracias, usando la dll del enlace que pase pude generar el QR en un picturebox y despues guardarlo en BLOB en mi base de datos y usar ese campo en el informe, me quedo todo ok.

      Eliminar
  79. hola Half, por casualidad usastes esta libreria ??
    https://github.com/codebude/QRCoder/

    recien voy a empezar con este tema del QR.
    siempre un nuevo invento tienen......


    ResponderEliminar
    Respuestas
    1. Hace mucho q se implemento en otros países, por lo menos sirve para ver si un comprobante es trucho, hay muchos con comprobantes truchos dando vuelta. Aunque estaría bueno que funcione, aun sigue sin funcionar del lado de la AFIP, redirecciona a un sitio de la AFIP, estaría bueno que muestren el comprobante online así también las empresas pueden ver si concuerda lo presentado en la AFIP y el comprobante enviado por el proveedor.

      Eliminar
    2. Alguien sabe cuándo estará funcional del lado de AFIP el tema del código qr? Sigue derivando a la web de AFIP genérica.

      Eliminar
  80. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  81. Buenas, despues de pelear un largo rato, logre actualizar el sistema al qr.
    Les dejo el codigo.
    Se instala por NUGET los paquetes de System.Text.Json y de ZXing.Net.
    Para generar la url:
    Primero declaran una clase, no pongo todo el codigo para no hacerlo largo, hacen asi por todos los campos.
    Public Class QR
    Public Property ver() As Integer
    Get
    Return m_Ver
    End Get
    Set
    m_Ver = Value
    End Set
    End Property
    Private m_Ver As Integer
    end class

    Cargan generan el objeto y lo cargan
    Dim objQR As New QR
    With objQR
    .ver = 1
    .fecha = Now.ToString("yyyy-MM-dd")
    .cuit = EMISOR_CUIT
    .ptoVta = PV
    .tipoCmp = COMPROBANTE_ID
    .nroCmp = COMPROBANTE_NUMERO
    .importe = Format(D_IMPORTE, "0000000000000.00")
    .moneda = MONEDA
    .ctz = "1"
    .tipoDocRec = RECEPTOR_DOCUMENTO.ToString()
    .nroDocRec = RECEPTOR_DOCUMENTO_NUMERO.ToString()
    .tipoCodAut = "E"
    .codAut = CAE
    End With

    Dim WS_QR_URL As String = "https://www.afip.gob.ar/fe/qr/"
    Dim url As String = WS_QR_URL + "?p="
    Dim json As String = JsonSerializer.Serialize(objQR)
    Dim base64EncodedExternalAccount As String = Convert.ToBase64String(Encoding.UTF8.GetBytes(json))
    Dim direccion_qr As String = url + base64EncodedExternalAccount


    Generamos el qr

    Dim GENERADOR As BarcodeWriter = New BarcodeWriter 'INICIALIZA EL GENERADOR
    GENERADOR.Format = BarcodeFormat.QR_CODE
    GENERADOR.Options.Margin = 2
    GENERADOR.Options.Width = 1000
    GENERADOR.Options.Height = 1000
    Dim IMAGEN_QR As Bitmap
    IMAGEN_QR = New Bitmap(GENERADOR.Write(direccion_qr), 600, 600)

    Y listo problema resulto...
    Use parte del codigo de Half, asi que los correspondientes creditos a el.


    ResponderEliminar
  82. buenas noches. trabajo solo en visual, me resulta muy comodo. les comparto el codigo para Qr de las facturas nueva resolucion. Tome data de Half y bnjis, graciassss por el aporte.

    Instale por nuget Newtonsoft.Json y QRCoder
    Finalmente grabo en disco la imagen del codigo Q, porque despues lo toma la rutina que arma el pdf de la factura completa.
    Espero que sirva para los que aun usamos vb.net

    saludos

    Daniel




    Public Class QR_DATOS_MODEL
    Public Property Ver As String 'version "1"
    Public Property Fecha As String 'fecha emision
    Public Property Cuit As String 'cuit emisor
    Public Property PtoVta As String 'punto venta
    Public Property TipoCmp As String '001 FactA /006 FactB
    Public Property NroCmp As String 'numero de compro
    Public Property Importe As String 'importe total
    Public Property Moneda As String 'moneda "PES"
    Public Property Ctz As String 'cotizacion = "1"
    Public Property TipoDocRec As String 'Documento Cliente 80 CUIT //96 DNI
    Public Property NroDocRec As String 'cuit cliente o DNI
    Public Property TipoCodAut As String 'tipo Cae = "E"
    Public Property CodAut As String 'numero Cae

    End Class

    Private Sub CodigoQ()

    'convierto fechas
    Dim nuevoano As String = Module1.mfecha.Substring(6, 4)
    Dim nuevomes As String = Module1.mfecha.Substring(3, 2)
    Dim nuevodia As String = Module1.mfecha.Substring(0, 2)
    Dim fechaemision As String = nuevoano + "-" + nuevomes + "-" + nuevodia

    'Cargan generan el objeto y lo cargan
    Dim codigoQ As New QR_DATOS_MODEL

    With codigoQ

    .Ver = 1
    .Fecha = fechaemision
    .Cuit = "xxxxxxxxxxx"
    .PtoVta = Convert.ToString(Convert.ToDecimal(Module1.mptovta))
    .TipoCmp = Module1.mcompafip
    .NroCmp = Convert.ToString(Convert.ToDecimal(Module1.mnumero))
    .Importe = Replace(Module1.mtotal, ",", ".")
    .Moneda = "PES"
    .Ctz = "1"
    If Module1.mtipodoc = "CUIT" Then
    qtipodoc = "80"
    End If
    If Module1.mtipodoc = "DNI" Then
    qtipodoc = "96"
    End If
    .TipoDocRec = qtipodoc
    .NroDocRec = Module1.mnumdoc
    .TipoCodAut = "E"
    .CodAut = Module1.mcae

    End With


    Dim json As String = JsonConvert.SerializeObject(codigoQ)
    Dim base64 As String = Convert.ToBase64String(Encoding.UTF8.GetBytes(json))
    Dim url_qr As String = "https://www.afip.gob.ar/fe/qr/" + "?p=" + base64

    Dim qrGenerator As QRCodeGenerator = New QRCodeGenerator()
    Dim qrCodeData As QRCodeData = qrGenerator.CreateQrCode(url_qr, QRCodeGenerator.ECCLevel.Q)
    Dim qrCode As QRCode = New QRCode(qrCodeData)
    Dim imagenqr As Bitmap = qrCode.GetGraphic(20)
    imagenqr.Save("c:\Sistemas\SistemaDeVentas1.0\SistemaDeVentas1.0\Imagenes\CodigoQR.png", System.Drawing.Imaging.ImageFormat.Png)

    End Sub

    ResponderEliminar
    Respuestas
    1. Hola. funciona perfecto, pero cuando quiero imprimir en una impresora termica Hasar 250 no tiene buena resolucion para ser escaneado por la camara del celular. lo cargo en un picturebox y lo imprimo con e.Graphics.DrawImage

      Eliminar
  83. consulta:
    La afip me devuelve por ej: {"FACTURA":150}
    Es decir es el comprobante número 150.
    Ahora si vemos una factura cualquiera, van a ver que empieza tipo 0001-000000000150
    Ese primer número que onda? Hay algún servicio para conseguirlo o de donde se saca?

    ResponderEliminar
    Respuestas
    1. Es el punto de venta... eso se crea en AFIP y despues lo configuras en tu sistema, generalmente empieza con 1 o cambia si ya tenian punto de venta y despues les crearon puntos de venta RECE.

      Eliminar
  84. Corresponde al punto de venta. Para poder facturar online debes crear entrando con el cuit y clave fiscal del titular un punto de venta del tipo RECE. Ese punto de venta es el que enviaras al momento de hacer la factura y es el que aparece delante del número de la factura

    ResponderEliminar
  85. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  86. buen día gente como va? les hago una consulta yo utilizo el comando FECompConsultaReq para traer los datos de una factura, pero me gustaria poder consulta varias facturas en una sola consulta poder traer una matriz de facturas o una lista, alguien tiene idea si es posible y como?

    , desde ya gracias, saludos!

    ResponderEliminar
    Respuestas
    1. Hola buen día, no existe la opción "desde" y "hasta" en la consulta, por lo que veo es solo por un comprobante.
      Saludos.

      Eliminar
  87. Hola, consulta, tengo problemas para completar el CbtesAsoc para hacer notas de crédito, probé varias formas y nada, pueden hacer notas drédito? gracias y excelente aporte.

    ResponderEliminar
    Respuestas
    1. No se si lo pudiste resolver pero yo lo que hago es lo siguiente
      CbteAsoc objcompasoc = new CbteAsoc();
      objcompasoc.PtoVta = Convert.ToInt32(NegocioConfigEmpresa.puntoventa);
      objcompasoc.Nro = comprobanteasoc;
      objcompasoc.Tipo = comprobantefactura;
      det.CbtesAsoc = new[] { objcompasoc };

      Instancio la clase cbteasoc agrego el punto de venta, el tipo de factura que quiero generar la nota de credito y
      el numero de comprobante de la factura

      Eliminar
    2. Hola, si, hice algo muy similar instanciando un objeto, pero mirando tu ejemplo le quité la fecha del comprobante y funciona, muchas gracias!!!!

      Eliminar
    3. Hola estimado, tendrias un ejemplo de como realizar la nota de credito?

      Eliminar
    4. Hola ASD, disculpa no puedo lograr hacer la nota de credito me podrias tirar una idea de como lo hiciste? o el codigo sino es mucho pedir muchas gracias

      Eliminar
  88. Al que generó esta página que es tan útil, estaría bueno crear un foro de WebServices con desarrolladores Visual Studio.
    Dejo la inquietud.
    Saludos!

    ResponderEliminar
  89. Aparece dentro del servicio MIS COMPROBANTES

    ResponderEliminar
  90. Es simple. Entras a AFIP con clave fiscal nivel 3. Si no tienes adherido el servicio que se llama MIS COMPROBANTES lo haces desde el servicio que se llama ADMINISTRADOR DE RELACIONES- presionas donde dice nueva relación, luego en AFIP interactivos y ahí buscas MIS COMPROBANTES y le das en adherir. Una vez agregado tendrás que salir y volver a entrar con tu clave fiscal y ya tendrás el servicio MIS COMPROBANTES, donde puedes ver tanto las FE emitidas como recibidas. Cualquier cosa mi mail es carlosalfonsofuentes@gmail.com. saludos

    ResponderEliminar
  91. Hola! Excelente el post! Tengo una pregunta.
    Tengo que emitir facturas tipo C pero con un detalle de ítem sencillo, por servicios. Trabajo en sis temas y la factura debería decir, por ejemplo: "Honorarios implementación sistema de facturación" y por supuesto el importe.

    Pero aparentemente el servicio WSFEV1 no permite enviar detalle, y el "WSMTXCA", en su decripción dice que es para facturas tipo "A" y "B"...

    ¿Se puede usar tb para emitir facturas tipo "C" con un detalle de ítem?

    Mil gracias!!

    ResponderEliminar
  92. Hay q diferenciar un par de cosas…
    Vos a Afip no le mandas descripción de lo q vendes, solo el total. Vos en tu factura lo armas.
    Para hacer factura c el q emite el l comprobante tiene q ser monotributosta y mandar el código correspondiente del comprobante. Sino te va a dar error.

    ResponderEliminar
    Respuestas
    1. Hola @bnjis :)
      No sé si estás contestandome a mí (@sheik). Si es así, debe haber algo más básico que no estoy entendiendo: estos servicios de AFIP (ya sea WSFEV1 o WSMTXCA) ¿No permiten emitir facturas? ¿La invocación no implica la generación de una FC de la que luego me puedo descargar el PDF ya completo, y para eso no necesito poder indicar el detalle? ¿Cómo es eso de que "yo en mi factura lo armo"?
      Mi idea es emitir facturas listas para descargar e imprimir o enviar por correo para mis posibles clientes, no que que tengan que entrar al sitio de AFIP a armar su factura "luego"... :(

      Eliminar
    2. No estás entendiendo cómo funciona esto.
      Aquí vos programas tu código en tu sistema el cual se comunica con la Afip y le das los parámetros de facturación como indican en el post. La Afip responde confirmando el comprobante con los datos necesarios para que vos en tu sistema diseñes como te guste (siguiendo los parámetros q te pide Afip) la factura.
      Si tu idea es simplemente emitir facturas, sin anexarlo a un aplicativo, usa el facturador q brinda la Afip en su web.

      Eliminar
    3. Gracias @bnjis :)
      Qué garrón! Lo que pasa es que mis posibles clientes (personal de salud: fonoaudiólogos, psicólogos, etc.) deben emitir decenas de facturas por mes, casi todas con datos muy similares, con datos que pueden estar en base de datos o en un Excel, CSV, etc.

      Mi idea era ahorrarles entrar la engorrosa página web de AFIP factura por factura, que les lleva horas al ser tantas facturas, y generarles las facturas de una...

      Entonces lo que tengo que hacer es "autorizar" facturas con los servicios web, y por cada FC de la que los WS me dan el OK, generar el PDF yo, en el cual puedo incluir el detalle que yo quiera?

      Para ello debo homologar mi sistema con la AFIP, o simplemente cumplir con ciertos requisitos?

      Leí bastante documentación, pero nunca encontré nada como lo que te estoy preguntando al respecto...

      Mil gracias por tus rápidas respuestas :)

      Eliminar
  93. Ups, creo que te aburrí con mis preguntas de newbie :(
    Me salvarías la vida si me contestaras esas últimas :)
    Saludos y gracias por adelantado

    ResponderEliminar
  94. Consulta, alguno utilizo los web service para validar los datos del cliente?

    ResponderEliminar
  95. Hola como estas estoy queriendo utilizar esto pero en una app core 3.1 c# y estoy teniendo problemas. Tenes algo para guiar

    ResponderEliminar
  96. Hola. estoy migrando a Net core y tengo problemas con el cmssigner.
    parece que ya no soporta SHA1 pero no encuenrto nada de documentacion de como solucionarlo.
    Alguna mano?
    Dim contentInfo As New ContentInfo(argBytesMsg)
    Dim signedCms As New SignedCms(contentInfo)
    signedCms.ComputeSignature(cmsSigner, False)
    Return signedCms.Encode()

    ResponderEliminar
  97. Estuve buscando en el WebService y la consulta de la condición frente al IVA no la encontré.

    ResponderEliminar
  98. Hola a todos, como estan? Quisiera saber si alguien sabe como se haria el procedimiento desde Java - Netbeans. Desde ya muchas gracias

    ResponderEliminar