Veri*Factu

Veri*Factu

Postby Antonio Linares » Sat Dec 14, 2024 5:27 am

Code: Select all  Expand view  RUN
FUNCTION Main()
    LOCAL cXmlFile := "factura.xml"
    LOCAL cSignedXmlFile := "factura_firmada.xml"
    LOCAL cCertFile := "certificado.pfx"
    LOCAL cCertPass := "1234"
    LOCAL cUrl := "https://verifactu.aeat.es/ws"

    // Generar el XML
    GenerarFacturaXML(cXmlFile)

    // Firmar el XML
    FirmarXML(cXmlFile, cCertFile, cCertPass, cSignedXmlFile)

    // Enviar la factura al WebService
    EnviarFactura(cUrl, cSignedXmlFile)

RETURN NIL
 
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby Antonio Linares » Sat Dec 14, 2024 5:29 am

Code: Select all  Expand view  RUN
#include "hbxml.ch"

FUNCTION GenerarFacturaXML(cFileName)
    LOCAL oXml, oFactura, oCabecera, oDatosFactura, oIDFactura

    // Crear el objeto XML raíz
    oXml := HB_XMLDOCUMENT():New()

    // Nodo principal <Factura>
    oFactura := HB_XMLNODE():New("Factura")
    oXml:AppendChild(oFactura)

    // Nodo <Cabecera>
    oCabecera := HB_XMLNODE():New("Cabecera")
    oCabecera:AppendChild(HB_XMLNODE():New("Version", "1.0"))
    oCabecera:AppendChild(HB_XMLNODE():New("TipoComunicacion", "Alta"))
    oFactura:AppendChild(oCabecera)

    // Nodo <DatosFactura>
    oDatosFactura := HB_XMLNODE():New("DatosFactura")
    oIDFactura := HB_XMLNODE():New("IDFactura")
    oIDFactura:AppendChild(HB_XMLNODE():New("NumSerieFacturaEmisor", "2024-0001"))
    oIDFactura:AppendChild(HB_XMLNODE():New("FechaExpedicionFacturaEmisor", "2024-12-14"))
    oDatosFactura:AppendChild(oIDFactura)
    oDatosFactura:AppendChild(HB_XMLNODE():New("ImporteTotal", "100.00"))
    oFactura:AppendChild(oDatosFactura)

    // Guardar el XML en un archivo
    oXml:SaveToFile(cFileName)
    MsgInfo("XML generado: " + cFileName)

RETURN NIL
 
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby Antonio Linares » Sat Dec 14, 2024 5:30 am

Code: Select all  Expand view  RUN
FUNCTION FirmarXML(cXmlFile, cCertFile, cCertPass, cSignedXmlFile)
    LOCAL cCommand

    // Comando OpenSSL para firmar el XML
    cCommand := "openssl smime -sign -in " + cXmlFile + " -out " + cSignedXmlFile + " -signer " + cCertFile + " -passin pass:" + cCertPass + " -outform DER"

    // Ejecutar el comando
    IF HB_RUN(cCommand) == 0
        MsgInfo("XML firmado correctamente: " + cSignedXmlFile)
    ELSE
        MsgStop("Error al firmar el XML.")
    ENDIF

RETURN NIL
 
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby Antonio Linares » Sat Dec 14, 2024 5:31 am

Code: Select all  Expand view  RUN
#include "hbcurl.ch"

