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/open?id=0B5Xpb7ydUT4XLTU0VjBFQ3FVTEE


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





viernes, 24 de octubre de 2014

Aplicacion Android Auto Actualizable Sin Market

Hola de nuevo!!

Hoy traeremos la solucion para muchos que no pueden subir su aplicacion a Google Play (Ex Market) para que se actualice automáticamente con cada version, o para los que no quieran hacerlo por ser una version privada o por cualquier motivo sea.

Esto conciste en que la aplicacion automaticamente buscara una version mas reciente en el servidor, de haber una actualizacion la bajara e instalara, reemplazando la actual. Todo esto sin tener que depender de ningun market.

Para este proyecto voy a trabajar sobre el Android Studio de Google, sobre el lenguaje Java, Pero pueden usar cualquier IDE como Eclipse, sobre cualquier lenguaje siguiendo la logica.

Yo subire el archivo APK a un servidor propio por FTP, pero tambien se puede usar cualquier servicio de nuve que nos permita obtener el link publico (Dropbox recomiendo o Google Drive creo).

Comenzemos.

En el servidor vamos a subir dos archivos:
El APK de la aplicacion terminada y
un TXT que le llamaremos "version.txt":



en donde "versionCode" y "versionName" son los tags usados en el AndroidManifest.xml
y "downloadURL" es la direccion URL del APK (este link puede ser tambien uno de Dropbox u otro siempre que sea enlace directo). Recordemos que este archivo lo tendremos que cambiar manualmente con cada actualizacion (Para hacerlo automaticamente tambien hay una forma la cual voy a poner bajo de todo).


CODIGO!!!!

Primero y antes de que nos de error estableceremos los permisos que necesitamos en el AndroidManifest.xml


El primero para poder conectarnos a internet y el segundo para guardar el instalador en la memoria antes de ejecutarlo.

Ahora en nuestro proyecto android crearemos una clase Autoupdater.java
esta clase se encargara basicamente de todo. Tambien ejecutara las conecciones a internet a travez de AsyncTasck s para no conectarse en el hilo principal (Como ya sabran para evitar el NetworkOnMainThreatException).
En la clase que creamos (Autoupdater.java) ponemos este codigo.


Esa es la clase principal que va a hacer casi todo.

Ahora tenemos que insertarlo en la clase de UI (Interfaz de Usuario).
para eso recomiendo poner un ProgressBar en un RelativeLayout en alguna parte del layout donde vamos a ejecutar el Autoupdater.

Algo simple como esto (importante el id para reconocer):

Y tendremos que poner algo de codigo en la clase Principal, en este ejemplo hicimos que haga la actualizacion ni bien comienza la aplicacion.
Quedaria algo como esto:


Eso es lo que va en algun lado de tu pantalla donde se actualizara.
Tambien lo podemos hacer mediante un boton. Simplemente hay que llamar al metodo
comenzarActualizacion() desde donde quieras.
Algo asi querdaria con un boton:




Esto seria todo lo necesario para hacer que la aplicacion sea autoactualizable, si ven el codigo de la clase Autoupdate.java veran que esta todo comentado para que lo entiendan y modifiquen.

Ahora para cada actualizacion lo unico que tienen que hacer es subir el APK al servidor con un link publico (ya sea web propia, o servido de cloud) y modificar los TAGS en el archivo version.txt del servidor. Tambien recuerden de cambiar los TAGS en el AndroidManifest.xml en donde dice:


Pasandolo con cada aplicacion a algo de este estilo:



Recuerden que este metodo verifica la version instalada con la version en el servidor. Y si cambian una y no la otra, siempre pensara que hay una actualizacion disponible.

Aqui les dejo los archivos necesarios:



Y los agradecimientos a quienes me dieron esta idea y sobre los cual esta basada:

Androcode.es

Juro: StackOverflow


Esto es todo, Muchas gracias y espero que les sirva.

Cualquier cosa en los comentarios.



lunes, 28 de abril de 2014

Correr android en Android.

Hola de vuelta.

Hoy vamos a ver como hacer que nuestra aplicacion creada en Android Studio corra o se debugee en un Dispositivo Android tablet o telefono sin tener que buscar el APK o  hacer cosas raras... directamente con un boton. Sin pasar por una maquina virtual.

De vuelta. Yo trabajo con Android Studio, pero lo mismo funciona para cualquier otra IDE (Entorno de Desarrollo Integrado) con los SDK (Kit de Software de Desarrollo) de Android ya sea Net Beans, Eclipse, o el que les guste, solo tienen que buscar cada boton en algun lado.

Bueno empecemos.

Lo primero necesitamos un cable USB para el dispositivo. Recomiendo siempre los originales que vienen con ellos. Sobre todos los U2 de Samsung. Si no creo que cualquiera sirve.

Sin enchufarlo vamos a habilitar la opcion de debug en el telefono. Eso se encuentra en AJUSTES -> OPCIONES DE DESARROLLADOR (o Developer Options)-> DEPURACION POR USB (o USB Debuggin)





TIP:
En caso de que no encuentren las OPCIONES DE DESARROLLADOR en los ajustes es porque lo tienen oculta. Para mostrarlo tienen que ir a ACERCA DEL TELEFONO (o tablet) y precionar varias veces (6 o 7) en el NUMERO DE COMPILACION hasta que les diga AHORA ERES UN DESARROLLADOR.
Un pequeño toque de google

Ahora si, arrancamos el Android Studio y abrimos el proyecto si todavia no estaba abierto.
Conectamos el dispositivo por usb. Si los drivers no estan instalados esperamos a que se instalen.

Una vez instalados el programa deberia reconocerlo solo y te deberia quedar algo asi.

De abajo para arriba, en la pestaña de android de abajo te aparece ese menu, En donde el ComboBox de la derecha aparece el nombre de tu dispositivos y el SO que carga.

Mas arriba vemos los controles para correr la aplicacion. El primer boton (Play) es para correr la aplicacion en el dispositivo conectado, el segundo ( Bicho, Bug ) es para debugear la aplicacion, y el tercero ( Bicho con telefono) es para conectar el debugueador a la aplicacion si ya esta corriendo.

Cuando querramos correr la app en el telefono directamente apretamos play. y nos apareceria esta ventana:


Simplemente nos aseguramos de que sea nuestro dispositivo y le damos OK...

Con eso nada mas esperando como mucho un minuto deberiamos estar viendo la aplicacion correr en nuestro dispositivo.

Espero que les halla servido







martes, 28 de enero de 2014

Instalar drivers no firmados en Windows 8 u 8.1

Buenas tardes.

 Este problema me atormento la cabeza durante mucho tiempo. Aun después de hacerlo repetidas veces cada vez tengo que buscar donde es que estaba esa opción.

 Si es una opción y esta demaciado oculta. Como para que nadie lo haga.
Bueno para que querríamos hacer esto?
Para los que crean drivers Mod, para los que consiguen drivers Mod de hardware viejo o extraño.
En mi caso yo lo use para instalar los drivers de una tablet China sin marca, solo un Modelo raro:

Bueno en fin vallamos a ello.

Primero en el panel lateral vamos a CONFIGURACIÓN y después a CAMBIAR CONFIGURACIÓN DE PC

desde allí iremos en el panel lateral la opción ACTUALIZAR Y RECUPERAR luego en el mismo panel iremos a la opción de RECUPERACIÓN. Allí iremos a donde dice INICIO AVANZADO sobre el botón que dice REINICIAR AHORA. Con esto reiniciaremos la pc en un modo avanzado



Con eso reiniciaremos el sistema, y cuando arranque iniciara en el menú avanzado donde tenemos muchas configuraciones mas profesionales... Acá vemos como llegar a lo que nos interesa, los drivers:

Cuando reinicie veran un menu azul con tres opciones. Seleccionan SOLUCIONAR PROBLEMAS.


Aca vamos a la opcion OPCIONES AVANZADAS:

De ahi a CONFIGURACIONES DE INICIO:

