¿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
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
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
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.
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")
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é
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".
ResponderBorrarComo 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
Hola Mario, me puedes enviar un ejemplo a zona.lerh@gmail.com, por favor.
BorrarSaludos!!
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?
ResponderBorrarBuenas 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
ResponderBorraral 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
BorrarHola, 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.
ResponderBorrarGracias por el apoyo