Club Delphi  
    Paypal   FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Proyecto SIF/Veri*Factu/Ley Antifraude > General/Noticias
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 20-10-2025
espinete espinete is offline
Miembro
 
Registrado: mar 2009
Posts: 662
Poder: 18
espinete Va camino a la fama
Problema con los XML generados

Tengo un problema con mis XML y acabo de darme cuenta ahora. la verdad es que no sé cuándo pudo empezar, estoy revisando XML antiguos y son correctos, pero tras algún cambio me ocurre lo siguiente.

Algunos nodos del XML aparecen como <item> en vez del nombre correcto.

Por ejemplo este:

Código:
    <Desglose>
      <item>
        <Impuesto>01</Impuesto>
        <ClaveRegimen>04</ClaveRegimen>
        <CalificacionOperacion>S2</CalificacionOperacion>
        <TipoImpositivo>0</TipoImpositivo>
        <BaseImponibleOimporteNoSujeto>33.64</BaseImponibleOimporteNoSujeto>
        <CuotaRepercutida>0</CuotaRepercutida>
      </item>
    </Desglose>
Sin embargo, la forma de crear el RF es la de siempre y comparándolo con la DLL/SDK publicada en el foro, no hay ninguna diferencia, al menos que yo vea:

Aquí va una porción simplificada del código:

Código:
// Desglose de impuestos
ListaDesglose := DesgloseType.Create();

j:=0;

query2.First;
while not (query2.Eof) do
begin
	DetalleDesglose := DetalleType.Create;

	DetalleDesglose.Impuesto                      := ImpuestoType(combobox11.ItemIndex);   //IVA, IPSI, IGIC, OTROS
	DetalleDesglose.ClaveRegimen                  := GetOperationTypeFromCode('_'+copy(ComboBox3.text,1,2));

	if (AdvOfficeCheckBox3.Checked) or (ComboBox6.ItemIndex=8) then  //Inversión del sujeto pasivo
		DetalleDesglose.CalificacionOperacion         := CalificacionOperacionType.S2
	else
		DetalleDesglose.CalificacionOperacion         := CalificacionOperacionType.S1;

	DetalleDesglose.BaseImponibleOimporteNoSujeto := cambiaen(formatfloat('0.00',VeriFactuForm.query2.FieldByName('base_e').asfloat),',','.');

	if ComboBox6.ItemIndex=9 then     //No sujeta (no se incluyen las cuotas de iva)
	begin
		DetalleDesglose.CalificacionOperacion         := CalificacionOperacionType(2);
	end
	else
	if ComboBox6.ItemIndex=10 then     //No sujeta (no se incluyen las cuotas de iva)
	begin
		DetalleDesglose.CalificacionOperacion         := CalificacionOperacionType(3);
	end
	else                              // Sujeta + Inversión sujeto pasivo (cuotas iva a cero)
	if DetalleDesglose.CalificacionOperacion=CalificacionOperacionType.S2 then
	begin
		DetalleDesglose.TipoImpositivo           := '0';
		DetalleDesglose.CuotaRepercutida         := '0';
	end
	else
	begin                             //No exenta/sujeta
		DetalleDesglose.TipoImpositivo           := cambiaen(VeriFactuForm.query2.FieldByName('porciento').asstring,',','.');
		DetalleDesglose.CuotaRepercutida         := cambiaen(formatfloat('0.00',VeriFactuForm.query2.FieldByName('IVA_E').asfloat),',','.');
		if (Table1.fieldbyname('IVA_RE').asstring='R') and (query2.FieldByName('porciento_re').asfloat>0) then
		begin
			DetalleDesglose.TipoRecargoEquivalencia  := cambiaen(formatfloat('0.00',query2.FieldByName('porciento_re').asfloat),',','.');
			DetalleDesglose.CuotaRecargoEquivalencia := cambiaen(formatfloat('0.00',query2.FieldByName('RE_E').asfloat),',','.');
		end;
	end;

	// colocar el desglose en la lista
	SetLength(ListaDesglose, j+1 );
	ListaDesglose[j] := DetalleDesglose;
	inc(j);

	query2.Next;
end;

// asignar los desgloses de IVA al objeto de factura
F.RegistroAlta.Desglose:= ListaDesglose;
La forma de armar el RF parece correcta (de hecho ahí no he cambiado nada). El problema creo que está a la hora de guardar el XML en un archivo (que luego tengo que abrir con la app externa que se encarga de enviar los RF a Hacienda).
Ese archivo XML ya está mal guardado, con los nodos <item>, así que creo que el problema en la forma de guardarlos.

