Fuentes Factura electrónica Argentina
Posted: Sun Jun 28, 2015 11:28 pm
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.
Saludos
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( ''' $ cRespuesta ) // \
cRespuesta := StrTran( cRespuesta, ''', '\' )
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( ''' $ cRespuesta ) // \
cRespuesta := StrTran( cRespuesta, ''', '\' )
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