FUNCTION EnviarFactura(cUrl, cSignedXmlFile)
    LOCAL oCurl, cSoapRequest, cSoapResponse, cFacturaXml

    // Leer el contenido del XML firmado
    cFacturaXml := MemoRead(cSignedXmlFile)

    // Crear la solicitud SOAP
    cSoapRequest := ;
        '<?xml version="1.0" encoding="UTF-8"?>' + ;
        '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:veri="http://www.aeat.es/VeriFactu">' + ;
        '<soapenv:Header/>' + ;
        '<soapenv:Body>' + ;
        '<veri:EnviarFactura>' + ;
        '<veri:FacturaXML><![CDATA[' + cFacturaXml + ']]></veri:FacturaXML>' + ;
        '</veri:EnviarFactura>' + ;
        '</soapenv:Body>' + ;
        '</soapenv:Envelope>'

    // Inicializar hbcurl
    oCurl := hb_curlEasyInit()
    hb_curlEasySetopt(oCurl, HB_CURLOPT_URL, cUrl)
    hb_curlEasySetopt(oCurl, HB_CURLOPT_POSTFIELDS, cSoapRequest)
    hb_curlEasySetopt(oCurl, HB_CURLOPT_HTTPHEADER, {"Content-Type: text/xml", "SOAPAction: EnviarFactura"})
    hb_curlEasySetopt(oCurl, HB_CURLOPT_SSLCERT, "ruta_certificado.pem")
    hb_curlEasySetopt(oCurl, HB_CURLOPT_SSLCERTPASSWD, "password_certificado")

    // Enviar la solicitud
    cSoapResponse := hb_curlEasyPerform(oCurl)

    IF EMPTY(cSoapResponse)
        MsgStop("Error al enviar la factura.")
    ELSE
        MsgInfo("Respuesta del servidor: " + cSoapResponse)
    ENDIF

    hb_curlEasyCleanup(oCurl)

RETURN NIL
 
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby paquitohm » Sat Dec 14, 2024 9:00 am

Gracias Antonio !
Realmente no hace falta firmar el .xml.
Firmar .xml sólo es necesario para el modo No Verifactu

El modo No Verifactu es en realidad un modo "Verifactu" por el cual no se exige envio inmediato de las fras. pero si se exige guardar la informacion adecuadamente por si la requirieran
paquitohm
 
Posts: 281
Joined: Fri Jan 14, 2022 8:37 am

Re: Veri*Factu

Postby Garbi » Mon Dec 16, 2024 10:03 am

Hola Antonio.

¿Con estas funciones que has indicado podríamos tener solucionado todas las opciones de Veri*Factu o solo es una idea de como hacerlo porque no veo donde generas la huella de la factura?

¿Y para enviar la factura no hay que enviar la huella de la factura anterior?

Muchas gracias por todo tu apoyo.
Saludos,
Regards,

Jose Luis Alepuz
joseluis@mancomputer.com
www.mancomputer.com
Garbi
 
Posts: 344
Joined: Wed Nov 02, 2005 3:28 pm

Re: Veri*Factu

Postby Antonio Linares » Mon Dec 16, 2024 10:34 am

Jose Luis,

El código no está completo ni probado aún. La idea es comenzar una conversación para ir completándolo.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby Garbi » Mon Dec 16, 2024 11:16 am

Perfecto.
Aunque yo estoy con Carlos G. (un gran profesional. que me ha brindado toda su ayuda) y estoy siguiendo los pasos que me indica.

Estaría interesado en este proyecto que pudiéramos colaborar todos, y entre todos sacarlo.

He intentado hacer la primera prueba y me indica que no encuentra la funciones : HB_XMLDOCUMENT() y HB_XMLNODE()

Por supuesto he puesto el #include "hbxml.ch", no se si me faltara alguna librería.

¿Alguna sugerencia?
Saludos,
Regards,

Jose Luis Alepuz
joseluis@mancomputer.com
www.mancomputer.com
Garbi
 
Posts: 344
Joined: Wed Nov 02, 2005 3:28 pm

Re: Veri*Factu

Postby paquitohm » Mon Dec 16, 2024 11:33 am

Esto que voy a decir vale para TODO EL MUNDO porque recurrentemente en el foro se pregunta por "¿ Que libreria tengo que añadir para que no me salga el unresolved external xxxx() ?


Respuesta: Cuando sea necesario añadir una lib de harbour y no sabemos cual es entonces podemos hacer un grep o usar el notepad++ para saber en que lib aparece.
Por ejemplo:

Code: Select all  Expand view  RUN
C:\hb32bcc73\lib>GREP HB_XMLDOCUMENT *.lib
paquitohm
 
Posts: 281
Joined: Fri Jan 14, 2022 8:37 am

Re: Veri*Factu

Postby VictorCasajuana » Mon Dec 16, 2024 11:44 am