Para guardar el RF como XML uso esta función que alguien publicó en el foro:

Código:
function ExtraerXMLdelRF(RegistroFactura: RegistroFacturaType): String;
var
  RootNode, NewNode: IXMLNode;
  RefId, Swdsl: string;
  MyXML: TXMLDocument;
  Resultado: string;
  MOPToSoapDomConvert: TOPToSoapDomConvert;
begin
  Resultado := '';
  MyXML := TXMLDocument.Create(Application);
  MOPToSoapDomConvert := TOPtoSoapDomConvert.Create(Application);

  try
    MyXML.Active := True;
    MyXML.Encoding := 'utf-8';

    // Creamos nodo raíz neutro
    RootNode := MyXML.CreateNode('root');

    MOPToSoapDomConvert.Encoding := 'utf-8';
    MOPToSoapDomConvert.Options := [
      TSOAPConvertOption.soDontSendEmptyNodes,
      TSOAPConvertOption.soUTF8EncodeXML,
      TSOAPConvertOption.soTryAllSchema,
      TSOAPConvertOption.soSendUntyped,
      TSOAPConvertOption.soSOAP12
    ];

    Swdsl := 'https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl';

    // Aquí es donde se genera el XML sin duplicados
    NewNode := RegistroFactura.ObjectToSOAP(RootNode, RootNode, MOPToSoapDomConvert,
                                            'RegistroFactura', Swdsl, 'T',
                                            [ocoDontPrefixNode, ocoDontPutTypeAttr], RefId);

    // Reemplazamos el nodo raíz con el generado
    MyXML.DocumentElement := NewNode;
    MyXML.XML.Text := FormatXMLData(MyXML.XML.Text);
    MyXML.XML.SaveToFile(extractfilepath(application.exename)+'VeriFactu/RF'+RegistroFactura.RegistroAlta.RefExterna+'.xml',TEncoding.UTF8);  // Provisional

    Resultado := MyXML.XML.Text;

    // Eliminamos cabecera XML y envoltorios innecesarios si hicieran falta
    Resultado := StringReplace(Resultado, '<?xml version="1.0"?>', '', [rfIgnoreCase]);

  except
    on E: Exception do
    begin
      Log('Error al guardar el XML: ' + E.Message);
      ShowMessage('Error al guardar el XML: ' + E.Message);
      Resultado := '';
    end;
  end;

  MyXML.Free;
  MOPToSoapDomConvert.Free;

  Result := Resultado;
end;
¿Puede ser por la ruta de Swdsl := 'https://prewww2.aeat.es/static_files...cturacion.wsdl'; ,ahora que me fijo?

Agradecería cualquier pista, o si alguien usa otra forma de guardar el XML estaría agradecido de estudiarla y ver si me sirve, teniendo en cuenta que después tengo que volver a convertir ese XML en el RF para el envío.

Por cierto, aunque el XML tenga esos nodos <item>, los RF se envían sin problema a Hacienda y no da error, pero me preocupa por si en un futuro empiezan a fallar.

Última edición por espinete fecha: 20-10-2025 a las 14:33:56.
Responder Con Cita
  #2  
Antiguo 20-10-2025
Avatar de seccion_31
seccion_31 seccion_31 is offline
Miembro
 
Registrado: ene 2017
Posts: 472
Poder: 10
seccion_31 Va por buen camino
tienes razon, en que tiene ese comportamiento, tambien en el componente que diseñe lo hace asi. He revisado exportaciones de RF antiguas y asi estan.

aparentemente es el modo normal de delphi de exportar arrays que tiene esa funcion. (segun chatgpt)

chatgpt propone renombrar esas etiquetas una vez generado el XML.

¿ por curiosidad donde envias los registros de facturacion exportados ?

(no digo que hagais esto, ¿eh? ) para los que estamos en solo verifactu, segun hacienda, tras el envio no habria que conservada nada, puesto que ya esta en el servidor de ellos. tengo una consulta respondida de esta forma via email.

de todas formas, y por curiosidad lo probare mañana y vere si funciona bien el codigo que me dio chatgpt.

Saludos !
Responder Con Cita
  #3  
Antiguo 20-10-2025
espinete espinete is offline
Miembro
 
