¿Leer un XML CFDI 3.3 con una macro de Excel? aquí te explico como hacerlo.

7 mar 2019

¿Leer un XML CFDI 3.3 con una macro de Excel? aquí te explico como hacerlo.


Hace algún tiempo publique una macro para extraer la información de XML 3.3 y un usuario en Facebook me hizo el comentario que diera una explicación de como se hace. como la idea principal de este blog es compartir conocimientos me he dado a la tarea de redactar este post.
Antes de comenzar, les quiero comentar que hay muchísimos métodos para poder extraer la información de un XML. algunos menos útiles que otros para nuestro objetivo, pero aun así vale la pena que los conozcan.

Primer Método - QueryTable

Leer XML con excel

Con el objeto QueryTable es un miembro de la colección QueryTables.
Este método crea una conexión de tipo texto, extrae toda la información del XML mediante una consulta de datos externa y la convierte en una tabla.

Sub Leer_XML_1()
    With ActiveSheet.QueryTables.Add(Connection:= _
        "TEXT;" & "C:\Luis_Reyes\Tu_archivo_XML.xml", _
         Destination:=Range("$A$1"))
        .Refresh BackgroundQuery:=False
    End With
End Sub

¡Ojo! toda la información del XML la coloca en una tabla.
A trabajar: Copia y pega el código en un módulo de VBA de excel, sustituye C:\Luis_Reyes\Tu_archivo_XML.xml por la ruta de tu archivo


Método 2 - Workbooks.OpenXML


Leer XML con excel


Es una expresión variable que representa un objeto de libros de trabajo.
Con este método se va a importar el XML a Excel.

Sub Leer_XML_2()
Range("A1").Select
Workbooks.OpenXML Filename:="C:\Luis_Reyes\Tu_archivo_XML.xml"
End Sub

A trabajar: Copia y pega el código en un módulo de VBA de excel, sustituye C:\Luis_Reyes\Tu_archivo_XML.xml por la ruta de tu archivo

Método 3 - Scripting.FileSystemObject

Leer XML con excel

Para utilizar FSO, se puede hacer mediante una variable de tipo object y creando el objeto con CreateObject, o también incluyendo la referencia a Microsoft scripting Runtime desde el menú referencias del vb y declarar la variable fso como de tipo FileSystemObject.
Es método es muy interesante porque nos permite acceder al sistema de ficheros, y se puede realizar muchas acciones: leer, mover, copiar, etc.
Este método llega al mismo resultado que el primero, pero con la diferencia que no realiza ninguna conexión para extraer la información y es mucho más veloz que el primero. 

Sub Leer_XML_3()
Range("A1") = CreateObject("Scripting.FileSystemObject").GetFile _
("C:\Luis_Reyes\Tu_archivo_XML.xml").OpenAsTextStream(1, 0).ReadAll
End Sub

A trabajar: Copia y pega el código en un módulo de VBA de excel, sustituye C:\Luis_Reyes\Tu_archivo_XML.xml por la ruta de tu archivo

Método 4 (el mejor de todos)  - CreateObject("MSXML2.DOMDocument") 



Para el final he dejado lo más interesante y el objetivo de este post: "extraer la información de un XML CFDI 3.3" con una macro, y que la información de cada elemento lo coloque en una celda, pero, para poder lograr esto necesitamos conocer los elementos que tiene un CFDI 3.3; recordemos hay nodos padres y nodos hijos, cada nodo tiene sus propios atributos.

Conocer los nodos que vamos a trabajar:
  • Nodo Padre: “/cfdi:Comprobante”, etc.
  • Nodos Hijos: “/cfdi:Emisor”, “/cfdi:Emisor”, “/cfdi:Conceptos/”, etc.
Cabe mencionar que se puede hacer con otros métodos, pero he elegido el Método CreateObject("MSXML2.DOMDocument"), porque no se tiene que agregar la referencia : "Microsoft XML, v3.0" como en post pasado. porque corresponde a la biblioteca a la que hace referencia "Microsoft XML, v3.0" en VBA (msxml3.dll).

Sin más preámbulo, comenzamos:

Creamos los objetos:
Dim xmlObj As Object ' XMl
Dim ListaNodo As Object ' nodos