--------
¿ Y porque no ?
¿ And why not ?
User avatar
VictorCasajuana
 
Posts: 268
Joined: Wed Mar 28, 2018 4:38 pm
Location: Vinaròs

Re: Veri*Factu

Postby Garbi » Mon Dec 16, 2024 11:53 am

Lo he buscado con el programa FileSeek, que encuentra todo si esta y no ha localizado esas funciones: HB_XMLDOCUMENT() y HB_XMLNODE()
¿Alguna sugerencia?
Saludos,
Regards,

Jose Luis Alepuz
joseluis@mancomputer.com
www.mancomputer.com
Garbi
 
Posts: 344
Joined: Wed Nov 02, 2005 3:28 pm

Re: Veri*Factu

Postby Antonio Linares » Mon Dec 16, 2024 12:50 pm

Hay una clase en Harbour para generación de ficheros XML:

https://github.com/harbour/core/blob/master/contrib/hbdoc/_xml.prg

Vamos a construir la librería de esa carpeta de contribuciones y modificar el ejemplo publicado en esta conversación para que use esta clase
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby paquitohm » Mon Dec 16, 2024 3:28 pm

Garbi wrote:Lo he buscado con el programa FileSeek, que encuentra todo si esta y no ha localizado esas funciones: HB_XMLDOCUMENT() y HB_XMLNODE()
¿Alguna sugerencia?


Perdona, tenías razón, no aparece. No sé qué M. pasa

Otra manera de buscar es buscando en los fuentes de harbour poniendo esto en la barra de busqueda:
Code: Select all  Expand view  RUN
site:https://github.com/harbour/core <palabra a buscar>


Por ejemplo:
Code: Select all  Expand view  RUN
site:https://github.com/harbour/core txml
paquitohm
 
Posts: 281
Joined: Fri Jan 14, 2022 8:37 am

Re: Veri*Factu

Postby Antonio Linares » Mon Dec 16, 2024 6:11 pm

Aqui tenemos una primera versión que genera el XML correctamente. Os agradezco si lo probais y comentais:

xml.prg
Code: Select all  Expand view  RUN
#include "FiveWin.ch"

function Main()
    local oXML := TXml():New( "factura.xml" )
   
    // Start root element
    oXML:AddSection( "Factura" )
   
    // Add Cabecera section
    oXML:AddSection( "Cabecera" )
    oXML:AddElement( "Version", "1.0" )
    oXML:AddElement( "TipoComunicacion", "Alta" )
    oXML:EndSection() // Close Cabecera
   
    // Add DatosFactura section
    oXML:AddSection( "DatosFactura" )
   
    // Add IDFactura subsection
    oXML:AddSection( "IDFactura" )
    oXML:AddElement( "NumSerieFacturaEmisor", "2024-0001" )
    oXML:AddElement( "FechaExpedicionFacturaEmisor", "2024-12-14" )
    oXML:EndSection() // Close IDFactura
   
    oXML:AddElement( "ImporteTotal", "100.00" )
    oXML:EndSection() // Close DatosFactura
   
    oXML:EndSection() // Close Factura
   
    oXML:Save()
RETURN NIL

CLASS TXml
    DATA cFileName
    DATA aElements INIT {}
    DATA aOpenElements INIT {}

    METHOD New( cFilename )
    METHOD AddElement( cName, xValue )
    METHOD AddSection( cName )
    METHOD EndSection()
    METHOD Save()
    METHOD GenerateXmlContent()
ENDCLASS

METHOD New( cFilename ) CLASS TXml
    ::cFileName := cFilename
    IF !(".xml" $ ::cFileName)
        ::cFileName += ".xml"
    ENDIF
RETURN Self

METHOD AddElement( cName, xValue ) CLASS TXml
    local nLevel, cIndent, oElement

    IF xValue == NIL
        RETURN ::AddSection(cName)
    ENDIF

    nLevel := Len(::aOpenElements)
    cIndent := Space(nLevel * 4)
    oElement := { ;
        "name" => cName, ;
        "value" => xValue, ;
        "level" => nLevel, ;
        "indent" => cIndent ;
    }
   
    AAdd(::aElements, oElement)