Registrado: mar 2009
Posts: 662
Poder: 18
espinete Va camino a la fama
Gracias por la respuesta. Efectivamente, empezó a generar los XML así cuando cambié la forma de generar y enviar los RF, para que la aplicación solo generara los XML pero otra aplicación los enviara. Para ello, necesito primero generarlos y guardarlos y luego abrirlos desde la otra app (que puede estar en otro PC incluso).

Confirmo que funciona, incluso con esos <item>.

Actualmente lo que hago es modificarlo a mano antes de guardarlo, detectando si existen esos nodos y poniéndoles el nombre correcto
Responder Con Cita
  #4  
Antiguo 21-10-2025
Avatar de seccion_31
seccion_31 seccion_31 is offline
Miembro
 
Registrado: ene 2017
Posts: 472
Poder: 10
seccion_31 Va por buen camino
El "problema" viene desde el WSDL, todos los que usen la funcion ObjectToSOAP obtendran el mismo resultado.

En principio no estamos haciendo nada mal, si la definicion del WSDL viniera de otra forma, la funcion ObjectToSOAP lo haria "correcto".

Manipular el XML cuando ha salido me da un poco de mal rollo, he probado distintas alternativas y manipular el XML con funciones de nodos, etc... no funciona bien. Lo mejor a mi parecer es directamente manipular el archivo de texto:

Código:
function ReemplazarItemEnSeccion(const XMLText, Seccion, NombreViejo, NombreNuevo: string): string;
var
  Inicio, Fin, PosActual: Integer;
  ParteAntes, ParteMedia, ParteDespues: string;
begin
  Result := XMLText;
  PosActual := 1;

  while True do
  begin
    // Buscar apertura de sección
    Inicio := PosEx('<' + Seccion + '>', Result, PosActual);
    if Inicio = 0 then
      Break;

    // Buscar cierre de sección
    Fin := PosEx('</' + Seccion + '>', Result, Inicio);
    if Fin = 0 then
      Break;

    // Extraer contenido dentro de la sección
    ParteAntes := Copy(Result, 1, Inicio + Length(Seccion) + 1);
    ParteMedia := Copy(Result, Inicio + Length(Seccion) + 2, Fin - (Inicio + Length(Seccion) + 2));
    ParteDespues := Copy(Result, Fin, Length(Result) - Fin + 1);

    // Reemplazar solo en la parte media
    ParteMedia := StringReplace(ParteMedia,
      '<' + NombreViejo + '>', '<' + NombreNuevo + '>', [rfReplaceAll, rfIgnoreCase]);
    ParteMedia := StringReplace(ParteMedia,
      '</' + NombreViejo + '>', '</' + NombreNuevo + '>', [rfReplaceAll, rfIgnoreCase]);

    // Reconstruir XML
    Result := ParteAntes + ParteMedia + ParteDespues;

    // Mover posición para siguiente búsqueda (evitar ciclo infinito)
    PosActual := Fin + Length(Seccion) + 2;
  end;
end;
llamar a la funcion por cada "array" del XML

Código:
xmlText := ReemplazarItemEnSeccion(xmlText, 'Destinatarios', 'item', 'IDDestinatario');
y guardar el xmlText

Sinceramente no se que hacer, esto funciona. pero como digo me da "mal rollo" tocar un XML que ha salido limpio.

Tengo que recopilar que arrays existen y colocarlo, quizas como opcion.

Pero como digo no se si tocarlo o no. Porque en modo verifactu, no hay que enviar el RF, y tenerlo como esta ahora, no creo que sea problema. Maximo cuando la AEAT ya lo tiene.

Saludos !

Pienso que no vas a tener problemas en enviar los RF, mientras no toquen el WSDL para cambiar la generacion de las etiquetas de los arrays. Y creo que eso no lo van a tocar. Es mas yo creo que ni se han preocupado de ello.

Me gustaria tener alguna opinion.

Última edición por seccion_31 fecha: 21-10-2025 a las 08:19:20.
Responder Con Cita
  #5  
Antiguo 21-10-2025
espinete espinete is offline
Miembro
 
Registrado: mar 2009
Posts: 662
Poder: 18
espinete Va camino a la fama
Por lo pronto, Hacienda acepta el XML aunque ponga <item> en vez de <DetalleDesglose>. También ocurre en el nodo <Destinatarios>. Pero lo dicho, se lo traga como si estuviera bien.

En nuestro caso, nosotros guardamos el RF en la BD, porque luego otra aplicación se encarga de hacer los envíos a Hacienda desde el Servidor, así que hay que "leer" ese XML y volver a convertirlo en objeto, para poder firmarlo y enviarlo.

