Uso de XSL para transformar la factura electronica XML

Contenido/contents:
Introducion
Cadena Original
Verificacion de sello
Impresion
Informe
Pagina anterior/Previous page

Introduccion
Este es un ejemplo para trabajar con la factura electronica (CFD) usando la linea de comandos.
Aqui en la empresa para el trabajo diario NO usamos este codigo, lo que tenemos en produccion es el programa satxmlsv2.php desarrollado en PHP usando la extension openssl.
Este conjunto de pasos me sirvio en su momento para verificar que lo prograame en PHPO fue correcto.
Y claro tambien me sirvio para 'aprender' XSLT, no soy experto, no lo uso como trabajo rutinario pero como prueba de concepto me sirvio bastante.
Estas rutinas pueden estar incompletas o poco elegantes, pero insisto, solo son una 'prueba de concepto', la version actualizada y verificada es mi codigo en PHP.
Cadena Original
Esta es mi primera prueba con XSLT por lo cual este archivo de transformacion esta muy extenso. Estoy seguro de que se pudiera siplificar no repitiendo lo mismo para cada atributo pero mientras no descubra la manera asi lo dejo.
5/Jun/2006 Esta ya es mi segunda version, gracias a David Ongay por detectar varios errores, esta version ya no requiera que exista Addenda para generar la cadena correcta, y ademas corregi unos pequeñ errores de dedo.
30/Jun/2010 Esta ya es mi tercera version, gracias a Jorge Enrique Perez que me abrio los ojos para el correcto manejo del namespace del sat para las hojas xslt. Ya efectua la transformacion correctamente aun sin borrar el xmlns del archivo xml. pequeñ errores de dedo.
Ojo el SAT ya tiene publicado sus archivos xslt para generar la cadena original, les sugiero que useen esos archivos. correcto manejo del namespace del sat para las hojas xslt. Ya efectua la transformacion correctamente aun sin borrar el xmlns del archivo xml. pequeñ errores de dedo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<xsl:stylesheet version = '1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:cfd='http://www.sat.gob.mx/cfd/2'>
 
<xsl:output method = "text" /> 
 
<xsl:template match="/">|<xsl:apply-templates select="//cfd:Comprobante"/>||</xsl:template>
 
<xsl:template match="cfd:Comprobante">
      <xsl:if test="@version">|<xsl:value-of select="@version"/></xsl:if>
      <xsl:if test="@serie">|<xsl:value-of select="@serie"/></xsl:if>
      <xsl:if test="@folio">|<xsl:value-of select="@folio"/></xsl:if>
      <xsl:if test="@fecha">|<xsl:value-of select="@fecha"/></xsl:if>
      <xsl:if test="@noAprobacion">|<xsl:value-of select="@noAprobacion"/></xsl:if>
      <xsl:if test="@anoAprobacion">|<xsl:value-of select="@anoAprobacion"/></xsl:if>
      <xsl:if test="@tipoDeComprobante">|<xsl:value-of select="@tipoDeComprobante"/></xsl:if>
      <xsl:if test="@formaDePago">|<xsl:value-of select="@formaDePago"/></xsl:if>
      <xsl:if test="@condicionesDePago">|<xsl:value-of select="@condicionesDePago"/></xsl:if>
      <xsl:if test="@subTotal">|<xsl:value-of select="@subTotal"/></xsl:if>
      <xsl:if test="@descuento">|<xsl:value-of select="@descuento"/></xsl:if>
      <xsl:if test="@total">|<xsl:value-of select="@total"/></xsl:if>
      <xsl:apply-templates select="//cfd:Emisor"/>
      <xsl:apply-templates select="//cfd:DomicilioFiscal"/>
      <xsl:apply-templates select="//cfd:ExpedidoEn"/>
      <xsl:apply-templates select="//cfd:Receptor"/>
      <xsl:apply-templates select="//cfd:Domicilio"/>
      <xsl:apply-templates select="//cfd:Concepto"/>
      <xsl:apply-templates select="//cfd:Impuestos"/>
</xsl:template>
 
<xsl:template match="cfd:Emisor">
      <xsl:if test="@rfc">|<xsl:value-of select="@rfc"/></xsl:if>
      <xsl:if test="@nombre">|<xsl:value-of select="@nombre"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:DomicilioFiscal">
      <xsl:if test="@calle">|<xsl:value-of select="@calle"/></xsl:if>
      <xsl:if test="@noExterior">|<xsl:value-of select="@noExterior"/></xsl:if>
      <xsl:if test="@noInterior">|<xsl:value-of select="@noInterior"/></xsl:if>
      <xsl:if test="@colonia">|<xsl:value-of select="@colonia"/></xsl:if>
      <xsl:if test="@localidad">|<xsl:value-of select="@localidad"/></xsl:if>
      <xsl:if test="@referencia">|<xsl:value-of select="@referencia"/></xsl:if>
      <xsl:if test="@municipio">|<xsl:value-of select="@municipio"/></xsl:if>
      <xsl:if test="@estado">|<xsl:value-of select="@estado"/></xsl:if>
      <xsl:if test="@pais">|<xsl:value-of select="@pais"/></xsl:if>
      <xsl:if test="@codigoPostal">|<xsl:value-of select="@codigoPostal"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:ExpedidoEn">
      <xsl:if test="@calle">|<xsl:value-of select="@calle"/></xsl:if>
      <xsl:if test="@noExterior">|<xsl:value-of select="@noExterior"/></xsl:if>
      <xsl:if test="@noInterior">|<xsl:value-of select="@noInterior"/></xsl:if>
      <xsl:if test="@colonia">|<xsl:value-of select="@colonia"/></xsl:if>
      <xsl:if test="@localidad">|<xsl:value-of select="@localidad"/></xsl:if>
      <xsl:if test="@referencia">|<xsl:value-of select="@referencia"/></xsl:if>
      <xsl:if test="@municipio">|<xsl:value-of select="@municipio"/></xsl:if>
      <xsl:if test="@estado">|<xsl:value-of select="@estado"/></xsl:if>
      <xsl:if test="@pais">|<xsl:value-of select="@pais"/></xsl:if>
      <xsl:if test="@codigoPostal">|<xsl:value-of select="@codigoPostal"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:Receptor">
      <xsl:if test="@rfc">|<xsl:value-of select="@rfc"/></xsl:if>
      <xsl:if test="@nombre">|<xsl:value-of select="@nombre"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:Domicilio">
      <xsl:if test="@calle">|<xsl:value-of select="@calle"/></xsl:if>
      <xsl:if test="@noExterior">|<xsl:value-of select="@noExterior"/></xsl:if>
      <xsl:if test="@noInterior">|<xsl:value-of select="@noInterior"/></xsl:if>
      <xsl:if test="@colonia">|<xsl:value-of select="@colonia"/></xsl:if>
      <xsl:if test="@localidad">|<xsl:value-of select="@localidad"/></xsl:if>
      <xsl:if test="@referencia">|<xsl:value-of select="@referencia"/></xsl:if>
      <xsl:if test="@municipio">|<xsl:value-of select="@municipio"/></xsl:if>
      <xsl:if test="@estado">|<xsl:value-of select="@estado"/></xsl:if>
      <xsl:if test="@pais">|<xsl:value-of select="@pais"/></xsl:if>
      <xsl:if test="@codigoPostal">|<xsl:value-of select="@codigoPostal"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:Concepto">
      <xsl:if test="@cantidad">|<xsl:value-of select="@cantidad"/></xsl:if>
      <xsl:if test="@unidad">|<xsl:value-of select="@unidad"/></xsl:if>
      <xsl:if test="@noIdentificacion">|<xsl:value-of select="@noIdentificacion"/></xsl:if>
      <xsl:if test="@descripcion">|<xsl:value-of select="@descripcion"/></xsl:if>
      <xsl:if test="@valorUnitario">|<xsl:value-of select="@valorUnitario"/></xsl:if>
      <xsl:if test="@importe">|<xsl:value-of select="@importe"/></xsl:if>
      <xsl:apply-templates select="InformacionAduanera"/>
</xsl:template>
 
<xsl:template match="cfd:Impuestos">
      <xsl:apply-templates select="//cfd:Retencion"/>
      <xsl:if test="@totalImpuestosRetenidos">|<xsl:value-of select="@totalImpuestosRetenidos"/></xsl:if>
      <xsl:apply-templates select="//cfd:Traslado"/>
      <xsl:if test="@totalImpuestosTrasladados">|<xsl:value-of select="@totalImpuestosTrasladados"/></xsl:if>
  </xsl:template>
 
<xsl:template match="cfd:InformacionAduanera">
      <xsl:if test="@numero">|<xsl:value-of select="@numero"/></xsl:if>
      <xsl:if test="@fecha">|<xsl:value-of select="@fecha"/></xsl:if>
      <xsl:if test="@aduana">|<xsl:value-of select="@aduana"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:Retencion">
      <xsl:if test="@impuesto">|<xsl:value-of select="@impuesto"/></xsl:if>
      <xsl:if test="@importe">|<xsl:value-of select="@importe"/></xsl:if>
</xsl:template>
 
<xsl:template match="cfd:Traslado">
      <xsl:if test="@impuesto">|<xsl:value-of select="@impuesto"/></xsl:if>
      <xsl:if test="@tasa">|<xsl:value-of select="@tasa"/></xsl:if>
      <xsl:if test="@importe">|<xsl:value-of select="@importe"/></xsl:if>
</xsl:template>
 
</xsl:stylesheet>
Vamor a aplicar las reglas de transformacion anteriores al siguiente archivo XML.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<Comprobante anoAprobacion="2006"
             certificado="MIIEeTCCA2GgAwIBAgIUMDAwMDEwMDAwMDAwMDA4MjM3NDcwDQYJKoZIhvcNAQEFBQAwggGKMS8wLQYDVQQKDCZTZXJ2aWNpbyBkZSBBZG1pbmlzdHJhY2nDs24gVHJpYnV0YXJpYTE4MDYGA1UECwwvQWRtaW5pc3RyYWNpw7NuIGRlIFNlZ3VyaWRhZCBkZSBsYSBJbmZvcm1hY2nDs24xODA2BgNVBAMML0EuQy4gZGVsIFNlcnZpY2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMSYwJAYDVQQQEx1Bdi4gSGlkYWxnbyA3NywgQ29sLiBHdWVycmVybzEOMAwGA1UEERMFMDYzMDAxEjAQBgNVBAcMCUNveW9hY8OhbjEZMBcGA1UECBMQRGlzdHJpdG8gRmVkZXJhbDELMAkGA1UEBhMCTVgxFTATBgNVBC0TDFNBVDk3MDcwMU5OMzEhMB8GCSqGSIb3DQEJARYSYXNpc25ldEBzYXQuZ29iLm14MTUwMwYJKoZIhvcNAQkCEyZSZXNwb25zYWJsZTogQ2VzYXIgTHVpcyBQZXJhbGVzIFRlbGxlejAeFw0wNjA5MDgxMzQwMjFaFw0wODA5MDcxMzQwMjFaMIHFMSUwIwYDVQQtExxGSkM3ODAzMTVFOTEgLyBBRUJSNDIwMTE5Mk44MR4wHAYDVQQFExUgLyBBRUJSNDIwMTE5SERGTlJEMDkxIjAgBgNVBAoTGUZBQiBKQUJPTiBDT1JPTkEgU0EgREUgQ1YxEDAOBgNVBAsTB2dlbmVyYWwxIjAgBgNVBAMTGUZBQiBKQUJPTiBDT1JPTkEgU0EgREUgQ1YxIjAgBgNVBCkTGUZBQiBKQUJPTiBDT1JPTkEgU0EgREUgQ1YwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOx6usU1zIZTEKYRVxfZBUjvQcB9sVtLgrgAGDDL6K3vpExpl6/Q8g+fT+UTlLveg7JVheQamsQTZhMTUGnny4OTGpfdSn5SPnvCchBqq7gEGXi26jjwOrs3PO0mSIa6VweMCPPKFQ56RYaJ18kpiNgWUdl9yivh/SO5godmWZOpAgMBAAGjHTAbMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgbAMA0GCSqGSIb3DQEBBQUAA4IBAQDDAzYjTyHcYXjPzXIPfUtHizC8G/XjeOvcdScS/rsxHVbbusuKpAS8hK5hjT00GXKjB5u7NEkNEOJiOgZl08mk5FKuKqqXUbGB35atKdDMEMhN56RMbYump9tJJ8JQlbvuMywiL2J3GpO9wSdZ044Z37Szv5bE3/G8oM4/kz7rhsOrsVAFwodfIvHYDZmRQlYqPV5B7X8AzhUmehJ8rg3faJfjQLhVRgsCt27HCLIzLXYKRPSLNg0X1mxi745Ailxwk7nO7HIEN7coXdaMbkczFD4Fhe9aaskyj9mMwo/XASLiQb5PlBqE2uLQITUUbU5w4P32wlZGMqX4XxA3zG+h"
             descuento="0"
             fecha="2006-11-20T08:50:48"
             folio="051661"
             formaDePago="EL PAGO DE ESTA FACTURA (CONTRAPRESTACION) SE EFECTUARA EN UNA SOLA EXHIBICION, SI POR ALGUNA RAZON NO FUERA ASI, EMITIREMOS LOS COMPROBANTES DE LAS PARCIALIDADES RESPECTIVAS"
             noAprobacion="2951"
             noCertificado="00001000000000823747"
             sello="wVLpmRWPFxC6UzitqsdZkljtcYA8ESYPg21Tx0P3p8EAvmIUOSb8OqDw0d+RPswU2BTN77rzygmOvS5O3L6kv4q8u9Yg04b1CjOUbo+YggrJgxUsG3ymMOl4eh/JQ1utNzwJk44ZBeTnWldT4Hm5V5ThKW5yXlc/udV61PnSzIo="
             serie="FAXA"
             subTotal="43824.69"
             tipoDeComprobante="ingreso"
             total="50398.39"
             version="2.0"
             xmlns="http://www.sat.gob.mx/cfd/2"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.sat.gob.mx/cfd/2 http://www.sat.gob.mx/sitio_internet/cfd/2/cfdv2.xsd">
    <Emisor nombre="FABRICA DE JABON LA CORONA, S.A. DE C.V."
            rfc="FJC780315E91">
        <DomicilioFiscal calle="CARLOS B. ZETINA"
                         codigoPostal="55348"
                         colonia="INDUSTRIAL XALOSTOC"
                         estado="MEXICO"
                         localidad="ECATEPEC DE MORELOS"
                         municipio="ECATEPEC"
                         noExterior="80"
                         pais="MEXICO" />
        <ExpedidoEn calle="CARLOS B.ZETINA NO.80"
                    codigoPostal="55348"
                    estado="EDO. DE MEXICO"
                    localidad="XALOSTOC, EDO. DE MEXICO"
                    municipio="XALOSTOC, EDO. DE MEXICO"
                    pais="MEXICO" />
    </Emisor>
    <Receptor nombre="NUEVA WAL-MART DE MEXICO SRL DE CV"
              rfc="NWM9709244W4">
        <Domicilio calle="NEXTENGO"
                   codigoPostal="2770"
                   colonia="STA. CRUZ ACAYUCAN"
                   estado="DISTRITO FEDERAL"
                   localidad="AZCAPOTZALCO"
                   municipio="AZCAPOTZALCO"
                   noExterior="78"
                   pais="MEXICO" />
    </Receptor>
    <Conceptos>
        <Concepto cantidad="22"
                  descripcion="TEPEYAC 25P 400 G C/ENV"
                  importe="2441.34"
                  valorUnitario="110.97" />
        <Concepto cantidad="11"
                  descripcion="ZOTE AZUL 25P 400 G."
                  importe="1220.67"
                  valorUnitario="110.97" />
        <Concepto cantidad="22"
                  descripcion="ZOTE BCO. 25P 400 G"
                  importe="2441.34"
                  valorUnitario="110.97" />
        <Concepto cantidad="7"
                  descripcion="ZOTE BCO. 50P 200 G"
                  importe="776.79"
                  valorUnitario="110.97" />
        <Concepto cantidad="44"
                  descripcion="ZOTE ROSA 25P 400 G"
                  importe="4882.68"
                  valorUnitario="110.97" />
        <Concepto cantidad="6"
                  descripcion="ZOTE ROSA 50P 200 G"
                  importe="665.82"
                  valorUnitario="110.97" />
        <Concepto cantidad="12"
                  descripcion="ROMA 4P 5 KGS"
                  importe="2620.08"
                  valorUnitario="218.34" />
        <Concepto cantidad="24"
                  descripcion="ROMA 10P 2 KGS"
                  importe="5273.64"
                  valorUnitario="219.74" />
        <Concepto cantidad="120"
                  descripcion="ROMA 10P 1 KG"
                  importe="13354.20"
                  valorUnitario="111.29" />
        <Concepto cantidad="12"
                  descripcion="FOCA 10P 2 KGS"
                  importe="2806.38"
                  valorUnitario="233.87" />
        <Concepto cantidad="10"
                  descripcion="BLANCA NIEVES 10P 2 KGS"
                  importe="2014.65"
                  valorUnitario="201.47" />
        <Concepto cantidad="40"
                  descripcion="BLANCA NIEVES 10P 1 KG"
                  importe="4086.00"
                  valorUnitario="102.15" />
        <Concepto cantidad="5"
                  descripcion="BRILOZA 10P 1 KG"
                  importe="510.75"
                  valorUnitario="102.15" />
        <Concepto cantidad="6"
                  descripcion="FOCA 12B 1LT"
                  importe="730.35"
                  valorUnitario="121.73" />
    </Conceptos>
    <Impuestos totalImpuestosTrasladados="6573.70">
        <Traslados>
            <Traslado importe="6573.70"
                      impuesto="IVA"
                      tasa="15.00" />
        </Traslados>
    </Impuestos>