RETURN Self

METHOD AddSection( cName ) CLASS TXml
    LOCAL nLevel := Len(::aOpenElements)
    LOCAL cIndent := Space(nLevel * 4)
    LOCAL oElement := { ;
        "name" => cName, ;
        "value" => NIL, ;
        "level" => nLevel, ;
        "indent" => cIndent ;
    }
   
    AAdd(::aOpenElements, oElement)
    AAdd(::aElements, oElement)
RETURN Self

METHOD EndSection() CLASS TXml
    local oElement, cIndent
    IF !Empty(::aOpenElements)
        oElement := ATail(::aOpenElements)
        cIndent := Space(oElement["level"] * 4)
       
        AAdd(::aElements, { ;
            "name" => oElement["name"], ;
            "value" => NIL, ;
            "level" => oElement["level"], ;
            "indent" => cIndent, ;
            "closing" => .T. ;
        })
       
        ASize(::aOpenElements, Len(::aOpenElements) - 1)
    ENDIF
RETURN Self

METHOD Save() CLASS TXml
    LOCAL cXmlContent := ::GenerateXmlContent()
    // LOCAL cFullXmlContent := '<?xml version="1.0" encoding="UTF-8"?>' + hb_eol() + cXmlContent
    fw_memoEdit( cXmlContent )
RETURN hb_memoWrit( ::cFileName, cXmlContent )

METHOD GenerateXmlContent() CLASS TXml
    LOCAL cXml := ""
    LOCAL oElement
   
    FOR EACH oElement IN ::aElements
        IF oElement["value"] == NIL
            IF hb_HGetDef(oElement, "closing", .F.)
                cXml += oElement["indent"] + "</" + oElement["name"] + ">" + hb_eol()
            ELSE
                cXml += oElement["indent"] + "<" + oElement["name"] + ">" + hb_eol()
            ENDIF
        ELSE
            cXml += oElement["indent"] + "<" + oElement["name"] + ">" + ;
                   hb_ValToStr(oElement["value"]) + ;
                   "</" + oElement["name"] + ">" + hb_eol()
        ENDIF
    NEXT
RETURN cXml

Code: Select all  Expand view  RUN
<Factura>
    <Cabecera>
        <Version>1.0</Version>
        <TipoComunicacion>Alta</TipoComunicacion>
    </Cabecera>
    <DatosFactura>
        <IDFactura>
            <NumSerieFacturaEmisor>2024-0001</NumSerieFacturaEmisor>
            <FechaExpedicionFacturaEmisor>2024-12-14</FechaExpedicionFacturaEmisor>
        </IDFactura>
        <ImporteTotal>100.00</ImporteTotal>
    </DatosFactura>
</Factura>
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42203
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: Veri*Factu

Postby FiveWiDi » Mon Dec 16, 2024 7:58 pm

Hola a todos,

Ya se tienen algunas piezas del puzzle.

Creo que falta una, ¿Cómo leen Ustedes un XML?

Yo tengo una función pero creo que no es una solución muy ortodoxa, aunque funciona muy bien. El único requisito es saber a priori que Tag son iterativos.

Ya que usar la clase para crear un XML me costó entenderla, pensé que para leerlo me costaría también, por lo que opté por lo que me es fácil, y cuando quiero leer un XML paso su contenido a una array.

Me creé una función LoadXML() que devuelve una Array de arrays tridimensionales, cada array de esta array tiene:
-una cadena del path del tag completo
-el valor que tiene ese tag
-una array de arrays bidimensionales con el atributo y su valor (si tiene atributos)

Defino previamente aTagIter (son los Tags iterativos) que és una array de array bidimensionales, para Verifactu:

Code: Select all  Expand view  RUN
Local aTagIter  := { { "/env:Envelope/env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/", 0 }, ;
                     { "/env:Envelope/env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/tikR:CodigoErrorRegistro/", 0 }, ;
                     { "/env:Envelope/env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/tikR:DescripcionErrorRegistro/", 0 } ;
                   }