Nosotros hemos optado por modificar el texto a mano, por si acaso algún día se pongan tiquismiquis con los nombres de esos nodos y deje de funcionar.

Concretamente hacemos esto:

Código:
    // Reemplazamos el nodo raíz con el generado
    MyXML.DocumentElement := NewNode;

    MyXML.XML.Text := stringreplace(MyXML.XML.Text,'<Desglose><item>','<Desglose><DetalleDesglose>',[rfReplaceAll]);
    MyXML.XML.Text := stringreplace(MyXML.XML.Text,'</item></Desglose>','</DetalleDesglose></Desglose>',[rfReplaceAll]);

    MyXML.XML.Text := stringreplace(MyXML.XML.Text,'<Destinatarios><item>','<Destinatarios><IDDestinatario>',[rfReplaceAll]);
    MyXML.XML.Text := stringreplace(MyXML.XML.Text,'</IDDestinatario></item>','</IDDestinatario></Destinatarios>',[rfReplaceAll]);

    MyXML.XML.Text := FormatXMLData(MyXML.XML.Text);
    MyXML.XML.SaveToFile(extractfilepath(application.exename)+'VeriFactu/RF'+RegistroFactura.RegistroAlta.RefExterna+'.xml',TEncoding.UTF8);  // Provisional
Hay que hacerlo ANTES de lalamr a FormatXMLData(), porque una vez formateado el XML, cada nodo tendrá su propia línea en el archivo y es más difícil encontrar los nodos chungos.
O eso, o usar la función ReemplazarItemEnSeccion() que has facilitado, que es más genérica y servirá para cualquier otra incidencia que pueda surgir.

Nosotros guardamos el XML/RF de cada factura en disco, pero solo por tener más seguridad y que en caso de algún error poder revisarlo todo más cómoda y rápidamente. En realidad el RF se guarda en la BD también, así que los archivos "sobran", pero es más rápido buscar/abrir un archivo en el disco que hacerlo en la BD.
Responder Con Cita
  #6  
Antiguo 21-10-2025
Avatar de seccion_31
seccion_31 seccion_31 is offline
Miembro
 
Registrado: ene 2017
Posts: 472
Poder: 10
seccion_31 Va por buen camino
Ok. entiendo tu proceso para que te veas un poco obligado a ese cambio

la verdad me ha sorprendido tu forma de reemplazo:

por cierto, tengo una duda: ¿ con varios desgloses funcionara ?

Saludos !

en el caso del componente, lo dejare tal cual esta, sin reemplazo, aunque guardare el código comentado en la DLL por si acaso.
Responder Con Cita
  #7  
Antiguo 21-10-2025
espinete espinete is offline
Miembro
 
Registrado: mar 2009
Posts: 662
Poder: 18
espinete Va camino a la fama
Probablemente haya otra forma mejor de hacer el reemplazo, pero me vi apurado ayer y lo hice así en plan rápido porque si no no dormía

Creo que hay un error aquí:
MyXML.XML.Text := stringreplace(MyXML.XML.Text,'</IDDestinatario></item>','</IDDestinatario></Destinatarios>',[rfReplaceAll]);

Debería ser así. me equivoqué al copiar/pegar:
MyXML.XML.Text := stringreplace(MyXML.XML.Text,'</item></Destinatarios>','</IDDestinatario></Destinatarios>',[rfReplaceAll]);

Esto solo funciona si el XML generado antes de llamar a FormatXMLData contiene todo el XML en una sola línea, porque es la única forma de que se encuentre esa cadena exacta a reemplazar.
Si ya estuviera formateado, con cada nodo en una línea del archivo, el reemplazo no funciona (no existe esa cadena seguida tal cual), y habría que usar la función comentada anteriormente, que está más currada.

Ojo, yo he detectado lo de <item> solo en estos dos nodos, pero puede que ocurra también en otros y habrá que estar atentos.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Restar Campo Generados de SQL kurono Varios 15 23-06-2013 09:35:10
Archivos _XXXX.DBF generados por consulta sql klionsis Varios 1 08-09-2008 18:05:43
Ficheros generados por delphi.NET Mariolarr .NET 1 29-10-2007 19:59:41
Archivos .MAP generados por Delphi Ana Tudela Varios 2 26-10-2005 18:44:48
Archivos temporales generados por TQuerys Balda Conexión con bases de datos 0 14-04-2005 14:18:29


La franja horaria es GMT +2. Ahora son las 18:00:05.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi