En el artículo anterior titulado Convirtiendo facturas electrónicas del XML a PDF describo el proceso para crear una representación impresa de una factura electrónica en la versión 2.0, basado en el famoso anexo 20 del SAT.
Ahora les dejo el código fuente del programa para la versión 2.2 (requiere aplicar el archivo de transformación cadenaoriginal_2_2.xslt), que debe usarse a partir del 1o de julio de este año…
#!/usr/bin/perl #===================================================================# # Program => CFD_to_PDF_2_2.pl (In Perl 5.0) version 0.0.1 # #===================================================================# # Autor => Fernando "El Pop" Romo (pop@cofradia.org) # # Creation date => 01/jul/2012 # #-------------------------------------------------------------------# # Info => This program take a CFD file in xml format and convert to # # PDF using a template # #-------------------------------------------------------------------# # (c) 2012 - Fernando Romo # #-------------------------------------------------------------------# # Release under the GNU/GPL License v3.0 # #===================================================================# # Load Modules use strict; use PDF::Reuse; use Lingua::ES::Numeros ":constants"; use XML::Simple; use Text::Wrap::Smart qw(wrap_smart); my $file = $ARGV[0]; my $Fac = XMLin($file); $file =~ s/.xml$//g; prFile ( { Name => "$file.pdf", HideToolbar => 1, # 1 or 0 HideMenubar => 1, # 1 or 0 HideWindowUI => 1, # 1 or 0 FitWindow => 1, # 1 or 0 CenterWindow => 1 } ); # 1 or 0 prForm ( { file => '/Users/pop/SAT/util/factura_clean_incuvox_2_2.pdf', # template file page => 1, # page number (of imported template) adjust => 0, # try to fill the media box effect => 0, # action to be taken tolerant => 1, # continue even with an invalid form x => -13, # $x points from the left y => 49, # $y points from the bottom rotate => 0, # rotate size => 1, # multiply everything by $size xsize => 1, # multiply horizontally by $xsize ysize => 1, } ); # multiply vertically by $ysize prFont('Helvetica'); # Moneda my $Sufijo = 'PESOS'; my $Moneda = 'M.N.'; if (exists($Fac->{Moneda})) { if ($Fac->{Moneda} eq 'MXN') { $Sufijo = 'PESOS'; $Moneda = 'M.N.'; } else { $Moneda = "$Fac->{Moneda}"; $Sufijo = ''; } } sub Escape_UTF8 { my @out = @_; for (@out) { s/á/\x{e1}/g; s/é/\x{e9}/g; s/í/\x{ed}/g; s/ó/\x{f3}/g; s/ú/\x{fa}/g; s/Á/\x{c1}/g; s/É/\x{c9}/g; s/Í/\x{cd}/g; s/Ó/\x{d3}/g; s/Ú/\x{da}/g; s/ñ/\x{f1}/g; s/Ñ/\x{d1}/g; } return wantarray ? @out : $out[0]; } sub Wrap_Text { my ($ref_string, $col, $row, $width, $font_size, $space, $split) = @_; my @text = wrap_smart($$ref_string, { no_split => $split, max_msg_size => $width,} ); prFontSize ($font_size); foreach my $linea (@text) { prText( $col, $row, $linea, 'left'); $row = $row - $space; } return $row + $space; } sub Dinero { my ($number) = @_; my $dec_pos = 2; if ($number =~ /\./) { my $len = length($number); $dec_pos = rindex($number,"\."); $dec_pos = $len - $dec_pos - 1 ; } my $number = sprintf("%.$dec_pos".'f', $number); 1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/; $number =~ s/^(-?)/$1\$/; return $number; } sub Cantidad { my $number = sprintf "%.2f", shift @_; 1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/; return $number; } sub Parte { my ($ref_parte, $row) = @_; my %Pedimentos = (); my @SinPedimento = (); my $parte_aux = 'N.S.: '; if (ref($$ref_parte) eq 'ARRAY') { for (my $i=0; $i <= (@{ $$ref_parte } - 1) ; $i++) { if (exists($$ref_parte->[$i]->{'cfd:InformacionAduanera'})) { $Pedimentos{ $$ref_parte->[$i]->{'cfd:InformacionAduanera'}{numero} }{aduana} = $$ref_parte->[$i]->{'cfd:InformacionAduanera'}{aduana}; $Pedimentos{ $$ref_parte->[$i]->{'cfd:InformacionAduanera'}{numero} }{fecha} = $$ref_parte->[$i]->{'cfd:InformacionAduanera'}{fecha}; push @{ $Pedimentos{ $$ref_parte->[$i]->{'cfd:InformacionAduanera'}{numero} }{serie} }, $$ref_parte->[$i]->{noIdentificacion}; } else { push @SinPedimento, $$ref_parte->[$i]->{noIdentificacion}; } } foreach my $numero (sort keys %Pedimentos) { $parte_aux = 'N.S.: '; $row = $row - 8; $row = Wrap_Text(\"Pedimento: $numero, Aduana: $Pedimentos{$numero}{aduana}, Fecha: $Pedimentos{$numero}{fecha}",85,$row,70,6,8,1); for (my $x=0; $x <= (@{ $Pedimentos{$numero}{serie} } - 1) ; $x++) { $parte_aux .= "$Pedimentos{$numero}{serie}[$x], "; } $parte_aux =~ s/, $//; $row = $row - 8; $row = Wrap_Text(\$parte_aux,85,$row,70,6,8,1); } if ($#SinPedimento >= 0) { $parte_aux = 'N.S.: '; foreach my $serie (sort @SinPedimento) { $parte_aux .= $serie . ', '; } $parte_aux =~ s/, $//; $row = $row - 8; $row = Wrap_Text(\$parte_aux,85,$row,70,6,8,1); } } else { if (exists($$ref_parte->{'cfd:InformacionAduanera'})) { $row = $row - 8; my $aduana_aux = 'Pedimento: ' . $$ref_parte->{'cfd:InformacionAduanera'}{numero} . ', Aduana: ' . $$ref_parte->{'cfd:InformacionAduanera'}{aduana} . ', fecha: ' . $$ref_parte->{'cfd:InformacionAduanera'}{fecha}; $row = Wrap_Text(\$aduana_aux,85,$row,70,6,8,1); } if (exists($$ref_parte->{noIdentificacion})) { $row = $row - 8; $parte_aux .= $$ref_parte->{noIdentificacion}; $row = Wrap_Text(\$parte_aux,85,$row,70,6,8,1); } } return $row; } sub Concepto { my ($ref_concepto, $row) = @_; if (ref($$ref_concepto) eq 'ARRAY') { for (my $i=0; $i <= (@{ $$ref_concepto } - 1) ; $i++) { prFontSize (8); prText( 18, $row, $$ref_concepto->[$i]->{noIdentificacion}, 'left'); prText( 375, $row, Cantidad($$ref_concepto->[$i]->{cantidad}), 'right'); prText( 477, $row, Dinero($$ref_concepto->[$i]->{valorUnitario}) . " $Moneda", 'right'); prText( 580, $row, Dinero($$ref_concepto->[$i]->{importe}) . " $Moneda", 'right'); $row = Wrap_Text(\$$ref_concepto->[$i]->{descripcion},85,$row,60,8,10,1); if (exists($$ref_concepto->[$i]->{'cfd:Parte'})) { $row = Parte(\$$ref_concepto->[$i]->{'cfd:Parte'},$row); } $row = $row - 10; } } else { prFontSize (8); prText( 18, $row, $$ref_concepto->{noIdentificacion}, 'left'); prText( 375, $row, Cantidad($$ref_concepto->{cantidad}), 'right'); prText( 477, $row, Dinero($$ref_concepto->{valorUnitario}) . " $Moneda", 'right'); prText( 580, $row, Dinero($$ref_concepto->{importe}) . " $Moneda", 'right'); $row = Wrap_Text(\$$ref_concepto->{descripcion},85,$row,55,8,10,1); if (exists($$ref_concepto->{'cfd:Parte'})) { $row = Parte(\$$ref_concepto->{'cfd:Parte'},$row); } $row = $row - 10; } return $row; } # Datos del CFD prFontSize (10); prText( 140, 793, "$Fac->{noCertificado}", 'left'); prText( 140, 780, "$Fac->{noAprobacion}", 'left'); prText( 300, 780, "$Fac->{anoAprobacion}", 'left'); prText( 140, 769, "$Fac->{tipoDeComprobante}",, 'left'); prText( 275, 768, "$Fac->{fecha}",, 'left'); prText( 140, 757, "$Fac->{formaDePago}", 'left'); prText( 140, 745, "$Fac->{metodoDePago}", 'left'); prFontSize (16); prText( 140, 728, "$Fac->{serie}-$Fac->{folio}", 'left'); # Emisor prFontSize (10); prFont('Helvetica-Bold'); prText( 402, 758, "$Fac->{'cfd:Emisor'}{nombre}", 'left'); prFont('Helvetica'); prFontSize (8); prText( 402, 746, "$Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{calle} $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{noExterior} $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{noInterior}", 'left'); prText( 402, 736, "Col. $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{colonia} C.P. $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{codigoPostal}", 'left'); prText( 402, 726, "$Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{municipio}, $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{estado}", 'left'); prText( 402, 716, "$Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{localidad}, $Fac->{'cfd:Emisor'}{'cfd:DomicilioFiscal'}{pais}", 'left'); prText( 402, 706, 'info@incuvox.com', 'left'); prText( 480, 706, 'http://incuvox.com', 'left'); prText( 402, 696, 'Tel. +52(55)4162-5800', 'left'); prFontSize (10); prText( 402, 684, "R.F.C.:", 'left'); prFont('Helvetica-Bold'); prText( 437, 684, "$Fac->{'cfd:Emisor'}{rfc}", 'left'); prFont('Helvetica'); # Lugar de emision prFontSize (8); prText( 402, 650, "$Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{calle} $Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{noExterior} $Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{noInterior}", 'left'); prText( 402, 640, "$Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{colonia}, C.P. $Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{codigoPostal}", 'left'); prText( 402, 630, "$Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{municipio}, $Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{estado}", 'left'); prText( 402, 620, "$Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{localidad}, $Fac->{'cfd:Emisor'}{'cfd:ExpedidoEn'}{pais}", 'left'); # Facturado A prFontSize (10); prFont('Helvetica-Bold'); prText( 20, 690, "$Fac->{'cfd:Receptor'}{nombre}", 'left'); prFont('Helvetica'); prText( 20, 678, "$Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{calle} $Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{noExterior} $Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{noInterior}" , 'left'); prText( 20, 666, "$Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{colonia}, C.P. $Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{codigoPostal}", 'left'); prText( 20, 654, "$Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{municipio}, $Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{localidad}", 'left'); prText( 20, 642, "$Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{estado}, $Fac->{'cfd:Receptor'}{'cfd:Domicilio'}{pais}", 'left'); prText( 20, 620, "R.F.C.:", 'left'); prFont('Helvetica-Bold'); prText( 56, 620, "$Fac->{'cfd:Receptor'}{rfc}", 'left'); prFont('Helvetica'); # partidas my $row = Concepto(\$Fac->{'cfd:Conceptos'}{'cfd:Concepto'},579); # Addenda if (exists($Fac->{'cfd:Addenda'}{Incuvox}{Note})) { prFont('Helvetica-Oblique'); my $nota = 'Nota: ' . $Fac->{'cfd:Addenda'}{Incuvox}{Note}; Wrap_Text(\$nota,85,($row - 3),80,8,10,1); prFont('Helvetica'); } # Numero en letras my ( $pesos, $centavos ) = split( /\./, $Fac->{total} ); $pesos = 0 unless( $pesos ); $centavos = 0 unless( $centavos ); my $obj = new Lingua::ES::Numeros ('MAYUSCULAS' => 1, ACENTOS => 0); my $letrero = $obj->cardinal($pesos) . " $Sufijo " . "$centavos/100 $Moneda\n"; Wrap_Text(\$letrero,65,314,70,8,10,1); # Totales prFontSize (8); prText( 580, 314, Dinero($Fac->{subTotal}) . " $Moneda", 'right'); prText( 580, 303, Dinero($Fac->{descuento}) . " $Moneda", 'right'); prText( 458, 292, '16', 'right'); prText( 580, 292, Dinero($Fac->{'cfd:Impuestos'}{totalImpuestosTrasladados}) . " $Moneda", 'right'); prFont('Helvetica-Bold'); prText( 580, 281, Dinero($Fac->{total}) . " $Moneda", 'right'); prFont('Helvetica'); # Cadena Original prFont('Courier'); my $cadena = Escape_UTF8(qx(xsltproc ~/SAT/xslt/cadenaoriginal_2_2.xslt $file.xml)); Wrap_Text(\$cadena,20,252,155,6,8,0); # Sello Wrap_Text(\$Fac->{sello},75,100,130,8,10,0); # Verifica si esta cancelada y pone sello if (exists($Fac->{'cfd:Addenda'}{Incuvox}{Factura}{Status})) { if (lc($Fac->{'cfd:Addenda'}{Incuvox}{Factura}{Status}) eq 'cancelada') { prAdd("q\nBT\n/" . prFont('Courier') . " 2 Tf\n40 45 -45 40 100 160 Tm\n1 0 0 rg\n(CANCELADO) Tj\nET\nQ\n"); prAdd("q\nBT\n/" . prFont('Courier') . " .3 Tf\n40 45 -45 40 190 220 Tm\n1 0 0 rg\n(No puede ser usado como comprobante fiscal) Tj\nET\nQ\n"); } } prEnd(); |
Esto procesa un XML como este:
<?xml version="1.0" encoding="UTF-8"?> <cfd:Comprobante xmlns:cfd="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/cfdv22.xsd" version="2.2" serie="Falsa" folio="666" fecha="2012-07-01T00:13:32" sello="1LBn6TRoUuDN5J9rgdrCg3hKYPMU8qT+USefiDQU3ig4vFF25c8KgfSiaLuMEE4bzeOuXj5C1OMJjHgRcVuC1//Tv3N9GMMQYstg+OIANhtx5fobxqobbDBgLXNlc+7AqM6OjOM2vk6J8iLyAF+esdPbmE/XUDRneWajl09Lmr0=" noAprobacion="263844" anoAprobacion="2010" formaDePago="Contado en una sola exibición" tipoDeComprobante="ingreso" noCertificado="00001000000102302080" certificado="MIIECTCCAvGgAwIBAgIUMDAwMDEwMDAwMDAxMDIzMDIwODAwDQYJKoZIhvcNAQEFBQAwggE2MTgwNgYDVQQDDC9BLkMuIGRlbCBTZXJ2aWNpbyBkZSBBZG1pbmlzdHJhY2nDs24gVHJpYnV0YXJpYTEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3RyYWNpw7NuIFRyaWJ1dGFyaWExHzAdBgkqhkiG9w0BCQEWEGFjb2RzQHNhdC5nb2IubXgxJjAkBgNVBAkMHUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDELMAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEzARBgNVBAcMCkN1YXVodGVtb2MxMzAxBgkqhkiG9w0BCQIMJFJlc3BvbnNhYmxlOiBGZXJuYW5kbyBNYXJ0w61uZXogQ29zczAeFw0xMDEyMDMyMzA4MTNaFw0xMjEyMDIyMzA4MTNaMIGpMRkwFwYDVQQDExBJTkNVVk9YIFNBIERFIENWMRkwFwYDVQQpExBJTkNVVk9YIFNBIERFIENWMRkwFwYDVQQKExBJTkNVVk9YIFNBIERFIENWMSUwIwYDVQQtExxJTkMxMDExMDlRMzYgLyBST0FGNjQxMjE3Szk0MR4wHAYDVQQFExUgLyBST0FGNjQxMjE3SERGTUxSMDgxDzANBgNVBAsTBk1hdHJpejCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2UQW4gKh5RBbYXKvkYcr5gEtp5R0QVVeHsMNlfOdhByXZKMWH6rE+wSlTrCAmOigy8fUJpw7Tokx8FQvGHZOWqwHjrr0Nw274MHMwAX8d0++xvhrJbAonCFCUkGGHgMwxrSVm9Fib5t6sxaZkN4N3NLkV80SJxXn0a2p+ux3zIcCAwEAAaMdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwDQYJKoZIhvcNAQEFBQADggEBACrnr14H3QbM4Gp52HNdkqvdvH8ByWqe9C532g2aT8g1Ft9HL8p25x5DSX+SjtzKwtwVo97/M1y+8Xpfw6tfG0iKc5XPoKv9UnRr4Yy3VnMiL0Wdjj874ywQ/o0X+zn7VlNMW4PBfkI3W82ms+EKavSgUIO1L7cwidwFiektkb37D3HOei66076QCwp5NZ53yLISpftdmzYGjvbSPwhILIwiDcN95vKOR9WqC4g/zN+CQnn4cF9CYyNRvamz46D1/iltU0QI9ho6BjrpRPH9KmM3oYiC4p+FhvvJBHy32Sxzmu1vMh/b4dPwP/4yL5vp50KqxdjdgFtcbW48nAYE4/8=" metodoDePago="Depósito en cuenta o compensación de documentos" Moneda="USD" TipoCambio="13.45" subTotal="4797.1012" descuento="0" total="5564.64" LugarExpedicion="México, D.F."> <cfd:Emisor rfc="INC101109Q36" nombre="Incuvox, S.A. de C.V."> <cfd:DomicilioFiscal calle="José Enrique Pestalozzi" noExterior="837-A" colonia="Narvarte Poniente" localidad="Distrito Federal" referencia="Esquina con Concepción Beistegui" municipio="Benito Juárez" estado="Distrito Federal" pais="México" codigoPostal="03020"/> <cfd:ExpedidoEn calle="José Enrique Pestalozzi" noExterior="837-A" colonia="Narvarte Poniente" localidad="Distrito Federal" municipio="Benito Juárez" estado="Distrito Federal" pais="México" codigoPostal="03020"/> <cfd:RegimenFiscal Regimen="Persona Moral, Régimen General"/> </cfd:Emisor> <cfd:Receptor rfc="JCC960111P76" nombre="Juan Camanei Company, S.A. de C.V."> <cfd:Domicilio calle="Av. Insurgentes Sur" noExterior="1600" noInterior="301" colonia="Guadalupe Inn" localidad="Distrito Federal" municipio="Álvaro Obregón" estado="Distrito Federal" pais="México" codigoPostal="01020"/> </cfd:Receptor> <cfd:Conceptos> <cfd:Concepto cantidad="1.00" unidad="pz" noIdentificacion="A104DE" descripcion="Tarjeta Sangoma A104De AFT para 4 x T1/E1 con cancelador de eco" valorUnitario="2429.10" importe="2429.10"> <cfd:Parte cantidad="1.00" noIdentificacion="104E4DM-10414" descripcion="Tarjeta Sangoma A104De AFT para 4 x T1/E1 con cancelador de eco"> <cfd:InformacionAduanera numero="3817-2000166" fecha="2012-03-15" aduana="México"/> </cfd:Parte> </cfd:Concepto> <cfd:Concepto cantidad="4.00" unidad="pz" noIdentificacion="BANEG703" descripcion="Convertidor de Impedancia de 75 a 120 Ohms con conector RJ45 a CCITT G703" valorUnitario="70.00" importe="280.00"> <cfd:Parte cantidad="1.00" noIdentificacion="NEO-B1267" descripcion="Convertidor de Impedancia de 75 a 120 Ohms con conector RJ45 a CCITT G703"> <cfd:InformacionAduanera numero="3646-0001935" fecha="2010-07-05" aduana="México"/> </cfd:Parte> <cfd:Parte cantidad="1.00" noIdentificacion="NEO-B1268" descripcion="Convertidor de Impedancia de 75 a 120 Ohms con conector RJ45 a CCITT G703"> <cfd:InformacionAduanera numero="3646-0001935" fecha="2010-07-05" aduana="México"/> </cfd:Parte> <cfd:Parte cantidad="1.00" noIdentificacion="NEO-B1271" descripcion="Convertidor de Impedancia de 75 a 120 Ohms con conector RJ45 a CCITT G703"> <cfd:InformacionAduanera numero="3646-0001935" fecha="2010-07-05" aduana="México"/> </cfd:Parte> <cfd:Parte cantidad="1.00" noIdentificacion="NEO-B1272" descripcion="Convertidor de Impedancia de 75 a 120 Ohms con conector RJ45 a CCITT G703"> <cfd:InformacionAduanera numero="3646-0001935" fecha="2010-07-05" aduana="México"/> </cfd:Parte> </cfd:Concepto> <cfd:Concepto cantidad="1.00" unidad="pz" noIdentificacion="D100-060E" descripcion="Tarjeta Sangoma D100-060 para transcoding de 60 puertos" valorUnitario="787.50" importe="787.50"> <cfd:Parte cantidad="1.00" noIdentificacion="D100-60E-00125" descripcion="Tarjeta Sangoma D100-060 para transcoding de 60 puertos"> <cfd:InformacionAduanera numero="3646-1001788" fecha="2011-06-16" aduana="México"/> </cfd:Parte> </cfd:Concepto> <cfd:Concepto cantidad="1.00" unidad="pz" noIdentificacion="D1X0-UPG-340" descripcion="Upgrade de 60 a 400 puertos para Tarjeta Sangoma D100-060E" valorUnitario="1300.5012" importe="1300.5012"> </cfd:Concepto> </cfd:Conceptos> <cfd:Impuestos totalImpuestosTrasladados="767.54"> <cfd:Traslados> <cfd:Traslado impuesto="IVA" tasa="16.00" importe="767.54"/> </cfd:Traslados> </cfd:Impuestos> <cfd:Addenda> <Incuvox xmlns="http://incuvox.com/cfd" xsi:schemaLocation="http://incuvox.com/cfd http://incuvox.com/cfd/addenda.xsd"> <Factura Status="Emitida"/> <Contacto Nombre="Juan Camanei" eMail="juan@camanei.com.mx" Telefono="(55)53506500"/> <Note>Equipo solicitado por Juan Camanei</Note> </Incuvox> </cfd:Addenda> </cfd:Comprobante> |
Los cambios importantes son el uso del prefijo “cfd:” en los tags de XML que usa la factura electrónica, así como la inclusión de los campos “Moneda”, “TipoCambio”, “metodoDePago” y en el emisor el campo “Regimen”. En el artículo anterior describo las dependencias de este programa.
Autor: Fernando “El Pop” Romo (pop at cofradia.org), twitter @El_Pop
Si este artículo te es útil, ayúdanos con un donativo de 100.00 MXN para poder seguir operando:
En CFDI, el prefijo CFDI esta claro tanto el XSD: http://www.sat.gob.mx/cfd/3/cfdv32.xsd
como en el XSLT: http://www.sat.gob.mx/sitio_internet/cfd/3/cadenaoriginal_3_0/cadenaoriginal_3_2.xslt
Pero para CFD, en el XLST, si viene el prefijo CFD: http://www.sat.gob.mx/sitio_internet/cfd/2/cadenaoriginal_2_2/cadenaoriginal_2_2.xslt
pero en el XSD no: http://www.sat.gob.mx/sitio_internet/cfd/2/cfdv22.xsd
El validador del SAT: https://www.consulta.sat.gob.mx/SICOFI_WEB/ModuloECFD_Plus/ValidadorComprobantes/Validador.asp
pasa correctamente la validación con y sin prefijo CFD, pero muchos validadores que están pululando por ahí, incluyendo los de varios que se creen dueños de la verdad en cuestión de facturacíon electrónica.
Lo menciono para que tomen sus precauciones.
Saludos