Aca podremos ver las opciones que nos dejara elegir mas adelante. Alli podran ver la opcion de instalar Drivers no firmados.
Simplemente le damos REINICIAR.

Esto reiniciara el sistema una vez mas. Ahora aparecera una pantalla muy parecida a la anterior.
Cuidado con lo que aprietan. Simplemente tienen que apretar el numero que aparece al lado de la opcion DESHABILITAR EL USO OBLIGATORIO DE CONTROLADORES FIRMADOS.
en mi caso es la tecla 7

Una vez hecho eso el sistema se reiniciara de vuelta. Esta vez iniciando en el inicio comun.
Si todo salio bien ya deberias tener permiso para instalar estos drivers.
El permiso se pierde una vez reiniciado el sistema. Asi que cada vez que querramos instalar drivers tendremos que repetir el proceso

Para instalar el driver ahora la cosa es mas simple.
Vamos al panel derecho y abrimos las configuraciones. Alli deberia estar el PANEL DE CONTROL.
Una vez alli abrimos el ADMINISTRADOR DE DISPOSITIVOS.

En esta nueva ventana buscamos el dispositivo que queremos instalar. Por lo general tendran esta forma. o un nombre generico

Una vez que lo encuentres simplemente le das doble click buscas el boton de actualizar controlador.
Le indicas donde es que esta el controlador que bajaste.

Ahora no deberia dar problema.
___________________________________________________________

Problema aparte.
Los que tengan una tablet china sin marca y quieran hacerla degugear para probar sus aplicaciones necesitan el driver primero que todo... Despues les mostrare como hacer para debugear aplicaciones en android.

Aqui les dejo el driver generico para esas tablet. La gran mayoria se manejan bien con este driver.

 Driver Tablet China


Espero que halla servido.






lunes, 27 de enero de 2014

Correr Android

Como hacer para correr nuestra aplicacion de Android en Android, es decir, en un telefono con android conectado por usb directamente.

O en PC, es decir, en una Maquina virtual para que la puedas probar, para aquellos que no tengan un dispositivo android, o que quieran probar en dispositivos variados.

Proximamente....


jueves, 23 de enero de 2014

Crear aplicacion auto actualizable con Visual Studio

Hola de nuevo.

Hoy les voy a mostrar algo que me costo mucho hacer.

Yo tengo escrita una aplicación en VB.NET con Visual Studio 2012. Estoy muy seguro que funciona con cualquier aplicación de escritorio hecha con Visual Studio. La misma aplicación yo hago actualizaciones y revisiones constantemente y varias personas usan esta aplicación. Para no tener que andar instalando una nueva versión del programa cada vez que corrijo o agrego algo, utilice la opción de PUBLICACIÓN del Visual Studio. Ahora la aplicación busca e instala la ultima versión antes de abrirse. A su vez también puede abrirse Off-Line con la ultima versión bajada.

Bueno empecemos.

Los requisitos:
  1. Visual Studio 2012 (es la que uso yo pero seguro en otras es igual o parecido)
  2. La Aplicación lista para compilar.
  3. Servidor FTP (bueno yo lo voy a hacer con un servidor que pago, también se puede hacer con recursos de red, solo para la red local)
El servidor tiene que tener instalado las Exenciones de FrontPage. En caso de no tenerlas pedir a su proveedor del servidor que las instale, yo lo tuve que hacer.

______________________________________________________________________________

Primero lo primero. en el servidor creamos un usuario FTP para el Visual Studio. y le especificamos una carpeta para la instalación de la aplicación. También pueden usar el usuario root o el común y especificar después la ruta.

una vez creado el usuario me quedaría así
Usuario: usuarioInstalador
Pass: passUsuarioInstalador
path: /httpdocs/instaladorPrograma/

entonces cuando habrá el servidor con ftp me lleve directo a esa carpeta
ftp://servidor.com (iniciado con ese usuario) ->  /httpdocs/instaladorPrograma/
______________________________________________________________________________

Ahora yendo al VS vamos a configurar la opción de PUBLICAR.

- En el explorador de soluciones vamos a ir al panel de mi proyecto:

luego en la pestaña de la derecha donde dice PUBLICAR.


   Allí configuramos la 'Ubicación de la carpeta de publicación' con el link FTP que configuramos. (En el botón con putos suspensivos al lado podrán configurar el usuario y contraseña)
y la 'Dirección URL de la carpeta de instalación' que es donde se guardara la pagina donde después instalaremos el programa.

La versión de publicación es irrelevante y se actualiza cada vez que hagamos una publicación

Ahora lo interesante. Abrimos el botón que dice ACTUALIZACIONES y veremos esto:

   allí configuraremos el tema de las actualizaciones automáticas. Como mis actualizaciones muchas veces corrigen errores prefiero que se actualice siempre antes de iniciar la aplicación. y dan aceptar

Listo. Ya esta todo configurado
Lo único que queda por hacer es COMPILAR y PUBLICAR.

guardan todo y en la pestaña de COMPILAR (parte superior) y le dan PUBLICAR.
con eso el VS debe conectarse con el servidor y subir todo.
probablemente les pida de vuelta los datos del usuario FTP.
en este caso: US: usuarioInstalador PASS: passUsuarioInstalador

______________________________________________________________________________

La Instalación

Ahora para instalar esta aplicación deben ir a la pagina donde se publico mediante el protocolo HTTP

en este caso seria así: http://servidor.com/instaladorPrograma/publish.htm 
la pagina publish.htm se crea automáticamente. Eso también se puede configurar para que la cree con otro nombre si quieren.
Y la pagina debería ser algo así:



donde el nombre del publicador y la versión de publicación se puede configurar.

Para instalar simplemente hacen clic en el botón instalar.
se les baja un archivo SETUP.EXE
el mismo lo abren y les instala solo el programa.

Con esto hecho cada vez que abran la aplicación buscara actualizaciones.

Recuerden que el programa se actualizara cada vez que hagan una publicación nueva no con cada compilación (ya sea en DEBUG o RELASE).

Las configuraciones se deberían guardar todas.


Espero que les haya sido de utilidad.

lunes, 20 de enero de 2014

Android: ResourcesLoweCase y AndroidStudioUpdateCompativility

Buenas.
Yo programo en android, usando el nuevo Android Studio presentado en la Google I/O del 2013
actualmente la versión 0.4.2 (preview). Anda muy bien el programa y cada vez anda mejor. la verdad que lo recomiendo mucho para los que tengan ganas de programar para esta plataforma y tengan algo de conocimiento de Java.

Bueno en esta plataforma de trabajo he tenido varios problemas que a medida que me acuerde ire relatando.

Sobre todo la que me hizo dar mucha vuelta fue esta:
Execution failed for task ':ClaudeGarnie:mergeDebugResources'.
> C:\Users\Guille\Documents\Android\ClaudeGarnieProject\ClaudeGarnie\src\main\res\layout\Abc.xml: Error: Invalid file name: must contain only lowercase letters and digits ([a-z0-9_.])

En fin despues de un rato largo de dar vuelta ( creo que en la versión en la que me paso el problema no estaba tan explicito el problema). En fin una vez bien leído el problema descubrí que el nombre de los resources de android solo pueden estar en MINÚSCULAS o con números.

Ahí aprendí a leer bien los problemas.


Luego un problema reciente causado por la actualizacion de la plataforma a la version 0.4.2

A problem occurred evaluating project ':ClaudeGarnie'.
> Gradle version 1.6 is required. Current version is 1.9

Para esto habían varias variantes y varias soluciones.

Mi problema: Se actualizo Android Studio junto con el gradle y se ve que hubo un problema de compatibilidad con el que trabaja el proyecto actual.

Mi Solución: simplemente modificar el archivo build.gradle en la linea:
   classpath 'com.android.tools.build:gradle:0.5.+'
a : classpath 'com.android.tools.build:gradle:0.7.+'
y rehacer el proyecto ( ctrl + F9 )
Bueno esas fueron los primeros problemas