</Comprobante>
Para aplicar las reglas usamos el comando xsltproc dando como primer argumento el archivo con las reglas de transofrmacion XSL y como segundo el nombre del archivo con la factura XML.
[web@web sat]$ xsltproc cadena_original.xsl fact.xml
||2.0|FAXA|051661|2006-11-20T08:50:48|2951|2006|ingreso|EL PAGO DE ESTA FACTURA (CONTRAPRESTACION) SE EFECTUARA EN UNA SOLA EXHIBICION, SI POR ALGUNA RAZON NO FUERA ASI, EMITIREMOS LOS COMPROBANTES DE LAS PARCIALIDADES RESPECTIVAS|43824.69|0|50398.39|FJC780315E91|FABRICA DE JABON LA CORONA, S.A. DE C.V.|CARLOS B. ZETINA|80|INDUSTRIAL XALOSTOC|ECATEPEC DE MORELOS|ECATEPEC|MEXICO|MEXICO|55348|CARLOS B.ZETINA NO.80|XALOSTOC, EDO. DE MEXICO|XALOSTOC, EDO. DE MEXICO|EDO. DE MEXICO|MEXICO|55348|NWM9709244W4|NUEVA WAL-MART DE MEXICO SRL DE CV|NEXTENGO|78|STA. CRUZ ACAYUCAN|AZCAPOTZALCO|AZCAPOTZALCO|DISTRITO FEDERAL|MEXICO|2770|22|TEPEYAC 25P 400 G C/ENV|110.97|2441.34|11|ZOTE AZUL 25P 400 G.|110.97|1220.67|22|ZOTE BCO. 25P 400 G|110.97|2441.34|7|ZOTE BCO. 50P 200 G|110.97|776.79|44|ZOTE ROSA 25P 400 G|110.97|4882.68|6|ZOTE ROSA 50P 200 G|110.97|665.82|12|ROMA 4P 5 KGS|218.34|2620.08|24|ROMA 10P 2 KGS|219.74|5273.64|120|ROMA 10P 1 KG|111.29|13354.20|12|FOCA 10P 2 KGS|233.87|2806.38|10|BLANCA NIEVES 10P 2 KGS|201.47|2014.65|40|BLANCA NIEVES 10P 1 KG|102.15|4086.00|5|BRILOZA 10P 1 KG|102.15|510.75|6|FOCA 12B 1LT|121.73|730.35|IVA|15.00|6573.70|6573.70||
Ya con esa cadena original podemos aplicar las reglas para calcular el sello. El primer paso es calcular el hash md5.
[web@web sat]$ xsltproc cadena_original.xsl fact.xml | md5sum | cut -f1 -d \ 
907327b350082d1d40e0f3787db973e8
Este hash ya se puede sellar con el certificado (que ya tengo convertido en formato PEM) y ademas codificarlo en formato base64 porque el sello lo deja en binario.
Openssl tiene la facilidad de calcular el md5 y de sellarlo en un mismo paso, por lo cual ya no hace falta calcular el md5 en un paso previo.
[web@web sat]$ xsltproc cadena_original.xsl fact.xml | openssl dgst -md5 -sign 00001000000000823747.key.pem | openssl enc -base64 -A
wVLpmRWPFxC6UzitqsdZkljtcYA8ESYPg21Tx0P3p8EAvmIUOSb8OqDw0d+RPswU2BTN77rzygmOvS5O3L6kv4q8u9Yg04b1CjOUbo+YggrJgxUsG3ymMOl4eh/JQ1utNzwJk44ZBeTnWldT4Hm5V5ThKW5yXlc/udV61PnSzIo=
Si son observadores veran que el sello recien calculado es el mismo que esta almacenado en el atributo "sello" del XML.
Verificacion del sello
Ahora voy a simular lo que hay que hacer para validar que el sello es correcto, es decir, es el sello correspondiente a la factura firmada por el certificado correspondiente.
Este es una pequen˜a hoja de conversion para extraer el campo del sello.
1
2
3
4
5
6
7
8
9
10
11
<xsl:stylesheet version = '1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:cfd="http://www.sat.gob.mx/cfd/2">
                   
