Fuentes Factura electrónica Argentina

Post Reply
horacio
Posts: 1270
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Fuentes Factura electrónica Argentina

Post by horacio »

Colegas, posteo las fuentes para la generación de FE en argentina totalmente operativas. Este trabajo fue desarrollado por Carlos A. Fernandez Cardeneu que, generosamente las publicó en foro de Google sobre este tema. Solo se puede compilar con Harbour ya que utiliza funciones que no tienen equivalencia en xHarbour ( Creo ). Se pueden emitir facturas A y B y con _ también C. Lo interesante es que tenemos código nativo, sin necesidad de usar aplicaciones de terceros y es un buen ejercicio para aquellos que necesiten entender como funciona el consumo de servicios WebService.

Code: Select all

Static cToken    
Static cSign     
Static cCuit     
Static nUltCmpAut

//----------------------------------------------------------------------------//
Function Main()

    * Declaro Las Variables De Trabajo
    Local nOpc := 1
    * Declaro estas Variables como privadas para poder usarlas en todas las funciones
    cToken     := ''
    cSign      := ''
    cCuit      := '20267565393' // CUIT de la Empresa
    nUltCmpAut := 0
    //Private CRLF       := chr( 13 ) + chr( 10 ) // Retorno de carro y avance de linea

    * Leo los valores del Ultimo Ticket De Acceso -> cSign
    Leo_Ultimo_Ticket_de_Acceso()
    Cls
    nOpc := 1
    While( nOpc # 0 )
        @ 04, 18 Prompt Padr( '1) Consultar el Estado de los Servidores', 44, ' ' )
        @ 06, 18 Prompt Padr( '2) Pedir el Ticket de Acceso', 44, ' ' )
        @ 08, 18 Prompt Padr( '3) Consultar el Nro de la Ultima FACTURA', 44, ' ' )
        @ 10, 18 Prompt Padr( '4) Pedir el CAE de una FACTURA', 44, ' ' )
        Menu to nOpc
         If( nOpc == 1 )
            Opc_FEDummy()
        ElseIf( nOpc == 2 )
            Opc_PidoElTicketDeAcceso()
        ElseIf( nOpc == 3 )
            Opc_FECompUltimoAutorizado( cToken, cSign, cCUIT )
        ElseIf( nOpc == 4 )
            Opc_FECAESolicitar( cToken, cSign, cCUIT )
        End
    Enddo
    Return( .t. )

//----------------------------------------------------------------------------//
*: Leo_Ultimo_Ticket_de_Acceso          :
*: Leo El Ultimo Ticket De Acceso Para Leer El Token Y El Sign Y Validar Si :
*: Esta Dentro De Las 12 Horas De Que Fue Emitido.       :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
    Function Leo_Ultimo_Ticket_de_Acceso()

    Local nIdx               := 0
    Local cXml            := ''
    Local aRespuesta      := {}
    Local cFechaHoraDesde := ''
    Local cFechaHoraHasta := ''
        
    * Si NO existe el archivo TA.xml es porque NO tengo un Ticket de Acceso (TA) 
    * Dejo las variables vacias con lo cual obligo a pedir el TA desde el menu ==
    If( !File( 'TA.xml' ) )
        MsgStop( 'NO existe TA.xml;Pida un Nuevo TICKET DE ACCESO' )
        Return( .f. )
    End
    cXml       := MemoRead( 'TA.xml' )
    aRespuesta := Opc_LeeRespuesta( cXml, 'TA' )
    If( !Empty( aRespuesta ) )
        nIdx := Ascan( aRespuesta, { | aAux | aAux[ 1 ] = 'token' } )
        If( nIdx # 0 )
            cToken := aRespuesta[ nIdx, 2 ]
        End
        nIdx := Ascan( aRespuesta, { | aAux | aAux[ 1 ] = 'sign' } )
        If( nIdx # 0 )
            cSign := aRespuesta[ nIdx, 2 ]
        End
    End
    * Valido que TA todavia sea VALIDO 
    nIdx := Ascan( aRespuesta, { | aAux | aAux[ 1 ] = 'generationtime' } )
    If( nIdx # 0 )
        cFechaHoraDesde := CToD( aRespuesta[ nIdx, 2 ] )
    End
    nIdx := Ascan( aRespuesta, { | aAux | aAux[ 1 ] = 'expirationtime' } )
    If( nIdx # 0 )
        cFechaHoraHasta := aRespuesta[ nIdx, 2 ]
    End
    Return( .t. )

//-----------------------------------------------------------------------------//
*: LlamoAlWSAA Llamo al WS de AUTENTICACION para pedir el Ticket de Acceso :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function LlamoAlWSAA( c_CMS_Base64 )

    Local cXml       := ''
    Local oWSAA      := ''
    Local cWSAA_URL  := 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms'
    Local cRespuesta := ''

    oWSAA  := Win_OleCreateObject( 'MSXML2.XMLHTTP' )
    If( Empty( oWSAA ) )
        MsgStop( 'NO se Pudo Crear el Objeto oWSAA;se Cancela el Programa' )
        Return( .f. )
    End
    * Armo el XML con el TRA
    cXml += '<?xml version="1.0" encoding="UTF-8"?>'    + CRLF
    cXml += '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' + CRLF
    cXml += '<soap:Body>'       + CRLF
    cXml += '    <loginCms xmlns="http://wsaa.view.sua.dvadac.desein.afip.gov">' + CRLF
    cXml += '    <in0>' + c_CMS_Base64 + '</in0>'    + CRLF
    cXml += '    </loginCms>'       + CRLF
    cXml += '</soap:Body>'       + CRLF
    cXml += '</soap:Envelope>'       + CRLF
    * Llamo al Webservice y defino Opciones
    oWSAA : Open( 'POST', cWSAA_URL, .f. )
    oWSAA : SetRequestHeader( "SOAPAction:", "None" )
    oWSAA : SetRequestHeader( "Content-Type", "text/xml;charset=UTF-8" )
    * Envio el Archivo y Recibo la Respuesta del WS
    oWSAA : Send( cXml )

    * Si el status es diferente a 200, ocurri¢ alg£n error de conectividad con el WS ---
    cRespuesta := oWSAA : ResponseText
    if( Empty( cRespuesta ) )
        MsgStop( "Error; cRespuesta esta VACIO" )
    End
    * Por las Dudas de que la Respuesta venga en Formato RAW
    If( !Empty( cRespuesta ) )
        If( '<' $ cRespuesta ) // <
            cRespuesta := StrTran( cRespuesta, '<', '<' )
        End
        If( '>' $ cRespuesta ) // >
            cRespuesta := StrTran( cRespuesta, '>', '>' )
        End
        If( '"' $ cRespuesta ) // "
            cRespuesta := StrTran( cRespuesta, '"', '"' )
        End
        If( '&apos;' $ cRespuesta ) // \
            cRespuesta := StrTran( cRespuesta, '&apos;', '\' )
        End
        If( '&' $ cRespuesta ) // &
            cRespuesta := StrTran( cRespuesta, '&', '&' )
        End
    End
    * Hago mas legible el XML
    cRespuesta := StrTran( cRespuesta, '><', '>' + CRLF + '<' )
    * Dicen que si no se borra el Objeto, despues de un tiempo se incrementa mucho
    * el uso de memoria por parte del programa. No lo constate, pero por las dudas
    oWSAA  := NIL
    Release oWSAA
    Return( cRespuesta )

//----------------------------------------------------------------------------//
*: LlamoAlWSFE Llamo al WS de Negocios para Procesar el Requerimiento     :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function LlamoAlWSFE( c_Xml, c_ServicioLlamado )

    Local oWSFE  := ''
    Local cWSFE_URL := 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx' // Modo HOMOLOGACION
    *Local cWSFE_URL := 'https://servicios1.afip.gov.ar/wsfev1/service.asmx' // Modo PRODUCCION
    Local cRespuesta := ''

    * Creo y Valido el Objeto oWSFE
    oWSFE  := Win_OleCreateObject( 'MSXML2.XMLHTTP' )
    If( empty( oWSFE ) )
        MsgStop( 'NO se Pudo Crear el Objeto oWSFE;se Cancela el Programa' )
        Return( .f. )
    End

    * Llamo al Webservice y Defino Opciones
    oWSFE   : Open( 'POST', cWSFE_URL, .f. )
    oWSFE : SetRequestHeader( 'SOAPAction', 'http://ar.gov.afip.dif.FEV1/' + c_ServicioLlamado )
    oWSFE : SetRequestHeader( "Content-Type", "text/xml;charset=UTF-8" )
    oWSFE : SetRequestHeader( 'Connection', 'Keep-Alive' )

    * Envio el Archivo y Recibo la Respuesta del WS
    oWSFE   : Send( c_Xml )

    * Si el status es diferente a 200, ocurri¢ alg£n error de conectividad con el WS
    cRespuesta := oWSFE : ResponseText
    If( Empty( cRespuesta ) )
        MsgStop( "Error;cRespuesta esta VACIO" )
    End

    * Por las Dudas de que la Respuesta venga en Formato RAW
    If( !Empty( cRespuesta ) )
        If( '<'  $  cRespuesta ) // <
            cRespuesta := StrTran( cRespuesta, '<', '<' )
        End
        If( '>'  $  cRespuesta ) // >
            cRespuesta := StrTran( cRespuesta, '>', '>' )
        End
        If( '"'  $  cRespuesta ) // "
            cRespuesta := StrTran( cRespuesta, '"', '"' )
        End
        If( '&apos;'  $  cRespuesta ) // \
            cRespuesta := StrTran( cRespuesta, '&apos;', '\' )
        End
        If( '&'  $  cRespuesta ) // &
            cRespuesta := StrTran( cRespuesta, '&', '&' )
        End
    End
    * Hago mas legible el XML
    cRespuesta := StrTran( cRespuesta, '><', '>' + CRLF + '<' )
    oWSFE  := NIL
    Release oWSFE
    Return( cRespuesta )

//----------------------------------------------------------------------------//
*: Opc_FEDummy Verifica el funcionamiento de los servidores de la afip     :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function Opc_FEDummy()
    
    Local nHandle
    Local cXml       := ''
    Local aRespuesta := {}
    Local cRespuesta := ''

    * Armo El Archivo Xml con el Pedido
    cXml += '<?xml version="1.0" encoding="UTF-8"?>'   + CRLF
    cXml += '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' + CRLF
    cXml += '<soap:Body> '      + CRLF
    cXml += '   <FEDummy xmlns="http://ar.gov.afip.dif.FEV1/" />' + CRLF
    cXml += '</soap:Body>'      + CRLF
    cXml += '</soap:Envelope>'      + CRLF
    
    * Esto NO hace falta, pero lo hago para controlar como se formo el XML
    If( ( nHandle := fCreate( 'FEDummy_Req.xml', 0 ) ) == -1 )
        MsgStop( 'NO se pudo crear FEDummy_Req.xml' )
        Return( .f. )
    Else
        fWrite( nHandle, cXml )
        fClose( nHandle )
    End
    * Llamo al WS
    cRespuesta := LlamoAlWSFE( cXml, 'FEDummy' )
    hb_MemoWrit( 'FEDummy_Res.xml', cRespuesta )
    MsgStop( 'FINALIZO la CONSULTA DEL ESTADO;DE LOS SERVIDORES DE LA AFIP;EL PEDIDO SE GRABO COMO: FEDummy_Req; LA RESPUESTA RECIBIDA SE GRABO COMO: FEDummy_Res' )
    * Muestro el contenido del Xml Recibido
    aRespuesta := Opc_LeeRespuesta( cRespuesta, 'FEDummy' )
    MuestroRespuesta( aRespuesta )
    Return( .t. )

//----------------------------------------------------------------------------//
*: PidoElTicketDeAcceso Funci¢n para Pedir el Tique de Acceso a los     :
*:   Servidores de la AFIP        :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function Opc_PidoElTicketDeAcceso()

    Local cBat
    Local nHandle
    Local aRespuesta
    Local cCert              := 'c:\pyrece\reingart\reingart.crt' // The X.509 obtained from Seg. Inf. 
    Local cPrivateKey    := 'c:\pyrece\reingart\reingart.key' // The private key correspoding to CERT
    Local cSource        := 'C=AR, O=PyAfipWs-Sistemas Agiles, SERIALNUMBER=CUIT 20267565393, CN=Mariano Reingart'
    Local cDestinationDn  := 'cn=wsaahomo,o=afip,c=ar,serialNumber=CUIT 33693450239'
    Local cUniqueId          := '1432505199' // > VER DE DONDE LO OBTENGO   * NUMERO QUE IDENTIFICA EL REQUERIMIENTO, TIENE QUE SER DISTINTO CADA VEZ
    Local cGenerationTime := TimeFMT( HB_DateTime(), -1 ) // - 1Hs
    Local cExpirationTime := TimeFMT( HB_DateTime(), 1 ) // + 1Hs
    Local cService       := 'wsfe' // The WS service name you are asking a TA for ( "wdepmovimientos", "wsfe" )
    Local cXML               := ''  // Variable donde Armo el XML
    Local cCMS               := ''  // Variable donde esta el XML y su Firma Electronica
    Local cTRA               := ''  // Ticket de requerimiento de Acceso
    Local cCMS_Base64    := ''  // Variable donde esta el CMS Codificado en Base64
    Local cCmdSign       := ''  // Comando para llamar a OpenSsl y Generar la Firma del Archivo XML
    Local cPathOpenSsl    := 'C:\OpenSsl-Win32\Bin' // Donde Tengo Instalado OpenSsl
    Local cRespuesta      := ''

    * Armo El Archivo Xml con el mensaje del TRA (LoginTicketRequest.xml)
    cXml += '<?xml version="1.0" encoding="UTF-8"?>' + CRLF
    cXml += '<loginTicketRequest version="1.0">' + CRLF
    cXml += '  <header>'  + CRLF
    cXml += '    <source>' + cSource + '</source>' + CRLF
    cXml += '    <destination>' + cDestinationDn + '</destination>' + CRLF
    cXml += '    <uniqueId>' + cUniqueId + '</uniqueId>' + CRLF
    cXml += '    <generationTime>' + cGenerationTime + '</generationTime>' + CRLF
    cXml += '    <expirationTime>' + cExpirationTime + '</expirationTime>' + CRLF
    cXml += '  </header>' + CRLF
    cXml += '  <service>' + cService + '</service>' + CRLF
    cXml += '</loginTicketRequest>' + CRLF
    * Grabo el Archivo XML con el Nombre TRA.xml
    If( ( nHandle := fcreate( 'TRA.xml', 0 ) ) == -1 )
        MsgStop( 'NO se pudo crear TRA.xml' )
        Return( .f. )
    Else
    fWrite( nHandle, cXml )
    fClose( nHandle )
    End

    * Genero la Firma Electronica de TRA.xml en el Archivo TRA.tmp 
    * Creo un Archivo BAT, con los comandos para llamar a OpenSsl y lo ejecuto.
    * Preferiria Hacer Lo Siguiente Usando Funciones De Harbour. 
    * SI Bien Harbour Tiene La Libreria Hbssl Que Creo Que Sirve Para Firmar El 
    * Archivo Como Lo Pide La Afip, No Tengo Conocimientos De Encriptacion Y No
    * Se Cual De Sus Funciones Usar.
    * Ademas, Tube Muchos Problemas Para Crear La Libreria Y No Quedo Funcional.
    * Por Lo Tanto, Si OpenSsl Funciona, No Pierdo Mas Tiempo Y Lo Uso.
    * SI Alguien Se Anima A Cambiarlo Por Funciones Propias De Harbour, Bienvenido
    * Al Barco :-)
    * Otro Pedido, Usando Otros Lenguajes, Vi Que La Salida De Openssl, Se Puede
    * Enviar A Pantalla Y De Ahi Tomar Esa Respuesta En Una Variable.
    * DE Poder Hacerse Eso En Harbour, SE Evita La Grabacion Y Posterior Lectura
    * Del Archivo Tra.tmp

    cCmdSign := 'openssl smime' +;
                    ' -sign' +;
                    ' -in ' + 'TRA.xml' +;       // Archivo XML a Firmar
                    ' -out ' + 'TRA.tmp' +;      // Archivo con la Firma
                    ' -signer ' + cCert +;       // 
                    ' -inkey ' + cPrivateKey + ; // 
                    ' -outform ' + 'DER'  +;     // Lo Graba en Binario ('PEM' lo graba como numeros con 4 lineas de titulos)
                    ' -nodetach'                 // NO se Incluye el Archivo Original en la Salida
    cBat := ''
    cBat += 'path ' + cPathOpenSsl + CRLF
    cBat += cCmdSign   + CRLF
    MemoWrit( 'FirmoXML.bat', cBat )

    * Ejecuto el BAT para Generar TRA.tmp con la Firma 
    HB_run( 'FirmoXML.bat' )      // HB_run() -> Funci¢n de Harbour para llamar progrmas externos
    cCMS := MemoRead( 'TRA.tmp' ) // Leo el Archivo Firmado 

    * Codifico en base64
    cCMS_Base64 := hb_base64Encode( cCMS ) // Funci¢n de Harbour

    * Llamo al WS de Autenticaci¢n (WSAA)
    cRespuesta := LlamoAlWSAA( cCMS_Base64 )
    MemoWrit( 'TA.xml', cRespuesta )
    MsgStop( 'FINALIZO EL PEDIDO DEL TICKET DE ACCESO;EL PEDIDO SE GRABO COMO: TRA.xml;LA RESPUESTA RECIBIDA SE GRABO COMO: TA.xml' )
    * Muestro el contenido del XML RECIBIDO
    aRespuesta := Opc_LeeRespuesta( cRespuesta, 'TA' )
    MuestroRespuesta( aRespuesta )
    Return( .t. )

//----------------------------------------------------------------------------//
*: Opc_FECompUltimoAutorizado   
*: Consulto el Nro. de la ULTIMA FACTURA AUTORIZADA 
*: Estado: OK (funcionando) 
//----------------------------------------------------------------------------//
Function Opc_FECompUltimoAutorizado( c_Token, c_Sign, c_CUIT )

    Local nIdx
    Local nHandle
    Local cRespuesta
    Local aRespuesta
    Local cXml  := ''
    Local cPtoVta  := Alltrim( Str( 0001 ) ) // Punto de Venta
    Local cCbteTipo := Alltrim( Str( 001 ) ) // 1=FACTURA

    If( Empty( cToken ) .or. Empty( cSign ) )
        MsgStop( 'Antes de usar esta opci¢n;Pida un Nuevo TICKET DE ACCESO' )
        Return( .f. )
    End

    * Armo El Archivo XML con el Pedido
    cXml += '<?xml version="1.0" encoding="UTF-8"?>'   + CRLF
    cXml += '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' + CRLF
    cXml += '<soap:Body> '      + CRLF
    cXml += '   <FECompUltimoAutorizado xmlns="http://ar.gov.afip.dif.FEV1/">' + CRLF
    cXml += '      <Auth>'      + CRLF
    cXml += '         <Token>' + c_Token + '</Token>'   + CRLF
    cXml += '         <Sign>' + c_Sign + '</Sign>'   + CRLF
    cXml += '         <Cuit>' + c_Cuit + '</Cuit>'   + CRLF
    cXml += '      </Auth>'      + CRLF
    cXml += '      <PtoVta>' + cPtoVta + '</PtoVta>'   + CRLF
    cXml += '      <CbteTipo>' + cCbteTipo + '</CbteTipo>'  + CRLF
    cXml += '   </FECompUltimoAutorizado>'    + CRLF
    cXml += '</soap:Body>'      + CRLF
    cXml += '</soap:Envelope>'      + CRLF
    
    * Esto NO hace falta, pero lo hago para controlar como se formo el XML
    If( ( nHandle := fCreate( 'FECompUltimoAutorizado_Req.xml', 0 ) ) == -1 )
        MsgStop( 'NO se pudo crear FECompUltimoAutorizado_Req.xml' )
        Return( .f. )
    Else
        fWrite( nHandle, cXml )
        fClose( nHandle )
    End

    * Llamo al WS
    cRespuesta := LlamoAlWSFE( cXml, 'FECompUltimoAutorizado' )
    MemoWrit( 'FECompUltimoAutorizado_Res.xml', cRespuesta )
    MsgStop( 'FINALIZO la CONSULTA DEL ULTIMO;NUMERO DE FACTURA AUTORIZADO;EL PEDIDO SE GRABO COMO: FECompUltimoAutorizado_Req; LA RESPUESTA RECIBIDA SE GRABO COMO: FECompUltimoAutorizado_Res' )
    
    * Muestro el contenido del XML RECIBIDO
    aRespuesta := Opc_LeeRespuesta( cRespuesta, 'FECompUltimoAutorizado' )
    MuestroRespuesta( aRespuesta )

    * Actualizo el Numero del Ultimo Comprobante Autorizado
    nIdx := AScan( aRespuesta, { |aAux| aAux[ 1 ] = 'CbteNro' } )
    If( nIdx <> 0 )
        nUltCmpAut := val( aRespuesta[ nIdx, 2 ] )
    End
    Return( .t. )

//----------------------------------------------------------------------------//
*: Opc_FECAESolicitar Pido la CAE para una FACTURA       :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function Opc_FECAESolicitar( c_Token, c_Sign, c_CUIT )

    Local cXml  := ''
    Local nhandle
    Local cRespuesta
    Local aRespuesta
    * Estas Variables tienen que Salir del Sistema de Gesti¢n.
    * Aca las pongo como valores fijos para probar el WS.
    Local cCantReg      := Str( 0001 )   // 
    Local cPtoVta       := Alltrim( Str( 0001 ) ) // Punto de Venta
    Local cCbteTipo     := Alltrim( Str( 001 ) ) // 1=FACTURA
    Local cConcepto     := Alltrim( Str( 01 ) )  // 1=
    Local cDocTipo      := Alltrim( Str( 80 ) )  // 80=CUIT
    Local cDocNro       := '30651704657'                // CUIT del Comprador
    Local cCbteDesde    := Alltrim( Str( 4794 + 1 ) ) // Nro de la 1er.  Factura a Autorizar
    Local cCbteHasta    := Alltrim( Str( 4794 + 1 ) ) // Nro de la Ultima Factura a Autorizar
    Local cCbteFch      := DToS( date() )                 // Fecha de la Factura
    Local cImpTotal     := Alltrim( Str( 14.93 ) )     // Importe Total de la Factura
    Local cImpTotConc   := Alltrim( Str( 0 ) )       // Neto NO Gravado
    Local cImpNeto      := Alltrim( Str( 12.34 ) )      // Importe Neto (Sin Iva)
    Local cImpOpEx      := Alltrim( Str( 0 ) )          // Importe Exento
    Local cImpTrib      := Alltrim( Str( 0 ) )          // Suma de los importes del array de tributos
    Local cImpIVA       := Alltrim( Str( 2.59 ) )         // 
    Local cFchServDesde := ''                      // Cuando cConcepto = 2 o 3
    Local cFchServHasta := ''                      // Cuando cConcepto = 2 o 3
    Local cFchVtoPago   := ''                        // Cuando cConcepto = 2 o 3
    Local cMonId        := 'PES'                         // C¢digo de Moneda (sale de consultar el WS Respectivo)
    Local cMonCotiz     := Alltrim( Str( 1 ) )         // Cotizaci¢n de la Moneda. Para cMonId='PES' es 1.
    * Es un array que se repite tantas veces como tasas de iva haya en la factura
    Local cId      := Alltrim( Str( 5 ) )  // 21% de IVA
    Local cBaseImp := Alltrim( Str( 12.34 ) ) // Base Imponible
    Local cImporte := Alltrim( Str( 2.59 ) ) // Importe de Iva
    
    If( empty( cToken )  .or.  empty( cSign ) )
        MsgStop( 'Antes de usar esta opci¢n;Pida un Nuevo TICKET DE ACCESO' )
        Return( .f. )
    End
    * ARMO EL ARCHIVO XML con el Pedido de CAE
    cXml += '<?xml version="1.0" encoding="UTF-8"?>'   + CRLF
    cXml += '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' + CRLF
    cXml += '<soap:Header/>'      + CRLF
    cXml += '<soap:Body>'      + CRLF
    cXml += '   <FECAESolicitar xmlns="http://ar.gov.afip.dif.FEV1/">' + CRLF
    cXml += '      <Auth>'      + CRLF
    cXml += '         <Token>' + c_Token + '</Token>'   + CRLF
    cXml += '         <Sign>' + c_Sign + '</Sign>'   + CRLF
    cXml += '         <Cuit>' + c_Cuit + '</Cuit>'   + CRLF
    cXml += '      </Auth>'      + CRLF
    cXml += '      <FeCAEReq>'      + CRLF
    cXml += '         <FeCabReq>'     + CRLF
    cXml += '            <CantReg>' + cCantReg + '</CantReg>'  + CRLF
    cXml += '            <PtoVta>' + cPtoVta + '</PtoVta>'  + CRLF
    cXml += '            <CbteTipo>' + cCbteTipo + '</CbteTipo>'  + CRLF
    cXml += '         </FeCabReq>'     + CRLF
    cXml += '         <FeDetReq>'     + CRLF
    cXml += '            <FECAEDetRequest>'    + CRLF
    cXml += '               <Concepto>' + cConcepto + '</Concepto>' + CRLF
    cXml += '               <DocTipo>' + cDocTipo + '</DocTipo>'  + CRLF
    cXml += '               <DocNro>' + cDocNro + '</DocNro>'  + CRLF
    cXml += '               <CbteDesde>' + cCbteDesde + '</CbteDesde>' + CRLF
    cXml += '               <CbteHasta>' + cCbteHasta + '</CbteHasta>' + CRLF
    cXml += '               <CbteFch>' + cCbteFch + '</CbteFch>'  + CRLF
    cXml += '               <ImpTotal>' + cImpTotal + '</ImpTotal>' + CRLF
    cXml += '               <ImpTotConc>' + cImpTotConc + '</ImpTotConc>'+ CRLF
    cXml += '               <ImpNeto>' + cImpNeto + '</ImpNeto>'  + CRLF
    cXml += '               <ImpOpEx>' + cImpOpEx + '</ImpOpEx>'  + CRLF
    cXml += '               <ImpTrib>' + cImpTrib + '</ImpTrib>'  + CRLF
    cXml += '               <ImpIVA>' + cImpIVA + '</ImpIVA>'  + CRLF
    cXml += '               <FchServDesde>' + cFchServDesde + '</FchServDesde>' + CRLF // Cuando cConcepto = 2 o 3
    cXml += '               <FchServHasta>' + cFchServHasta + '</FchServHasta>' + CRLF // Cuando cConcepto = 2 o 3
    cXml += '               <FchVtoPago>' + cFchVtoPago + '</FchVtoPago>' + CRLF // Cuando cConcepto = 2 o 3
    cXml += '               <MonId>' + cMonId + '</MonId>'  + CRLF
    cXml += '               <MonCotiz>' + cMonCotiz + '</MonCotiz>' + CRLF
    cXml += '               <Iva>'     + CRLF
    cXml += '                  <AlicIva>'    + CRLF
    cXml += '                     <Id>' + cId + '</Id>'   + CRLF
    cXml += '                  <BaseImp>' + cBaseImp + '</BaseImp>' + CRLF
    cXml += '                     <Importe>' + cImporte + '</Importe>' + CRLF
    cXml += '                  </AlicIva>'    + CRLF
    cXml += '               </Iva>'     + CRLF
    cXml += '            </FECAEDetRequest>'    + CRLF
    cXml += '         </FeDetReq>'     + CRLF
    cXml += '      </FeCAEReq>'      + CRLF
    cXml += '   </FECAESolicitar>'     + CRLF
    cXml += '</soap:Body>'      + CRLF
    cXml += '</soap:Envelope>'      + CRLF

    If( ( nHandle := fCreate( 'FECAESolicitar_Req.xml', 0 ) ) == -1 )
        MsgStop( 'NO se pudo crear FECAESolicitar_Req.xml' )
        Return( .f. )
    Else
        fWrite( nHandle, cXml )
        fClose( nHandle )
    End

    * Llamo al WS
    cRespuesta := LlamoAlWSFE( cXml, 'FECAESolicitar' )
    MemoWrit( 'FECAESolicitar_Res.xml', cRespuesta )
    MsgStop( 'FINALIZO EL PEDIDO DE CAE;EL PEDIDO SE GRABO COMO: FECAESolicitar_Req; LA RESPUESTA RECIBIDA SE GRABO COMO: FECAESolicitar_Res' )

    * Muestro el contenido del XML RECIBIDO
    aRespuesta := Opc_LeeRespuesta( cRespuesta, 'FECAESolicitar' )
    MuestroRespuesta( aRespuesta )
    Return( .t. )

//----------------------------------------------------------------------------//
*: Opc_LeeRespuesta Toma el Archivo XML devuelto por la AFIP y muestra  :
*:   su contenido.         :
*:                          :
*: Estado: FALTA MEJORARLA MUCHOOOOOO         :
//----------------------------------------------------------------------------//
Function Opc_LeeRespuesta( c_XmlRecibido, c_ServicioLlamado )

    Local oXMLDoc
    Local oXMLNode
    Local cXml  := c_XmlRecibido
    Local aRespuesta := {}
    Local cText  := ''
    Local cName  := ''
    Local cData  := ''
    Local aText  := {}
    Local nIdx  := 0

    * Proceso el XML (TXMLDocument sale de la contrib xHb)
    oXMLDoc  := TXMLDocument() : New()
    cText    := oXMLDoc : Read( cXML )
    oXMLNode := oXMLDoc : FindFirst()
    While( Valtype( oXMLNode ) == 'O' )
        cName := oXMLNode:cName // Nombre del Nodo
        cData := oXMLNode:cData // Datos dentro del Nodo
        If( valtype( cData ) = 'N' )
            cData := Str( cData )
        ElseIf( valtype( cData ) = 'D' )
            cData := DTOC( cData )
        ElseIf( valtype( cData ) = 'C' )
            cData := Alltrim( cData )
            If( subs( cData, 5, 1 ) = '-' .And. Subs( cData, 8, 1 ) = '-' )
            If( len( cData ) = 10 )
                    cData := SubStr( cData, 9, 2 ) + '/' + SubStr( cData, 6, 2 ) + '/' + SubStr( cData, 1, 4 )
            Else
                    cData := SubStr( cData, 9, 2 ) + '/' + SubStr( cData, 6, 2 ) + '/' + SubStr( cData, 1, 4 ) + ' - ' + SubStr( cData, 11 )
            End
            End
        Else
            cData := ''
        End 
        If( valtype( cName ) = 'C' )
            Aadd( aText , { Alltrim( cName ), Alltrim( cData ) } )
        End
        oXmlNode := oXMLDoc : FindNext()
    Enddo

    * Proceso los Valores Obtenidos, Esta Parte Da Lastima, Pero Estaba Apurado Asi Que No Tuve Tiempo De Mejorarla.
    For nIdx := 1 to Len( aText )
        If( c_ServicioLlamado = 'FEDummy' )
            if( aText[ nIdx, 1 ] == 'AppServer' .or.  ;
                 aText[ nIdx, 1 ] == 'DbServer' .or.  ;
                 aText[ nIdx, 1 ] == 'AuthServer' .or.  ;
                 aText[ nIdx, 1 ] == 'Code'  .or.  ;
                 aText[ nIdx, 1 ] == 'Msg' )
            Aadd( aRespuesta, aText[ nIdx ] )
            End
        ElseIf( c_ServicioLlamado = 'FECompUltimoAutorizado' )
            If( aText[ nIdx, 1 ] == 'CbteNro' .or.  ;
                 aText[ nIdx, 1 ] == 'Code'  .or.  ;
                 aText[ nIdx, 1 ] == 'Msg' )
            Aadd( aRespuesta, aText[ nIdx ] )
            End
        ElseIf( c_ServicioLlamado = 'TA' )
            If( aText[ nIdx, 1 ] == 'generationTime' .or.  ;
                 aText[ nIdx, 1 ] == 'expirationTime' .or.  ;
                 aText[ nIdx, 1 ] == 'token'  .or.  ;
                 aText[ nIdx, 1 ] == 'sign' )
            Aadd( aRespuesta, aText[ nIdx ] )
            End
        ElseIf( c_ServicioLlamado = 'FECAESolicitar' )
            If( aText[ nIdx, 1 ] == 'Resultado' .or.  ;
                 aText[ nIdx, 1 ] == 'Reproceso' .or.  ;
                 aText[ nIdx, 1 ] == 'CAE'  .or.  ;
                 aText[ nIdx, 1 ] == 'CAEFchVto' .or.  ;
                 aText[ nIdx, 1 ] == 'Code'  .or.  ;
                 aText[ nIdx, 1 ] == 'Msg' )
            Aadd( aRespuesta, aText[ nIdx ] )
            End
        End
    Next
    Return( aRespuesta )

//----------------------------------------------------------------------------//
*: TimeFMT Devuelve la fecha y hora recibida en forma de texto
//----------------------------------------------------------------------------//
Static Function TimeFMT( dt, n_Horas )

    Local c          := ''
    Local cFechaHora := ''

    dt := dt + ( n_Horas / 24 )
    c := hb_TToS( dt )
    cFechaHora := SubStr( c, 01, 4 ) + '-' + SubStr( c, 05, 2 ) + '-' + SubStr( c, 07, 2 ) +  ;
                 'T' +  ;
                      SubStr( c, 09, 2 ) + ':' + SubStr( c, 11, 2 ) + ':' + SubStr( c, 13, 2 )  // +  '-' + '03:00'
    Return( cFechaHora )

//----------------------------------------------------------------------------//
*: MuestroRespuesta           :
*: Muestra informaci¢n recibida del WS correspondiente       :
*: Estado: OK (funcionando)          :
//----------------------------------------------------------------------------//
Function MuestroRespuesta( a_Respuesta )

    Local nIdx := 0

    Cls
    If( !Empty( a_Respuesta ) )
        ? 'COMIENZA LA VISUALIZACION DE LA RESPUESTA'
        ?
        For nIdx := 1 to Len( a_Respuesta )
            ? a_Respuesta[ nIdx, 01 ], a_Respuesta[ nIdx, 02 ]
        Next
        ?
        ? 'FINALIZO LA VISUALIZACION DE LA RESPUESTA'
        ? 'PRESIONE CUALQUIER TECLA PARA CONTINUAR'
    Else
        ? '****************************************************'
        ? ' HUBO ALGUN TIPO DE ERROR, NO HAY NADA PARA MOSTRAR '
        ? '****************************************************'
    End
    //Inkey( 0, 128 ) // 128 para que si muevo el mouse no me saque del inkey
    Return( .t. )


Saludos
Ariel
Posts: 309
Joined: Wed Nov 29, 2006 1:51 pm
Location: Rosario - Argentina

Re: Fuentes Factura electrónica Argentina

Post by Ariel »

Horacio,

Gracias x compartirlo.

Salu2, Ariel
acartucho
Posts: 58
Joined: Thu Nov 02, 2006 1:17 pm

Re: Fuentes Factura electrónica Argentina

Post by acartucho »

Hola Horacio, gracias por el aporte. Una pregunta donde consigo los siguientes archivos:

Local cCert := 'c:\pyrece\reingart\reingart.crt' // The X.509 obtained from Seg. Inf.
Local cPrivateKey := 'c:\pyrece\reingart\reingart.key' // The private key correspoding to CERT
User avatar
cmsoft
Posts: 653
Joined: Wed Nov 16, 2005 9:14 pm
Location: Mercedes - Bs As. Argentina

Re: Fuentes Factura electrónica Argentina

Post by cmsoft »

Esos archivos los tenes que generar vos desde la pagina de AFIP
http://www.afip.gob.ar/ws/WSAA/WSAA.Obt ... ficado.pdf
postinelli
Posts: 128
Joined: Tue Jul 15, 2008 7:12 pm
Location: Argentina

Re: Fuentes Factura electrónica Argentina

Post by postinelli »

son el certificado electronico que te da la afip y su clave privada
luego se procesan con el OpenSSl
horacio
Posts: 1270
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Re: Fuentes Factura electrónica Argentina

Post by horacio »

Colegas, si necesitan los archivos 'reingart.crt' y 'reingart.key' manden sus correos. Gracias

Saludos
luisduque
Posts: 133
Joined: Mon May 12, 2008 4:13 pm
Location: Venezuela
Contact:

Re: Fuentes Factura electrónica Argentina

Post by luisduque »

Horacio, excelente aporte, gracias

Lo importante es como dices analizar como es el comportamiento del WebService
Fivewin 16.11
Harbour 3.2.0
Visual Studio 2015 community
MariaDb/MySql

Ing. MSc. Luis Duque
http://www.accasoft.net
lexyn
Posts: 3
Joined: Tue Mar 24, 2020 4:54 pm

Re: Fuentes Factura electrónica Argentina

Post by lexyn »

Hola estimado Horacio
Estoy interesado en el código para factura electrónica y los archivos que ofreces.
Pregunto si el código que esta publicado, todavía esta vigente y que version de fwh y harbour necesita para correr correctamente.
Desde ya muchas gracias.

Mi correo es olexyn@gmail.com

// Colegas, si necesitan los archivos 'reingart.crt' y 'reingart.key' manden sus correos. Gracias


Leonardo
horacio
Posts: 1270
Joined: Wed Jun 21, 2006 12:39 am
Location: Capital Federal Argentina

Re: Fuentes Factura electrónica Argentina

Post by horacio »

Hola Leonardo, Yo empecé a usar este código desde la versión FWH1204, la única restricción que encontré en ese momento es que solo funcionaba con con harbour, no xharbour. Actualmente lo estoy compilando con la última versión FWH. También lo podés usar solo consola ( de hecho estaba para consola y yo lo adapté ), Si tenés alguna duda te dejo mi correo horacio.dellacasa arroba gmail.com

Saludos
Post Reply