Usamos la instrucción SET para asignar valores a nuestra variable tipo objeto, (xmlObj será nuestro objeto del XML.)
Set xmlObj = CreateObject("MSXML2.DOMDocument")

Cargamos el XML con el método "load"
xmlObj.Load ("C:\Users\Luis Reyes\Desktop\tu_archivo_XML.xml") 'coloca la ruta de tu archivo

Utilizamos un control de errores On Error Resume Next, por si no encuentra un nodo o un atributo que no marque el error y siga buscando.
On Error Resume Next

Cargamos el nodo padre Comprobante
Set ListaNodo = xmlObj.SelectNodes("/cfdi:Comprobante")
Enumeramos los elementos (atributos) que forman el nodo a través del ciclo For Each ... Next.
For Each Nodo In ListaNodo
'......aquí el atributo que buscamos......
Next Nodo

Dentro del For anterior comenzamos a extraer los valores de los atributos que necesitamos con "Attributes.getNamedItem"

Subtotales = Nodo.Attributes.getNamedItem("SubTotal").Text
'Nodo es el número de atributo
'Attributes.getNamedItem adquiere el nombre de nuestro atributo que buscamos y obtiene su valor

Entonces nuestro for queda de la siguiente manera:
For Each Nodo In ListaNodo
Subtotal = Nodo.Attributes.getNamedItem("SubTotal").Text
'puedes agregar los demás atributos
Next Nodo

Y para los nodos hijos se tiene que agredar al nodo padre queda así: "/cfdi:Comprobante/cfdi:Emisor",

Set ListaNodo = DocumentoXML.SelectNodes("/cfdi:Comprobante/cfdi:Emisor")
For Each Nodo In ListaNodo
Proveedor = Nodo.Attributes.getNamedItem("Nombre").Text
'puedes agregar los demás atributos
Next Nodo

Para cuando el nodo padre tiene más de un hijo: "/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto"

Set ListaNodo = DocumentoXML.SelectNodes("/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto")
For Each Nodo In ListaNodo
    Concepto = Concepto & Nodo.Attributes.getNamedItem("Descripcion").Text & "|"
'puedes agregar los demás atributos  
Next Nodo


Seguimos cargando  y agregando los for necesarios.

Si te gusto está publicación me puedes invitar un café



6 comentarios :

  1. En el ejemplo que existe el nodo padre "/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto" que tiene 3 nodos hijos, a su vez cada nodo hijo tiene otro nodo hijo "/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto/cfdi:Impuestos/cfdi:Traslados/cfdi:Traslado".

    Como puedo obtener el atributo "impuesto" especificamente del primer nodo con indice 0. Esto porque a veces los xml no traen en nodo Traslado, si que despues de obtener los datos de los atributos del primer nodo hijo "/cfdi:Comprobante/cfdi:Conceptos/cfdi:Concepto" quiero los datos del impuesto de ese nodo.

    Gracias de antemano

    ResponderBorrar
    Respuestas
    1. Hola Mario, me puedes enviar un ejemplo a zona.lerh@gmail.com, por favor.

      Saludos!!

      Borrar
  2. Hola, muy buen aporte, sólo tengo una duda; cómo puedo sacar el estatus de la factura en el xml, si está activo o está cancelado?

    ResponderBorrar
  3. Buenas tardes, ha sido muy útil el codigo fuente, sin embargo te quiero consultar, como le puedo hacer con los descuentos, como no todos los artículos tienen descuento, al momento de concatenar descuento con el separador ­"|" y querer separarlo posteriormente el descuento me queda en una fila distinta al articulo que le corresponde, dicho de otro modo, como le puedo decir a excel que en caso que no aparezca el texto descuento arroje un 0 en ese nodo? gracias por el apoyo

    ResponderBorrar
    Respuestas
    1. al contrario, gracias a ti por tu visita, se tiene que cambiar el codigo fuente y cambiar el orden de como se acomodan los datos, contactame por whatsapp

      Borrar
  4. Hola, muchas gracias por el aporte, como puedo modificar la macro, para que cuando no encuentre un campo, ejemplo IVA, arroje un 0, para que al concatenar y desunir no se pierda la secuencia.

    Gracias por el apoyo

    ResponderBorrar