<xsl:output method = "text" /> 
 
<xsl:template match="cfd:Comprobante">
      <xsl:value-of select="@sello"/>
</xsl:template>
 
</xsl:stylesheet>
Para aplicar las reglas usamos el comando xsltproc dando como primer argumento el archivo con las reglas de transofrmacion XSL y como segundo el nombre del archivo con la factura XML.
[web@web sat]$ xsltproc sello.xsl fact.xml
wVLpmRWPFxC6UzitqsdZkljtcYA8ESYPg21Tx0P3p8EAvmIUOSb8OqDw0d+RPswU2BTN77rzygmOvS5O3L6kv4q8u9Yg04b1CjOUbo+YggrJgxUsG3ymMOl4eh/JQ1utNzwJk44ZBeTnWldT4Hm5V5ThKW5yXlc/udV61PnSzIo=
Ahora este campo del sello obtenido del archivo XML lo de-codificamos del codigo base64 y lo dejamos nuevamente como el sello en binario y lo dejamos en un archivo temporal.
[web@web sat]$ xsltproc sello.xsl fact.xml | openssl enc -base64 -d -A -out sello
Ahora volvemos a 'firmar' pero con la llave publica y verificamos que el sello obtenido coincida con el enviado.
[web@web sat]$ xsltproc cadena_original.xsl fact.xml | openssl dgst -md5 -verify 00001000000000823747.cer.pem -signature sello
Verified OK
Generacion de archivo para impresion
Con el siguiente arhivo de transformacion genero un archivo HTML para consultar en un formato 'agradable al usuario' el contenido de la factura XML.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<xsl:stylesheet version = '1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:cfd='http://www.sat.gob.mx/cfd/2'>
 