Así es como leo la respuesta a los envíos que hago a Verifactu:
Code: Select all  Expand view  RUN
//----------------------------------------------------------------------------/
FUNCTION LoadXml( hFile, cBDPathFull, aTagIter )

Local oXmlDoc, oXmlIter
Local lAbreaqui := .F.
//Local oTagActual, oTagLast, aRoots := {}
Local oTagActual, oTagLast := {}

Local litetagA  := ""
Local litetagB  := ""
Local nContador := 0
Local aXml      := {}
Local cDummy    := ""

/* Tag iteratius i sumatori de iteracions trovades.
   L'estructura de l'array es:
   - 1er. element el tag que pot ser iteratiu
   - 2on. element el número de iteració del tag.
*/


/*
Local aTagIter  := { {"/Document/CstmrDrctDbtInitn/PmtInf/", 0}, ;
                     {"/Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf/", 0}, ;
                     {"/Document/CstmrDrctDbtInitn/PmtInf/DrctDbtTxInf/Dbtr/PstlAdr/AdrLine/", 0} }
*/


//Msgnowait( AMPAarra, GetTrad("Atenció!"), GetTrad("Llegint el fitxer: ") + cBDPathFull )


// Cal ordenar la array per a que es puguin controlar correctament les iteracions.
aTagIter := Asort( aTagIter,,, { |x, y| x[1] < y[1] } )

If hFile = 0
    hFile    = FOpen( cBDPathFull )
    lAbreaqui := .T.
EndIf

oXmlDoc  = TXmlDocument():New( hFile )
oXmlIter = TXmlIterator():New( oXmlDoc:oRoot )

oTagActual := oTagLast := Nil
   
litetagA := "/"
   
while ( oTagActual := oXmlIter:Next() ) != nil

    //Traza( 1, "oTagActual:Depth()-oTagActual:cName=", oTagActual:Depth(), "-", oTagActual:cName )

    If oTagLast != nil
     
        if oTagLast:Depth() < oTagActual:Depth()
            litetagA := litetagA + oTagActual:cName + "/"
        endif

        if oTagLast:Depth() > oTagActual:Depth()
            //Traza( 1, "litetagA=", litetagA )
            litetagB := "/"
            For nContador := 1 To ( oTagActual:Depth() - 1 )
                litetagB := litetagB + StrToken( litetagA, nContador, "/" ) + "/"
            EndFor
            litetagA := litetagB
            If Empty(oTagActual:cName)
                litetagA := litetagA + "/"
              Else
                litetagA := litetagA + oTagActual:cName + "/"
            EndIf
            //litetagA := CheckItera( litetagA, aTagIter )
        endif

        if oTagLast:Depth() == oTagActual:Depth()
            //Traza( 1, "litetagA=", litetagA )
            litetagB := "/"
            For nContador := 1 To ( oTagActual:Depth() - 1 )
                litetagB := litetagB + StrToken( litetagA, nContador, "/" ) + "/"
            EndFor
            litetagA := litetagB
            If Empty(oTagActual:cName)
                litetagA := litetagA + "/"
              Else
                litetagA := litetagA + oTagActual:cName + "/"
            EndIf
            //litetagA := CheckItera( litetagA, aTagIter )
        endif
       
        litetagA := CheckItera( litetagA, aTagIter )

      else
     
        If .Not. Empty(oTagActual:cName)
            litetagA := litetagA + oTagActual:cName + "/"
        EndIf
       
        //Traza( 1, "litetagA=", litetagA )
    endif
   
    //litetagA := CheckItera( litetagA, aTagIter )

    // Aquí es captura el TAG, el seu valor y els atributs si en té.
    // exemple: { "/Document/CstmrDrctDbtInitn/PmtInf<1>/DrctDbtTxInf<2>/Dbtr/PstlAdr/AdrLine<1>/", "BARCINO, 29 P-D - SELVA NEGRA", {} }
    //          { "/Document/CstmrDrctDbtInitn/PmtInf<1>/DrctDbtTxInf<2>/Dbtr/PstlAdr/AdrLine<2>/", "08750 Vallirana", {} }

    // Es captura el TAG si té data o si hi han atributs del TAG.
    If .Not. Empty( oTagActual:cData ) .or. Len( oTagActual:aAttributes ) > 0
        AADD( aXml, { litetagA, oTagActual:cData, {} } )
   
        HEval( oTagActual:aAttributes,;
               { | cKey, cData | AADD( ATail( aXml )[3], { cKey, cData } ) } )
    EndIf

    /* En aquesta traza s'enregistren els TAG i els seus valors.
       ------------------------------------------------------ */

    /*
    cDummy := ""
    HEval( oTagActual:aAttributes,;
           { | cKey, cData | cDummy := cDummy + cKey + ":" + cData + "-" } )

    If .Not. Empty( oTagActual:cData ) .or. Len( oTagActual:aAttributes ) > 0
        Traza( 1, "litetagA=", litetagA, "..", oTagActual:cData, "..", cDummy, "..", Len( ATail( aXml )[3]) )
    EndIf
    */

           
    //Traza( 1, "" )
     
    oTagLast = oTagActual
end

If lAbreaqui
    FClose( hFile )
EndIf

//EndMsgNoWait( AMPAarra )

// Podem ordenar l'array... però crec que no serà necessari.
//aXml := Asort( aXml,,, { |x, y| x[1] < y[1] } )

//Traza( 1 , aXml )

Return aXml
//----------------------------------------------------------------------------//
FUNCTION CheckItera( litetagA, aTagIter )

/* litetagA es reb acabat en '/'.

   Aquesta funció es llença al moment d'afegir un node de XML a litetagA, i
   es comprova si aquest nou node afegit és iteratiu, i si és el cas s'el
   numera. A la resta de nodes que arriberien a 'penjar' d'ell s'els posa
   el numeral a '0', doncs es tractaria d'una nova captura de nodes iteratius.
   
   Per a que la possada a zero de les iteracions que penjen de la que
   estem examinant, cal que aTagIter estigui ordenada ascendentment.
   ------------------------------------------------------------------------ */


Local cCadena     := litetagA
Local nContadorA  := 0
Local nContadorB  := 0
Local PosA        := 0
Local PosB        := 0
Local LenaTagIter := 0
Local LencCadena  := 0

Do While (PosA := AT( "<", cCadena) ) > 0
    //Traza( 1 , "a-cCadena=", cCadena )
    PosB := AT( ">", cCadena )
    cCadena := Left( cCadena, PosA - 1 ) + SubStr( cCadena, PosB + 1 )
    //Traza( 1 , "b-cCadena=", cCadena )
End

If ( nContadorA := ASCan( aTagIter, { |aVal| aVal[1] = cCadena } ) ) > 0
   
    /* Poso la resta d'elements iteratius que penjen del que estem examinant a zero (0).
       ------------------------------------------------------------------------------ */

    LenaTagIter := Len(aTagIter)
    LencCadena  := Len(cCadena)
   
    For nContadorB := ( nContadorA + 1 ) To LenaTagIter
        If cCadena = Left( aTagIter[nContadorB][1], LencCadena )
            aTagIter[nContadorB][2] := 0
        EndIf
    EndFor

    aTagIter[ nContadorA ][2] ++
    cCadena :=  Left( litetagA, Len(litetagA) - 1 ) + "<" + AllTrim(Str(aTagIter[ nContadorA ][2], 10,0)) + ">/"
   
  Else
    cCadena   := litetagA
EndIf

Return cCadena
//----------------------------------------------------------------------------//


Prueben a pasarle a LoadXML() un fichero XML y vean el array que les devuelve, hacer un AT() nos facilita recuperar el valor que lleva ese Tag.

Con mucho gusto compartiría mi código, pero no lo tengo tan bien estructurado para aislarlo del programa donde se utiliza, hay demasiadas llamadas a funciones que utilizo para otras cosas.

Si que puedo compartir parte de él y como he resuelto situaciones que voy detectando.

Saludos,
Un Saludo
Carlos G.

FiveWin 24.02 + Harbour 3.2.0dev (r2403071241), BCC 7.7 Windows 10
FiveWiDi
 
Posts: 1203
Joined: Mon Oct 10, 2005 2:38 pm

Next

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: Google [Bot] and 10 guests