Libreria Para facturacion electronica en Delphi
#1
Escrito 02 diciembre 2010 - 11:43
La librería fue escrita, desde cero y de manera nativa para Delphi, compatible desde su versión 2007 hasta Delphi XE aprovechando las ventajas de la programación orientada a objetos que Delphi ofrece, además de contener ayuda a través de Code Insight.
http://www.bambucode...nica_delphi.php
#2
Escrito 02 diciembre 2010 - 01:31
Salud OS
#3
Escrito 02 diciembre 2010 - 01:51
#4
Escrito 02 diciembre 2010 - 03:20
#5
Escrito 02 diciembre 2010 - 05:05
#6
Escrito 31 enero 2011 - 10:58
Tendrán alguna imagen del formato de la factura electrónica.La recomiendo, fue la que utilice en mi sistemas para la generación de los CFD.
Actualmente estoy trabajando con un modulo de facturación electrónica de Compaqi pero no me ha acabado de convencer.
#7
Escrito 09 agosto 2011 - 07:32
Soporte los nuevos esquemas de CDFI y el de codigo de barra CBB?
Gracias de antemano
#8
Escrito 12 agosto 2011 - 09:49
UPDATE: Es para Mejico... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?
En cada pais se piden cosas diferentes, en españa por ejemplo hay un formato de factura electronica especifico para facturarle a la administracion publica que usa XML, pero en la web del link no indica nada sobre pais, aunque las siglas que aparecen no me suenan de nada, asi que imagino que de españa no son.
Otro detalle: Vi en unas capturas de pantalla que utiliza como algoritmo de hash MD5 o SHA1... el MD5 esta muerto a nivel de seguridad, no lo useis o vuestra factura sera hackeable realtivamente facil (podran crear una factura falsa, pegarle la firma de la factura original, y que no se note en absoluto). El SHA1 esta moribundo, pero es el mas usado... igual el fin del mundo en 2012 es porque terminan de romper el algoritmo del SHA1 y el dinero electronico se hace facilmente hackeable de un dia para otro!
Aparte de estas cosillas que os pongo, es una gran noticia que exista esto, y si tengo tiempo aportare el codigo necesario para generar el XML español FacturaE, aunque como yo esto ya lo resolvi por otro lado, quizas sea mas practico dejar mi codigo en sus foros por si alguien necesita hacer la adaptacion al facturae.
#9
Escrito 11 septiembre 2013 - 03:01
Esta semana me ha surgido la necesidad de crear facturas XML en formato FACTURAE (para España)
http://www.facturae....inas/Index.aspx
Sergio, ¿cómo lo resolviste?, ¿Puedes poner tu código?
Muchas gracias.
#10
Escrito 12 septiembre 2013 - 03:10
Esta semana me ha surgido la necesidad de crear facturas XML en formato FACTURAE (para España)
http://www.facturae....inas/Index.aspx
Sergio, ¿cómo lo resolviste?, ¿Puedes poner tu código?
Bueno, tienes tres tareas, así que te pongo tres trozos de código: Crear en XML la cabecera (datos del emisor y receptor, total de las facturas incluidas, etc), luego añadir cada una de las facturas (una o varias) con todos los datos obligatorios correctos y esas cosas (dentro van las líneas de factura), y finalmente tienes el tema de firmarlo como te piden.
Empiezo con código para el XML de cabecera, usamos un objetito simple que nos abre y cierra ramas en el XML, hay muchas clases parecidas y todas son sencillas de usar, pero nos creamos la nuestra porque... pues porque sí.
Veras algunas variables que no se definen, son parte del objeto que usamos, pero los nombres están claros: "total" es el toal de las facturas, etc.
salida:= TXMLGenerador.create; QP:= TQueryPack.Create(Self); // Leo los datos de la empresa emisora: QP.EjecutaSQL('select direccion, CodigoPostal, Poblacion, cif, '+ '(select p.nombre from Provincia p '+ ' where p.provincia=substring(codigopostal from 1 for 2)) '+ ' from empresa where empresa=1'); salida.TextoLibre('<?xml version="1.0" encoding="ISO-8859-1"?>'); salida.Abre('fe:Facturae','xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); salida.Abre('FileHeader'); salida.Valor('SchemaVersion','3.2'); salida.Valor('Modality','I'); salida.Valor('InvoiceIssuerType','EM'); salida.Abre('Batch'); salida.Valor('BatchIdentifier', BatchID); salida.Valor('InvoicesCount',IntToStr(count)); salida.Abre('TotalInvoicesAmount'); salida.Valor('TotalAmount',FloatToSQL(Total)); salida.Cierra; salida.Abre('TotalOutstandingAmount'); salida.Valor('TotalAmount',FloatToSQL(Total)); salida.Cierra; salida.Abre('TotalExecutableAmount'); salida.Valor('TotalAmount',FloatToSQL(Total)); salida.Cierra; salida.Valor('InvoiceCurrencyCode','EUR'); salida.Cierra; salida.Cierra; // Partes salida.Abre('Parties'); // Parte vendedora salida.Abre('SellerParty'); salida.Abre('TaxIdentification'); salida.Valor('PersonTypeCode','J'); salida.Valor('ResidenceTypeCode','R'); salida.Valor('TaxIdentificationNumber',NifEmpresa); if not DNIValido(NifEmpresa) then result:= result + iif(result='','',', ')+'NIF "'+Trim(NifEmpresa)+'" inválido en la empresa'; salida.Cierra; salida.Abre('LegalEntity'); salida.Valor('CorporateName',Base.Empresa); salida.Abre('AddressInSpain'); salida.Valor('Address',QP[0].asString); salida.Valor('PostCode',QP[1].asString); if Length(Trim(QP[1].asString))<>5 then result:= result + iif(result='','',', ')+'Código postal de la empresa "'+Trim(QP[1].asString)+'" no es válido'; salida.Valor('Town',QP[2].asString); salida.Valor('Province',QP[4].asString); salida.Valor('CountryCode','ESP'); salida.Cierra; salida.Cierra; salida.Cierra; // Parte compradora QP.EjecutaSQL('select Nif, Nombre, direccion, cpostal, poblacion, '+ '(select p.nombre from Provincia p '+ ' where p.provincia=substring(cpostal from 1 for 2)), cliente '+ 'from cliente where Cliente='+IntToStr(Factura(0).Cliente)); salida.Abre('BuyerParty'); salida.Abre('TaxIdentification'); salida.Valor('PersonTypeCode','J'); salida.Valor('ResidenceTypeCode','R'); salida.Valor('TaxIdentificationNumber',QP[0].asString); if not DNIValido(Trim(QP[0].asString)) then result:= result + iif(result='','',', ')+'NIF "'+Trim(QP[0].asString)+'" invalido en el cliente nº'+QP[6].asString+' "'+Trim(QP[0].asString)+'"'; salida.Cierra; salida.Abre('LegalEntity'); salida.Valor('CorporateName',QP[1].asString); salida.Abre('AddressInSpain'); salida.Valor('Address',QP[2].asString); salida.Valor('PostCode',QP[3].asString); if Length(Trim(QP[3].asString))<>5 then result:= result + iif(result='','',', ')+'Código postal del cliente nº'+QP[6].asString+' "'+Trim(QP[3].asString)+'" no es válido'; salida.Valor('Town',QP[4].asString); salida.Valor('Province',QP[5].asString); salida.Valor('CountryCode','ESP'); salida.Cierra; salida.Cierra; salida.Cierra; // Se acaban las partes salida.Cierra; // Empezamos con las facturas salida.Abre('Invoices'); for i:= 0 to count-1 do Factura(i).SaveAsXML(salida); //Ver en el siguiente trozo de codigo // Se acabarón las facturas Salida.Cierra; //Se acabo el facturae Salida.Cierra; //A grabarse! salida.SaveToStream(Stream); if Base.FirmaDigital.Activa then Base.FirmaDigital.FirmaStream(TStream(stream), '', tfXML); //Ver parte 3 de codigo result:= iif(result='','',result+'.'); salida.Free; QP.Free;
Por cierto, el formato XML para floats es el americano.
Bueno, ahora como añadir cada factura:
procedure TXMLFacturaE.SaveAsXML(face: TXMLGenerador); var tipoiva, cuota: double; obra, refcli, fecvto: string; QP: TQueryPack; function Fecha(fecha: TDateTime): string; var d,m,a: word; begin DecodeDate(fecha,a,m,d); result:= IntToStr(a)+'-'+IntCero(m,2)+'-'+IntCero(d,2); end; function Descripcion: string; begin if QP[0].asInteger = 1 then result:= QP[2].asString else result:= QP[1].asString+' '+QP[2].asString; end; begin inherited; QP:= TQueryPack.Create(Self); QP.EjecutaSQL('select f.FacturaTxt, f.factura, f.ejercicio, f.serie'+ ',f.fecha,i.base, i.total, f.tipoiva, i.IVA '+ ',f.obra, o.referenciacliente, f.fechavence '+ ' from factura f '+ ' left join obra o on o.obra=f.obra '+ ' left join FacturaImportes('+IDFacturaStr+') i on 1=1 '+ ' where f.idfactura='+IDFacturaStr); facE.Abre('Invoice'); facE.Abre('InvoiceHeader'); facE.Valor('InvoiceNumber',QP[1].AsString); facE.Valor('InvoiceSeriesCode',SerieEjercicio); facE.Valor('InvoiceDocumentType','FC'); facE.Valor('InvoiceClass','OO'); facE.Cierra; facE.Abre('InvoiceIssueData'); facE.Valor('IssueDate',fecha(QP[4].asDateTime)); facE.Valor('InvoiceCurrencyCode','EUR'); facE.Valor('TaxCurrencyCode','EUR'); facE.Valor('LanguageName','es'); facE.Cierra; // Impuestos facE.Abre('TaxesOutputs'); facE.Abre('Tax'); facE.Valor('TaxTypeCode','01'); facE.Valor('TaxRate',format('%1.2f',[QP[7].asDouble])); facE.Abre('TaxableBase'); facE.Valor('TotalAmount',format('%1.2f',[QP[5].asDouble])); facE.Cierra; facE.Abre('TaxAmount'); facE.Valor('TotalAmount',format('%1.2f',[QP[8].asDouble])); facE.Cierra; facE.Cierra; facE.Cierra; // Totales facE.Abre('InvoiceTotals'); facE.valor('TotalGrossAmount',format('%1.2f',[QP[5].asDouble])); facE.valor('TotalGrossAmountBeforeTaxes',format('%1.2f',[QP[5].asDouble])); facE.valor('TotalTaxOutputs',format('%1.2f',[QP[8].asDouble])); // es obligatorio en f 3.2 facE.Valor('TotalTaxesWithheld','0.00'); facE.valor('InvoiceTotal',format('%1.2f',[QP[6].asDouble])); facE.valor('TotalOutstandingAmount',format('%1.2f',[QP[6].asDouble])); // es obligatorio en f 3.2 facE.abre('AmountsWithheld'); facE.Valor('WithholdingReason','Retencion'); facE.Valor('WithholdingRate','0.0000'); facE.Valor('WithholdingAmount','0.00'); facE.Cierra; facE.valor('TotalExecutableAmount',format('%1.2f',[QP[6].asDouble])); facE.Cierra; tipoiva:= QP[7].asDouble; obra:= QP[9].asString; refcli:= QP[10].asString; fecvto:= fecha(QP[11].AsDate); // Lineas de factura facE.Abre('Items'); QP.EjecutaSQL('select SinDescripcion,Descripcion,Notas, '+ ' unidades, precio, cast(unidades*precio as numeric(15,2)),l.ensayo '+ ' from FacturaLineas('+IDFacturaStr+') l'); while not QP.Eof do begin cuota:= QP[5].AsDouble - Redondeo(QP[5].AsDouble/(1+(tipoiva/100)),2); facE.Abre('InvoiceLine'); facE.valor('IssuerContractReference',Obra); if RefCli <> '' then facE.valor('ReceiverContractReference',RefCli,20); facE.valor('ItemDescription', Descripcion); facE.valor('Quantity',format('%1.2f',[QP[3].AsDouble])); facE.valor('UnitOfMeasure','01'); facE.valor('UnitPriceWithoutTax',format('%1.6f',[QP[4].AsDouble])); facE.valor('TotalCost',format('%1.6f',[QP[5].AsDouble])); facE.valor('GrossAmount',format('%1.6f',[QP[5].AsDouble])); facE.Abre('TaxesOutputs'); facE.Abre('Tax'); facE.valor('TaxTypeCode','01'); facE.valor('TaxRate',format('%1.2f',[tipoiva])); facE.Abre('TaxableBase'); facE.valor('TotalAmount',format('%1.2f',[QP[5].AsDouble-cuota])); facE.Cierra; facE.Abre('TaxAmount'); facE.valor('TotalAmount',format('%1.2f',[cuota])); facE.Cierra; facE.Cierra; facE.Cierra; facE.valor('ArticleCode',trim(QP[6].AsString)); facE.Cierra; QP.Next; end; facE.Cierra; // Condiciones de pago No es obligatorio facE.Abre('PaymentDetails'); facE.Abre('Installment'); facE.valor('InstallmentDueDate', fecvto); facE.valor('InstallmentAmount',format('%1.2f',[FTotal])); facE.valor('PaymentMeans','01'); facE.Cierra; facE.Cierra; facE.Cierra; QP.Free; end;
Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!
Por cierto, en su día publiqué en sus foros este mismo código, y en las versiones actuales de la librería tienes ya la opción de firmar en formato FacturaE basado en ese post, así que seguramente te puedas ahorrar todo este código, aunque yo lo prefiero así, sé lo que hace y porqué lo hace.
Veras que el código tiene muchos comentarios, es porque la información oficial está llena de errores y huecos, y solo mirando los ejemplos por dentro y probando con los validadores -que tampoco erán muy fiables cuando los probé- consegui que todo funcionase.
case TipoFirma of tfXML: //Fichero XML para FacturaE (con politica de firmado V3.1) begin // ************************ // ** Firmar fichero XML ** // ************************ // //Usamos FacturaE politica de firma V3.1: //http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf // //Creo objetos involucrados en la firma... XML_Doc:= ElXMLDOMDocument.create; //Documento XML a ser firmado XML_Refs:= TElXMLReferenceList.create; //Lista de nodos a ser firmados XML_RefDocu:= TElXMLReference.create; //Nodo que representa al XML completo XML_RefCert:= TElXMLReference.create; //Nodo del certificado utilizado XML_Signer:= TElXMLSigner.Create(nil); //Objeto firmador en XML... XML_XAdES:= TElXAdESSigner.Create(nil); //Subjeto firmador con info adicional XAdES XML_KeyData:= TElXMLKeyInfoX509Data.create(false); //Certificado a utilizar... try //Leo el fichero XML //================== // Por defecto en UTF-8 y normalizando los finales de linea (si llegan // CR+LF entonces la firma no se validará luego pues los digest no // deberían incluir estos CR+LF). XML_Doc.LoadFromStream(MS, '', true); if not XML_Doc.Loaded then raise Exception.create('Firma XML: No se pudo cargar el documento XML.'); //Configuro el objeto firmador de XML //=================================== // Se han de firmar la lista de nodos referenciados en XML_Refs XML_Signer.References:= XML_Refs; // Quiero firma electronica, no claves secretas (MAC) XML_Signer.SignatureMethodType:= xmtSig; // Se firma en formato "enveloped" XML_Signer.SignatureType:= xstEnveloped; // Se canoniza -ver ejemplos de la web facturae- el XML XML_Signer.CanonicalizationMethod:= xcmCanon; // La firma con el metodo mas estandar posible XML_Signer.SignatureMethod:= xsmRSA_SHA1; // La parte publica del certificado se debe incluir XML_Signer.IncludeKey:= true; // Necesito incrustar info adicional "XAdES" (XML Advanced Electronic Signatures )... XML_XAdES:= TElXAdESSigner.Create(nil); XML_Signer.XAdESProcessor:= XML_XAdES; //Configuro la parte XAdES del objeto firmador //============================================ // // Se pide usar XAdES V1.2.2 o superior pero ha de ser "compatible" (?) // Algunos ejemplos usando V1.3.2 validan, asi que lo uso. XML_Signer.XAdESProcessor.XAdESVersion:= XAdES_v1_3_2; //SignerRole (en calidad de qué firmamos): supplier, customer o third_party // Firmamos facturas emitidas por nosotros (supplier) en este caso. // Si recibimos una factura firmada por el supplier, la podemos volver // a firmar como que la damos por recibida usando "customer". //NOTA: El validador de [url=http://www.facturae.es]www.facturae.es[/url] no comprueba este dato (2-2010) XML_Signer.XAdESProcessor.Included := [xipSignerRole]; XML_Signer.XAdESProcessor.SignerRole.ClaimedRoles.AddText( XML_Signer.XAdESProcessor.XAdESVersion, XML_Doc, 'supplier'); // La politica de firmado es la definida en el PDF V3.1 sobre firmado Facturae XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= '[url=http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf';]http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf';[/url] //NOTA: Si vas a la web, este PDF esta en otro link diferente (WTF): //'[url=http://www.facturae.es/es-ES/Documentacion/Politicas/Politicas/Versi%C3%B3n%203_1/Politica_Firma_formato_facturae_v3_1.pdf';]http://www.facturae.es/es-ES/Documentacion/Politicas/Politicas/Versi%C3%B3n%203_1/Politica_Firma_formato_facturae_v3_1.pdf';[/url] XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Description:= 'Política de firma electrónica para facturación electrónica con formato Facturae'; //Ahora el hash del propio PDF en SHA1 (en la web esta debajo del PDF)... XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= '[url=http://www.w3.org/2000/09/xmldsig#sha1';]http://www.w3.org/2000/09/xmldsig#sha1';[/url] //El digest en SHA-1 de la web (en formato hex), esta MAL, no corresponde //con ese PDF, y ademas, en su ejemplo de la V3.0 el digest es correcto //y NO se corresponde con el digest que dan! // '613c46e7bac7df5b266e6be0349b5fe8bb4944e2' ESTA MAL EN LA WEB //Uso el digest SHA1 correcto generado a partir del PDF: SetLength(XML_Buf,0); //Evito un warning tonto if not HexStr2ByteArray('3A18B197ABA90FA6AFF0DEE912F0C006110BEA13', XML_Buf) then raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.'); {NOTA: Sustituido por HexStr2ByteArray() XML_Digest:= LowerCase('3A18B197ABA90FA6AFF0DEE912F0C006110BEA13'); SetLength(XML_Buf, Length(XML_Digest) div 2); //En delphi 7 HexToBin va bien, en Delphis mas moderno, no vale porque //WideChar y pChar se tratan de forma diferente, asi que se hace a mano. //HexToBin(PChar(XML_Digest), PChar(XML_Buf), Length(XML_Digest) div 2); for j:= 0 to Length(XML_Buf)-1 do begin k1:= SBMath.HexToDecDigit(XML_Digest[j*2 + 1]); k2:= SBMath.HexToDecDigit(XML_Digest[j*2 + 2]); if (k1<0) or (k2<0) then raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.'); XML_Buf[j]:= k1 shl 4 + k2; end;} XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestValue:= XML_Buf; //Anoto la fecha y hora del firmado (del PC, no es legalmente valida) XML_Signer.XAdESProcessor.SigningTime := UtcNow; //Pre-Genero los nodos de firmado necesarios //========================================== // Esta es la parte mas "confusa": He de firmar partes del XML que no // existen hasta que este firmado ¿Como? El objeto Signer, junto con el // prepocesador XAdES, crean esos nuevos nodos para que podamos // referenciarlos -decirle al firmador que son unas referecnias a ser // firmadas- y al final, al firmar, unira el XML original con estos // nuevos nodos -formato de firma "enveloped"- y se graba el XML final. // // Primero genero la zona de informacion XAdES: XML_Signer.XAdESProcessor.Generate; // Ahora, al objeto firmador "generico" le pido lo mismo, con lo que // tambien se crea el nodo "KeyInfo" con el certificado a utilizar. XML_Signer.UpdateReferencesDigest; //Cargo certificado para firmar //============================= // Cargo el certificado a usarse en la firma en el objeto firmador // NOTA: Se incluira la parte publica del certificado en base64. // De los certificados que me pases, elijo el primero con parte privada, // ya que no quiero incluir el certificado raiz del emisor (no se // menciona en la politica de firmado y no aparece en los ejemplos): XML_KeyData.IncludeKeyValue:= true; for i:= 0 to Cajon.Count-1 do begin if Cajon.Certificates[i].PrivateKeyExists then begin XML_KeyData.certificate:= Cajon.Certificates[i]; break; end; end; //Compruebo que ha quedado algún certificado válido... if not Assigned(XML_KeyData.certificate) then raise Exception.create('FirmaXML: No se cargó un certificado válido para firmar, no contiene clave privada.'); XML_Signer.KeyData:= XML_KeyData; //Añado a la lista de nodos a firmar //================================== //NOTA: El metodo SHA1 no es el mas seguro, pero el SHA256 no se usa mucho //y puede dar problemas (WinXP SP2 no lo admite) por eso el DNIe tampoco //lo usa aun, asi que mejor dejo el valor por defecto. // // NODO 1: Se ha de firmar el documento original XML completo... // XML_RefDocu.DigestMethod:= xdmSHA1; //El mas estandard/compatible de todos XML_RefDocu.URINode:= XML_Doc.DocumentElement; //El XML completo XML_RefDocu.URI:= ''; //No hay nombre especifico para este nodo // En los ejemplos aparece la transformacion "Enveloped-Signature" // aunque en el PDF de la firma V3.1 no lo menciona. XML_RefDocu.TransformChain.Add(TElXMLEnvelopedSignatureTransform.Create); XML_Refs.Add(XML_RefDocu); //Lo sumo a las cosas a ser firmadas // Actualizo digest con este nuevo nodo. XML_Signer.UpdateReferencesDigest; // NODO 2: Se han de firmar las propiedades de firmado (XAdES).... // // El nodo SignedProperties es parte de la informacion XAdES que se // creo en XML_Signer.XAdESProcessor.Generate y se firma siempre en // el standard XAdES. Dejo el codigo abajo como referencia solo, pero // no funcionaría si se activa porque ya esta siendo firmado una vez. //XML_Signer.XAdESProcessor.QualifyingProperties.SignedProperties.ID:= 'SignedPropertiesID'; //XML_RefProp.DigestMethod:= xdmSHA1; //XML_RefProp.URI:= '#SignedPropertiesID'; //XML_Refs.Add(XML_RefProp); //XML_Signer.UpdateReferencesDigest; // // NODO 3: Se ha de firmar el propio certificado utilizado // // El nodo KeyInfo ha de ir identificado por 'Certificate1' -segun los // ejemplos, el PDF no da ningun nombre concreto- // si se firma dos veces un mismo XML no chocan aunque el // nombre coincida por estar dentro de diferentes nodos "Signature". XML_RefCert.URI:= '#Certificate1'; XML_RefCert.DigestMethod:= xdmSHA1; XML_Refs.Add(XML_RefCert); //Lo sumo a las cosas a ser firmadas //Como ya tengo todos los nodos, ahora genero la firma y consigo que //exista el nodo KeyInfo, de forma que pueda darle el nombre que le toca //antes de unir el XML original con el nuevo nodo de la firma. XML_Signer.Sign; XML_Signer.Signature.KeyInfo.ID:= 'Certificate1'; //Añado la firma al XML //===================== XML_Nodo:= ElXMLDOMNode(XML_Doc.DocumentElement); XML_Signer.Save(XML_Nodo); //Actualizo el stream MS con el XML final firmado. MS.Position:= 0; XML_Doc.SaveToStream(MS); result:= true; //Guardo XML firmado a fichero //============================ // Out_Stream:= TFileStream.create(SAveDialog1.FileName, fmCreate); // XML_Doc.SaveToStream(Out_Stream); // FreeAndNil(Out_Stream); finally //NOTA: Los XML_Ref se destruyen solos junto al XML_Refs XML_Doc.free; XML_Refs.free; XML_Signer.free; XML_XAdES.free; XML_KeyData.Free; end; end; //XML
Bueno, un tocho como ves, me costos meses dejarlo funcionando -eso sí, aprendí de XML y firmas que no veas- así que me debes X cervezas, con X >> 10
#11
Escrito 12 septiembre 2013 - 04:56
Excelente post.
Yo peleándome con el xmlmapper de Delphi, con un XMLTransformProvider y 20 clientdataset, por los muchos campos TDataSetField que crea la transformación ...
Y lo que haces es construir tu el xml, sólo con las ramas que necesitas, no con todas (faltan <ThirdParty> y <FactoringAssignmentData>, por ejemplo)
Te invitaré a los botellines de cerveza que hagan falta cuando vengas a cuenca
#12
Escrito 12 septiembre 2013 - 06:37
Dos preguntas sobre esta librería:Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!
¿Permite firmar con certificados de la FNMT?
¿Permite incluir sellado de tiempo?.
Un saludo.
#13
Escrito 12 septiembre 2013 - 02:27
... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...
Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...
#14
Escrito 12 septiembre 2013 - 02:30
... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...
Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...
Si me das a elegir amigo, yo prefiero Mexicanos
#15
Escrito 13 septiembre 2013 - 03:42
He estado adaptando y probando tu código y el Servicio de validación de facturas electrónicas de la AEAT ya me valida la estructura y el formato contable !!
pero tengo un par de dudas:
1) ¿qué hace exactamente la funcion HexStr2ByteArray?
2) la variable XML_Buf, ¿es de tipo TByteArray = array[0..32767] of Byte;?
Por cierto, las líneas:
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= 'http://www.facturae....urae_v3_1.pdf'; "
y
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= 'http://www.w3.org/20.../xmldsig#sha1';"
no compilan tal cual están, no se si es un error debido al copy&paste....
#16
Escrito 13 septiembre 2013 - 04:22
Dos preguntas sobre esta librería:Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!
¿Permite firmar con certificados de la FNMT?
¿Permite incluir sellado de tiempo?.
Un saludo.
Si, permite de todo, puedes firmar con certificados "fichero" como el de FNMT, con cualquier certificado cargado en tu windows (incluyendo tarjetas criptograficas, como el eDNI), permite aplicar time stamp (evidentemente tienes que configurar un servidor de tiempo gratuito o tener uno "pagado", creo que la marina española tiene uno gratuito pero no lo he investigado).
#17
Escrito 13 septiembre 2013 - 04:25
Excelente post.
Yo peleándome con el xmlmapper de Delphi, con un XMLTransformProvider y 20 clientdataset, por los muchos campos TDataSetField que crea la transformación ...
Y lo que haces es construir tu el xml, sólo con las ramas que necesitas, no con todas (faltan <ThirdParty> y <FactoringAssignmentData>, por ejemplo)
Te invitaré a los botellines de cerveza que hagan falta cuando vengas a cuenca
XML = Fichero de texto, no hay que olvidarlo!
#18
Escrito 13 septiembre 2013 - 04:51
... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...
Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...
Resulta muy dificil hablar de este tema "escribiendo", porque pronunciamos diferente, lo estuve comentando con una amiga mexicana en directo, y la cosa es así:
Cuando se puso el nombre de "Mexico", en el español de esa época se leía como lo leeis vosotros ahora, pero en España el sonido fue cambiando, y el antiguo sonido "x" equivale a la "j" nuestra actual.
Así que si un español "de ahora" lee la palabra "mexico" os sonaría rarísimo (la x sonaría como en "extraordinario") pero si lee "mejico" os sonaría perfecto, como si vosotros leyeseis "mexico".
Es igual que con "Don Quixote de la Mancha", ese es el nombre original, pero con el tiempo y para que siguiese sonando igual en España, se ha cambiado a "Quijote".
#19
Escrito 13 septiembre 2013 - 06:12
He estado adaptando y probando tu código y el Servicio de validación de facturas electrónicas de la AEAT ya me valida la estructura y el formato contable !!
Eso ya es para celebrarlo!
1) ¿qué hace exactamente la funcion HexStr2ByteArray?
Convierte un texto con una valor hexadecimal (como '3A18B197ABA90FA6AFF0DEE912F0C006110BEA13') en un array de bytes.
Tienes un código similar comentado justo debajo, en D7 es una línea, pero en versiones posteriores no vale así, por eso me enviaron código "todo terreno" los de Eldos que luego saque a una función.
function HexStr2ByteArray(digest: string; var Buf: ByteArray): boolean; var j, k1, k2: integer; begin try //Longitud ha de ser par (van en parejas)... if Length(Digest) mod 2 <> 0 then raise Exception.create('Hex2ByteArray: El valor exadecimal no contiene un número par de dígitos.'); //Hexadecimal ha de estar en minusculas... Digest:= LowerCase(Digest); //Fijo tamaño del array of bytes final... SetLength(Buf, Length(Digest) div 2); //En delphi 7 HexToBin va bien, en Delphis mas moderno, no vale porque //WideChar y pChar se tratan de forma diferente, asi que se hace a mano. //HexToBin(PChar(XML_Digest), PChar(XML_Buf), Length(XML_Digest) div 2); for j:= 0 to Length(Buf)-1 do begin k1:= SBMath.HexToDecDigit(Digest[j*2 + 1]); k2:= SBMath.HexToDecDigit(Digest[j*2 + 2]); if (k1<0) or (k2<0) then raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.'); Buf[j]:= k1 shl 4 + k2; end; result:= true; except result:= false; end; end;
2) la variable XML_Buf, ¿es de tipo TByteArray = array[0..32767] of Byte;?
Es ByteArray (sin la T en mi código), ese tipo se define en SBUtil o SBType (según la versión) y debe ser algo similar a lo que pones, aunque no lo he mirado por dentro (opté por usar los .dcu en lugar de las .pas en la librería, no recuerdo porqué!).
Por cierto, las líneas:
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= 'http://www.facturae....urae_v3_1.pdf'; "
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= 'http://www.w3.org/20.../xmldsig#sha1';"
no compilan tal cual están, no se si es un error debido al copy&paste....
Es porque el editor del foro le ha añadido "[ u r l =" (sin los espacios) al principio de cada url y "[ / u r l ]" al final, quitaselo (el segundo añadido no es posible en pascal, está detras del ";") y prueba.
Un saludo y suerte con el firmado!
#20
Escrito 13 septiembre 2013 - 07:18
Es igual que con "Don Quixote de la Mancha", ese es el nombre original, pero con el tiempo y para que siguiese sonando igual en España, se ha cambiado a "Quijote".
Mira que cosa tan interesante nos has enseñado hoy. Toda la vida pensé que era "Don quijote"...
Como puede verse por cierto aquí:
http://books.google....quixote&f=false
Por cierto, veo que la V era usada como U en quellos años...