<xsl:output method = "html" /> 
 
<xsl:template match="//cfd:Comprobante">
   <html>
   <head>
   <link rel="STYLESHEET" media="screen" type="text/css" href="factura.css"/>
   <title>Factura Electronica <xsl:value-of select="@serie"/><xsl:value-of select="@folio"/></title>
   </head>
   <body>
   <table width="100%" border="1">
      <tr><td colspan="2" align="right">
          <table border="1">
               <tr><th class="h1">Serie</th><td class="h1"><xsl:value-of select="@serie"/></td></tr>
               <tr><th class="h1">Folio</th><td class="h1"><xsl:value-of select="@folio"/></td></tr>
               <tr><th class="h1">Fecha</th><td class="h1"><xsl:value-of select="@fecha"/></td></tr>
               <tr><th class="h1">Aprobacion</th><td class="h1"><xsl:value-of select="@noAprobacion"/></td></tr>
           </table>
           </td>
           </tr>
      <tr><td width="50%">
           <table width="100%" border="1"><tr><th colspan="2" class="h1">Emisor</th></tr>
               <tr><th>RFC</th><td><xsl:value-of select="cfd:Emisor/@rfc"/></td></tr>
               <tr><th>Nombre</th><td><xsl:value-of select="cfd:Emisor/@nombre"/></td></tr>
                <xsl:apply-templates select="//cfd:DomicilioFiscal"/>
 
             </table>
          </td>
          <td>
          <table width="100%" border="1"><tr><th colspan="2" class="h1">Receptor</th></tr>
             <tr><th>RFC</th><td><xsl:value-of select="cfd:Receptor/@rfc"/></td></tr>
             <tr><th>Nombre</th><td><xsl:value-of select="cfd:Receptor/@nombre"/></td></tr>
             <xsl:apply-templates select="//cfd:Domicilio"/>
 
 
           </table>
           </td>
         </tr>
         <tr><table width="100%" border="1">
             <tr><th>Cantidad</th>
                 <th>Descripcion</th>
                 <th>Precio</th>
                 <th>Importe</th>
             </tr>
             <xsl:apply-templates select="//cfd:Concepto"/>
             <xsl:for-each select="Concepto">
             </xsl:for-each>
 
             <xsl:apply-templates select="//cfd:Traslado"/>
            </table>
         </tr>
        </table>
        <hr/>
        <table width="100%" border="1">
            <tr><th>Numero de serie del Certificado</th></tr>
            <tr><td><xsl:value-of select="@noCertificado"/></td></tr>
            <tr><th>Cadena Original</th></tr>
            <tr><td>
            <xsl:apply-imports/>
            </td></tr>
            <tr><th>Sello Digital</th></tr>
            <tr><td><small><small><xsl:value-of select="@sello"/></small></small></td></tr>
        </table>
        <center>
        Este documento es una impresion de un comprobante fiscal digital
        </center>
    </body>
    </html>
</xsl:template>
 
 
<xsl:template match="//cfd:DomicilioFiscal">
    <tr><th colspan="2" class="h2">Domicilio</th></tr>
    <tr><td colspan="2"><xsl:value-of select="@calle"/> # <xsl:value-of select="@noExterior"/> - <xsl:value-of select="@noInterior"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@colonia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@localidad"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@referencia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@municipio"/>
    <xsl:if test="@codigoPostal"> CODIGO POSTAL <xsl:value-of select="@codigoPostal"/></xsl:if>
         </td></tr>
     <tr><td colspan="2"><xsl:value-of select="@estado"/></td></tr>
     <tr><td colspan="2"><xsl:value-of select="@pais"/></td></tr>
</xsl:template>
 
<xsl:template match="//cfd:Domicilio">
    <tr><th colspan="2" class="h2">Domicilio</th></tr>
    <tr><td colspan="2"><xsl:value-of select="@calle"/> # <xsl:value-of select="@noExterior"/> - <xsl:value-of select="@noInterior"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@colonia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@localidad"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@referencia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@municipio"/>
        <xsl:if test="@codigoPostal"> CODIGO POSTAL <xsl:value-of select="@codigoPostal"/></xsl:if>
        </td></tr>
    <tr><td colspan="2"><xsl:value-of select="@estado"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@pais"/></td></tr>
</xsl:template>
 
<xsl:template match="//cfd:Concepto">
    <tr><td align="center"><xsl:value-of select="@cantidad"/></td>
        <td><xsl:value-of select="@descripcion"/></td>
        <td align="right"><xsl:value-of select="@valorUnitario"/></td>
        <td align="right"><xsl:value-of select="@importe"/></td>
    </tr>
</xsl:template>
 
<xsl:template match="//cfd:Traslado">
    <tr><td colspan="2" align="right"><xsl:value-of select="@impuesto"/></td>
        <td align="right"><xsl:value-of select="@importe"/></td>
        <td><xsl:value-of select="@tasa"/> %</td>
    </tr>
</xsl:template>
 
</xsl:stylesheet>
/div>
Invocamos la regla de transformacion para generar el archivo HTML de salida y ya lo podemos consultar o mandar a imprimir.
xsltproc impresion.xml fact.xml > fact.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<html xmlns:cfd="http://www.sat.gob.mx/cfd/2">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="STYLESHEET" media="screen" type="text/css" href="factura.css">
<title>Factura Electronica FAXA051661</title>
</head>
<body>
<table width="100%" border="1">
<tr><td colspan="2" align="right"><table border="1">
<tr>
<th class="h1">Serie</th>
<td class="h1">FAXA</td>
</tr>
<tr>
<th class="h1">Folio</th>
<td class="h1">051661</td>
</tr>
<tr>
<th class="h1">Fecha</th>
<td class="h1">2006-11-20T08:50:48</td>
</tr>
<tr>
<th class="h1">Aprobacion</th>
<td class="h1">2951</td>
</tr>
</table></td></tr>
<tr>
<td width="50%"><table width="100%" border="1">
<tr><th colspan="2" class="h1">Emisor</th></tr>
<tr>
<th>RFC</th>
<td>FJC780315E91</td>
</tr>
<tr>
<th>Nombre</th>
<td>FABRICA DE JABON LA CORONA, S.A. DE C.V.</td>
</tr>
<tr><th colspan="2" class="h2">Domicilio</th></tr>
<tr><td colspan="2">CARLOS B. ZETINA # 80 - </td></tr>
<tr><td colspan="2">INDUSTRIAL XALOSTOC</td></tr>
<tr><td colspan="2">ECATEPEC DE MORELOS</td></tr>
<tr><td colspan="2"></td></tr>
<tr><td colspan="2">ECATEPEC CODIGO POSTAL 55348</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
</table></td>
<td><table width="100%" border="1">
<tr><th colspan="2" class="h1">Receptor</th></tr>
<tr>
<th>RFC</th>
<td>NWM9709244W4</td>
</tr>
<tr>
<th>Nombre</th>
<td>NUEVA WAL-MART DE MEXICO SRL DE CV</td>
</tr>
<tr><th colspan="2" class="h2">Domicilio</th></tr>
<tr><td colspan="2">NEXTENGO # 78 - </td></tr>
<tr><td colspan="2">STA. CRUZ ACAYUCAN</td></tr>
<tr><td colspan="2">AZCAPOTZALCO</td></tr>
<tr><td colspan="2"></td></tr>
<tr><td colspan="2">AZCAPOTZALCO CODIGO POSTAL 2770</td></tr>
<tr><td colspan="2">DISTRITO FEDERAL</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
</table></td>
</tr>
<tr><table width="100%" border="1">
<tr>
<th>Cantidad</th>
<th>Descripcion</th>
<th>Precio</th>
<th>Importe</th>
</tr>
<tr>
<td align="center">22</td>
<td>TEPEYAC 25P 400 G C/ENV</td>
<td align="right">110.97</td>
<td align="right">2441.34</td>
</tr>
<tr>
<td align="center">11</td>
<td>ZOTE AZUL 25P 400 G.</td>
<td align="right">110.97</td>
<td align="right">1220.67</td>
</tr>
<tr>
<td align="center">22</td>
<td>ZOTE BCO. 25P 400 G</td>
<td align="right">110.97</td>
<td align="right">2441.34</td>
</tr>
<tr>
<td align="center">7</td>
<td>ZOTE BCO. 50P 200 G</td>
<td align="right">110.97</td>
<td align="right">776.79</td>
</tr>
<tr>
<td align="center">44</td>
<td>ZOTE ROSA 25P 400 G</td>
<td align="right">110.97</td>
<td align="right">4882.68</td>
</tr>
<tr>
<td align="center">6</td>
<td>ZOTE ROSA 50P 200 G</td>
<td align="right">110.97</td>
<td align="right">665.82</td>
</tr>
<tr>
<td align="center">12</td>
<td>ROMA 4P 5 KGS</td>
<td align="right">218.34</td>
<td align="right">2620.08</td>
</tr>
<tr>
<td align="center">24</td>
<td>ROMA 10P 2 KGS</td>
<td align="right">219.74</td>
<td align="right">5273.64</td>
</tr>
<tr>
<td align="center">120</td>
<td>ROMA 10P 1 KG</td>
<td align="right">111.29</td>
<td align="right">13354.20</td>
</tr>
<tr>
<td align="center">12</td>
<td>FOCA 10P 2 KGS</td>
<td align="right">233.87</td>
<td align="right">2806.38</td>
</tr>
<tr>
<td align="center">10</td>
<td>BLANCA NIEVES 10P 2 KGS</td>
<td align="right">201.47</td>
<td align="right">2014.65</td>
</tr>
<tr>
<td align="center">40</td>
<td>BLANCA NIEVES 10P 1 KG</td>
<td align="right">102.15</td>
<td align="right">4086.00</td>
</tr>
<tr>
<td align="center">5</td>
<td>BRILOZA 10P 1 KG</td>
<td align="right">102.15</td>
<td align="right">510.75</td>
</tr>
<tr>
<td align="center">6</td>
<td>FOCA 12B 1LT</td>
<td align="right">121.73</td>
<td align="right">730.35</td>
</tr>
<tr>
<td colspan="2" align="right">IVA</td>
<td align="right">6573.70</td>
<td>15.00 %</td>
</tr>
</table></tr>
</table>
<hr>
<table width="100%" border="1">
<tr><th>Numero de serie del Certificado</th></tr>
<tr><td>00001000000000823747</td></tr>
<tr><th>Cadena Original</th></tr>
<tr><td></td></tr>
<tr><th>Sello Digital</th></tr>
<tr><td><small><small>wVLpmRWPFxC6UzitqsdZkljtcYA8ESYPg21Tx0P3p8EAvmIUOSb8OqDw0d+RPswU2BTN77rzygmOvS5O3L6kv4q8u9Yg04b1CjOUbo+YggrJgxUsG3ymMOl4eh/JQ1utNzwJk44ZBeTnWldT4Hm5V5ThKW5yXlc/udV61PnSzIo=</small></small></td></tr>
</table>
<center>
        Este documento es una impresion de un comprobante fiscal digital
        </center>
</body>
</html>
/div>
Si desea ver como se ve archivo de consulta de la factura electronica, dele click aqui
Generacion de archivo para informe mensual de folios
El SAT comenta que se puede generar el renglon del CFD para el informe mensual usando XSLT para cada factura.
En mi caso particular, lo que tengo en linea y en base de datos SQL son los campos de mi ERP con la informacion del CFD.
Asi que preferi hacer un programa Aubit4gl que lea la base de datos y que genere un reporte en el archivo de texto de informe mensual.

Warning: file_get_contents(report.4gl) [function.file-get-contents]: failed to open stream: No such file or directory in /home/httpd/www/fortiz/sat/xsl.php on line 230

Warning: Invalid argument supplied for foreach() in /usr/share/pear/Text/Highlighter/Renderer/Html.php on line 310
1