Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Internet (https://www.clubdelphi.com/foros/forumdisplay.php?f=3)
-   -   Ley antifraude 2021 (VERIFACTU) - Programas informáticos (https://www.clubdelphi.com/foros/showthread.php?t=95235)

djatila 21-10-2024 20:41:13

Cita:

Empezado por ermendalenda (Mensaje 558555)
Creo que me estoy mareando mss
A ver
Si se ha enviado un registro con la huella incorrecta, quien dice uno dice 2 seguidos o 4 por que envias en un lote, y ya están emitidos los qrs. Que pasa con esos qrs, siguen rulando?
Y está claro que sí te devuelve un registro con la huella errónea hay que regenerar y enviar como subsanacion desde ese hasta el último envío , digo yo que será así por lógica.

creo que el QR no lleva nada de la huella , según entendí yo el QR lleva

1.º NIF del obligado a expedir la factura.
2.º Número de serie y número de la factura expedida.
3.º Fecha de expedición de la factura.
4.º Importe total de la factura.

De hecho yo pienso que sino hay internet se pueden seguir emitiendo facturas y llevan sus QR, simplemente cuando se restablezca el internet se mandan los registros de facturacion en orden de creacion y una anotacion de incidencia en ellos. (que no estoy seguro de saber hacerla, pero que voy a probar en estos dias)

bmfranky 21-10-2024 20:52:57

Cita:

Empezado por ermendalenda (Mensaje 558543)
Ufdd.la factura electrónica tela.
Lo que si es mejor que dividas los conceptos por líneas para rellenar los items correctamente en la facturae, no recuerdo el tamaño de cada item en facturae si era 500 o 1000 pero tb puedes tener problemas si lo haces así. Además en facturae entras en más detalles co.o son las unidades de medida y para poner aceite, si rellenas 3 litros no puedes/debes poner 3 litros escrito, por que cuando empiece es fácil que algún cliente te la rechace por cualquier chorrada que no esté correcta , ya que el cliente tendrá otro soft2are que las absorberá y tiene que coincidir, y hay muchos mijitas

Ahi va la coña que todas esas mamonadas, ya las he tenido en cuenta ya que modificaba la version del programa, cada campo de texto del item son 2000 caracteres, lo de las unidades he creado un selector combobox que devuelve el item segun la norma por ejemplo "Unidades" es 01, "litros" 05, etc...
Estoy usando la version Factura-E 2.2 de momento, supongo que sera la que se usara en su momento.
En cuanto termine esto esto, me pongo con la segunda parte, a cada fatura generada de un cliente con la marca de empresa , generar la factura electronica a la espera de si la enviamos o no...

bmfranky 21-10-2024 20:55:47

Cita:

Empezado por ermendalenda (Mensaje 558555)
Creo que me estoy mareando mss
A ver
Si se ha enviado un registro con la huella incorrecta, quien dice uno dice 2 seguidos o 4 por que envias en un lote, y ya están emitidos los qrs. Que pasa con esos qrs, siguen rulando?
Y está claro que sí te devuelve un registro con la huella errónea hay que regenerar y enviar como subsanacion desde ese hasta el último envío , digo yo que será así por lógica.

Si, pero podemos aprovechar la misma respuesta de la aeat, para corregir, imagino que devolveran encadenada la secuencia de todas las huellas con sus datos, si pudierais probar alguno de los que envias varias a la vez, yo en mi programa , no lo he contemplado asi que no puedo probarlo.

bmfranky 21-10-2024 20:58:05

Error
 
Cita:

Empezado por sglorka (Mensaje 558556)
Os sigue dando error al deserializar la Respuesta ?

Es muy raro, pero a veces no envian la respuest abien formada, con la version moderna, como si hubiesen varios servidores respondiendo a la vez y alguno no estubiera actualizado, yo por siacaso en el retri , he tenido en cuenta la respuesta y si es por deserializar , reenvio usando http y parseo a pelo la respuesta.

bmfranky 21-10-2024 21:01:16

Qr
 
Cita:

Empezado por djatila (Mensaje 558557)
creo que el QR no lleva nada de la huella , según entendí yo el QR lleva

1.º NIF del obligado a expedir la factura.
2.º Número de serie y número de la factura expedida.
3.º Fecha de expedición de la factura.
4.º Importe total de la factura.

De hecho yo pienso que sino hay internet se pueden seguir emitiendo facturas y llevan sus QR, simplemente cuando se restablezca el internet se mandan los registros de facturacion en orden de creacion y una anotacion de incidencia en ellos. (que no estoy seguro de saber hacerla, pero que voy a probar en estos dias)


Exacto, otra cosa es que justo lo que fallo al crear el encadenado , sea uno de los parametros que se tiene en cuenta en el QR, como en mi caso la fecha de factura consignada.

djatila 21-10-2024 21:28:38

Error al intentar subsanar un registro AceptadoConErrores
 
Cita:

Empezado por bmfranky (Mensaje 558530)
@djatila Tienes el buzon lleno, no puedo contestar tu mensaje.

ya vacié el buzón


A ver si me explico bien, el otro día subí una factura que me dio parcialmente correcto

Código:

<q1:EstadoRegistro>AceptadoConErrores</q1:EstadoRegistro>
<q1:CodigoErrorRegistro>2004</q1:CodigoErrorRegistro>
<q1:DescripcionErrorRegistro>El valor del campo FechaHoraHusoGenRegistro debe ser la fecha actual del sistema de la AEAT, admitiéndose un margen de error de: 120 segundos.</q1:DescripcionErrorRegistro>

se que esta subida porque puedo consultarla, he intentado hacer una subsanación mandando el mismo registro pero añadiendo
RegistroFacturacionAlta.Subsanacion = SubsanacionType.S
y con el calculo de fechahorahusogenregistro actual y recibo el error

Código:

<q1:EstadoEnvio>ParcialmenteCorrecto</q1:EstadoEnvio>
.....
<q1:EstadoRegistro>Incorrecto</q1:EstadoRegistro>
<q1:CodigoErrorRegistro>3000</q1:CodigoErrorRegistro>
<q1:DescripcionErrorRegistro>Registro de facturación duplicado.</q1:DescripcionErrorRegistro>
<q1:RegistroDuplicado>
<IdPeticionRegistroDuplicado>20241016125647546025</IdPeticionRegistroDuplicado>
<EstadoRegistroDuplicado">AceptadaConErrores</EstadoRegistroDuplicado>
<CodigoErrorRegistro">2004</CodigoErrorRegistro>
<DescripcionErrorRegistro>El valor del campo FechaHoraHusoGenRegistro debe ser la fecha actual del sistema de la AEAT, admitiéndose un margen de error de:</DescripcionErrorRegistro>
</q1:RegistroDuplicado>

¿Ustedes que datos mandais para subsanar a parte de la corrección? esto y algo mas (RegistroFacturacionAlta.Subsanacion = SubsanacionType.S)

ya he mandado otros registros que han dado correcto, pero ese sigue sin subsanarse.
me surge la siguiente duda cuando vaya a subsanarlo tendré que actualizar la huella porque si tengo en cuenta el registro anterior varia, ¿debo actualizar a la nueva huella?

a ver si me arrojais luz sobre la subsanación que estoy atascado. gracias

bmfranky 21-10-2024 21:53:52

Cita:

Empezado por djatila (Mensaje 558562)
ya vacié el buzón


A ver si me explico bien, el otro día subí una factura que me dio parcialmente correcto

Código:

<q1:EstadoRegistro>AceptadoConErrores</q1:EstadoRegistro>
<q1:CodigoErrorRegistro>2004</q1:CodigoErrorRegistro>
<q1:DescripcionErrorRegistro>El valor del campo FechaHoraHusoGenRegistro debe ser la fecha actual del sistema de la AEAT, admitiéndose un margen de error de: 120 segundos.</q1:DescripcionErrorRegistro>

se que esta subida porque puedo consultarla, he intentado hacer una subsanación mandando el mismo registro pero añadiendo
RegistroFacturacionAlta.Subsanacion = SubsanacionType.S
y con el calculo de fechahorahusogenregistro actual y recibo el error

Código:

<q1:EstadoEnvio>ParcialmenteCorrecto</q1:EstadoEnvio>
.....
<q1:EstadoRegistro>Incorrecto</q1:EstadoRegistro>
<q1:CodigoErrorRegistro>3000</q1:CodigoErrorRegistro>
<q1:DescripcionErrorRegistro>Registro de facturación duplicado.</q1:DescripcionErrorRegistro>
<q1:RegistroDuplicado>
<IdPeticionRegistroDuplicado>20241016125647546025</IdPeticionRegistroDuplicado>
<EstadoRegistroDuplicado">AceptadaConErrores</EstadoRegistroDuplicado>
<CodigoErrorRegistro">2004</CodigoErrorRegistro>
<DescripcionErrorRegistro>El valor del campo FechaHoraHusoGenRegistro debe ser la fecha actual del sistema de la AEAT, admitiéndose un margen de error de:</DescripcionErrorRegistro>
</q1:RegistroDuplicado>

¿Ustedes que datos mandais para subsanar a parte de la corrección? esto y algo mas (RegistroFacturacionAlta.Subsanacion = SubsanacionType.S)

ya he mandado otros registros que han dado correcto, pero ese sigue sin subsanarse.
me surge la siguiente duda cuando vaya a subsanarlo tendré que actualizar la huella porque si tengo en cuenta el registro anterior varia, ¿debo actualizar a la nueva huella?

a ver si me arrojais luz sobre la subsanación que estoy atascado. gracias

Revise bien el procedimiento

Cita:

Cuando sea necesario subsanar la información que contiene un registro de facturación que ha sido enviado y con estado “Aceptado” o
“AceptadoConErrores” por la AEAT, siempre y cuando no se trate de una causa que exija la emisión de una factura rectificativa (u otro
mecanismo contemplado en el Reglamento de Facturación) ni se anule la factura, se deberá generar un nuevo registro (“de subsanación”), con
la misma clave de registro original que se quiere subsanar, ya con los datos completos y correctos, que será remitido dentro de un nuevo
fichero o mensaje de envío a la AEAT.
El registro original no se puede modificar, por lo que permanecerá inalterado.
En el nuevo registro subsanado se incluirá la propia huella (según se indica en el documento de huella) del registro de facturación. Como
siempre, el encadenamiento debe ser con el registro de facturación inmediatamente anterior (sea de alta o de anulación), por orden
cronológico de generación de registros de facturación en el SIF

[email protected] 21-10-2024 22:54:49

Empezar de nuevo desde cero
 
Después de muchas vicisitudes y con la ayuda de todos los componentes del club, he conseguido recibir la respuesta correcta por parte AEAT.

Como puedo borrar todas las pruebas que he estado haciendo para partir de nuevo desde cero creando el primer registro y a continuación el resto.

ermendalenda 21-10-2024 23:39:28

Cita:

Empezado por [email protected] (Mensaje 558564)
Después de muchas vicisitudes y con la ayuda de todos los componentes del club, he conseguido recibir la respuesta correcta por parte AEAT.

Como puedo borrar todas las pruebas que he estado haciendo para partir de nuevo desde cero creando el primer registro y a continuación el resto.

Para que qui2res borrar, son pruebas
No están comprobando el encadenamiento de momento, aunque si la huella, así que puedes hacer como si empezarás, empieza con otra serie y listo para que no te de registro duplicado

ermendalenda 22-10-2024 00:39:54

Cita:

Empezado por bmfranky (Mensaje 558563)
Revise bien el procedimiento

Ok entendido gracias
O sea si te falla un registro en, por ejemplo, la huella, y henos emitidos 2 registros más correctamente, sólo hay que enviar subsanado el que dio problemas encadenado con el último enviado y aceptado

ermendalenda 22-10-2024 07:36:40

No se donde Stan los post a los que he respondido en este post, error.

ermendalenda 22-10-2024 08:21:39

Como veis mejor en una empresa con multiples franquicias el tema de certificado?
Cada uno con el suyo o envio por terceros
Teniendo en cuenta que es programa de escritorio y envían desde el comercio
Es q veo ventajas e inconvenientes por ambos lados

bmfranky 22-10-2024 08:26:59

Certificado
 
Cita:

Empezado por ermendalenda (Mensaje 558572)
Como veis mejor en una empresa conmult8ples franquicias el tema de certificado?
Cada uno con el suyo o encio por terceros
Teniendo en cuenta que es programa de escritorio y envían desde el comercio
Es q veo ventajas e inconvenientes por ambos lados

Hola. lo que veo mal es el tema de la renovacion del certificado , si es propio, que tendreis que ir a cada tienda a renovarlo cada 2 años?, a parte de que ha de ser asignado al usuario que lo use, como representante, no?

Sistel 22-10-2024 08:47:37

Cita:

Empezado por bmfranky (Mensaje 558573)
Hola. lo que veo mal es el tema de la renovacion del certificado , si es propio, que tendreis que ir a cada tienda a renovarlo cada 2 años?, a parte de que ha de ser asignado al usuario que lo use, como representante, no?

Hola,

En TicketBAI se puede utilizar certificado digital de dispositivo, que es un tipo de certificado digital (sólo para TicketBAI) que emite la empresa pública IZENPE. Es gratuíto y dura 10 años.
Lo puede solicitar cualquiera sin estar a asociado a un obligado tributario. Y después el obligado tributario que lo va a utilizar lo debe autorizar a través de la web de la Hacienda Foral correspondiente.

En alguna de las presentaciones a las que asistí sobre VERIFACTU se comentó que era posible que se crease algún tipo de certificado similar para VERIFACTU.
Veremos cuando la cosa avance si se acuerdan de ese tema.

Saludos

ermendalenda 22-10-2024 08:55:37

Cita:

Empezado por Sistel (Mensaje 558574)
Hola,

En TicketBAI se puede utilizar certificado digital de dispositivo, que es un tipo de certificado digital (sólo para TicketBAI) que emite la empresa pública IZENPE. Es gratuíto y dura 10 años.
Lo puede solicitar cualquiera sin estar a asociado a un obligado tributario. Y después el obligado tributario que lo va a utilizar lo debe autorizar a través de la web de la Hacienda Foral correspondiente.

En alguna de las presentaciones a las que asistí sobre VERIFACTU se comentó que era posible que se crease algún tipo de certificado similar para VERIFACTU.
Veremos cuando la cosa avance si se acuerdan de ese tema.

Saludos

Gracias
Voy a relanzar la pregunta a verifactu, a ver si tienen algo claro
Gracias

bmfranky 22-10-2024 08:59:34

Cita:

Empezado por Sistel (Mensaje 558574)
Hola,

En TicketBAI se puede utilizar certificado digital de dispositivo, que es un tipo de certificado digital (sólo para TicketBAI) que emite la empresa pública IZENPE. Es gratuíto y dura 10 años.
Lo puede solicitar cualquiera sin estar a asociado a un obligado tributario. Y después el obligado tributario que lo va a utilizar lo debe autorizar a través de la web de la Hacienda Foral correspondiente.

En alguna de las presentaciones a las que asistí sobre VERIFACTU se comentó que era posible que se crease algún tipo de certificado similar para VERIFACTU.
Veremos cuando la cosa avance si se acuerdan de ese tema.

Saludos

Gracias por la respuesta.
Cita:

Empezado por ermendalenda (Mensaje 558575)
Gracias
Voy a relanzar la pregunta a verifactu, a ver si tienen algo claro
Gracias

Ya nos comentaras que responden.

antoine0 22-10-2024 09:17:24

Cita:

Empezado por pablog2k (Mensaje 558489)
nosotros ya desde el SII, guardamos tanto el xml enviado como el xml de respuesta, luego viene muy bien saber que se ha enviado exactamente y que han contestado exactamente

Estando de acuerdo contigo, remarcar que con el CSV respondido por AEAT se puede recuperar tanto lo que se ha enviado como la respuesta de la AEAT en el portal.

rci 22-10-2024 09:22:29

Cita:

Empezado por antoine0 (Mensaje 558577)
Estando de acuerdo contigo, remarcar que con el CSV respondido por AEAT se puede recuperar tanto lo que se ha enviado como la respuesta de la AEAT en el portal.

Antoine0 "se puede" o "se podrá"? ahora mismo que yo sepa no hay nada para consultar lo enviado no? (a parte del QR que no ofrece esa información que comentas)

Muchas gracias

antoine0 22-10-2024 09:38:24

Cita:

Empezado por ermendalenda (Mensaje 558572)
Como veis mejor en una empresa con multiples franquicias el tema de certificado?

Múltiples franquicias, ¿son empresas distintas (cada una con su CIF/NIF)?
Si es el mismo NIF, yo los llamaría delegaciones o establecimientos

antoine0 22-10-2024 09:41:13

Cita:

Empezado por rci (Mensaje 558578)
Antoine0 "se puede" o "se podrá"? ahora mismo que yo sepa no hay nada para consultar lo enviado no? (a parte del QR que no ofrece esa información que comentas)

En el SII se puede.
Con Veri*factu acaban de prometernos desarrollo de funciones complementarias en el portal, una siendo de consulta, yo veo como mínimo que se podrá consultar desde el CSV, ya que es la función oficial del CSV...

antoine0 22-10-2024 09:45:42

Cita:

Empezado por Sistel (Mensaje 558574)
En TicketBAI se puede utilizar certificado digital de dispositivo, que es un tipo de certificado digital (sólo para TicketBAI) que emite la empresa pública IZENPE. Es gratuíto y dura 10 años.
Lo puede solicitar cualquiera sin estar a asociado a un obligado tributario. Y después el obligado tributario que lo va a utilizar lo debe autorizar a través de la web de la Hacienda Foral correspondiente.

En la FNMT existe también el certificado de sello de entidad, es (bastante) más caro que los certificados normales pero puede ser asociado a una máquina de la empresa en lugar de ser asociado a una persona. Hay versiones que duran 3 años. Funciona para el SII, debe funcionar también para Veri*factu (pero no he probado aún).

pablog2k 22-10-2024 09:53:57

Cita:

Empezado por antoine0 (Mensaje 558577)
Estando de acuerdo contigo, remarcar que con el CSV respondido por AEAT se puede recuperar tanto lo que se ha enviado como la respuesta de la AEAT en el portal.

me puedes indicar desde que parte de la web de AEAT puedes recuperar esta información con el CSV? gracias
Edit: Disculpa , ya lo encontré, en cotejo de documentos

rci 22-10-2024 10:00:13

Cita:

Empezado por ermendalenda (Mensaje 558493)
He probado a hacer el envio con el certificado caducado, otro con un certificado que no se corresponda con el del emisor, otro con la clave incorrecta y el formato devuelto en la respuesta no está dentro del formato lógico de respuestas, pero se puede identificar buscando la frase por ejemplo "Error de identificación" "No autorizado"...
Código PHP:

<!DOCTYPE html>
<
html lang="es">
<
head>        
    <
meta name="site" content="Sede"/>
    <
link href="/static_files/common/css/aeat.07.css" rel="stylesheet" type="text/css">
<
meta title="AEATviewport" content="width=device-width, initial-scale=1.0" name="viewport">
<
title>Agencia Tributaria401</title><meta name="ObjectId" content="a8937bde849c7710VgnVCM100000dc381e0aRCRD"/><meta name="keyword" content="erro4011"/>
<
meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <
meta name="detalle" content="errores"/>
</
head>
<
body>
    <
div id="body" data-template-id="cc1c55b3cac46710VgnVCM100000dc381e0aRCRD" class="s_Sede p_estandar t_informacion">
        
        <
header id="AEAT_header" class="header_aeat d-print-none d-app-none" >
    <!-- 
CABECERA -->
       <!--
googleoffindex-->
    <
class="focus-only" href="#acc-main">Saltar al contenido principal</a>
    <
div class="header-sup_aeat w-100">
      <
div class="container">              
        <
div class="row">
        <!-- 
CABECERA IZQUIERDA-->
           <
div id="topIzquierda" class="pl-0"><nav class="header-contLogos_aeat"><a href="/Sede/inicio.html" title="P&aacute;gina de inicio" class="header-logosPrincipales_aeat"><span class="logoGobierno Sede">Logotipo Gobierno de España</span><span class="logoAEAT logoSite logoSede ml-2 overflow-hidden">Logotipo Organismo</span></a></nav></div><!-- CABECERA IZQUIERDA FIN -->
</
div>
      </
div>    
    </
div>
    <!--
googleonindex-->
    <!-- 
CABECERA FIN -->
    <!-- 
MENU -->
       <!--
googleoffindex--><div class="w-100 bg-primary p-1"><div class="container"><div class="row justify-content-between align-items-center"><button class="header-btnMovil_aeat text-white d-lg-none col-2 btn btn-link" type="button" id="menuPrincipal"><class="aeat-ico fa-ico-menu ico-size-2 w-100" title="Abrir men&uacute; m&oacute;vil" aria-hidden="true" role="presentation"></i><span class="sr-only">Menú móvil</span></button><nav id="aeat_menuPrincipal" class="navbar-dark navbar navbar-expand-lg p-0 col-12 col-xl-8 col-lg-9 d-none d-lg-flex" aria-label="Menú principal"><!--clase para movilheader-navMovil_aeat--><ul class="navbar-nav"><li class="nav-item"><a href="#" class="py-3 px-4 nav-link dropdown-toggle invisible" role="button"><span>Aqui se cargarán las opciones del menú cuando estén disponibles</span></a></li></ul></nav></div></div></div><!--googleonindex-->
<!-- 
MENU FIN -->
</
header><nav aria-label="Migas navegación" class="js-nav-principal d-print-none d-app-none">
    <
ol class="breadcrumb container bg-transparent px-md-4 px-3">           
<
li class="breadcrumb-item breadcrumb-item__volver"><a data-aeat-id='b3552c121d27c610VgnVCM100000dc381e0aRCRD' href='/Sede/inicio.html'><class="aeat-ico fa-ico-chevron-left ico-size-mini mr-2 d-md-none" aria-hidden="true" role="presentation"></i>Inicio</a></li>
</
ol>
</
nav><div class="container-text js-container">
            <
div class="row my-3 justify-content-center">
                <
main class="col-12 col-lg-7" id="acc-main">
                    <
div class="d-flex"><class="aeat-ico fa-ico-error-sm ico-size-4 mr-3 text-danger" aria-hidden="true" role="presentation"></i><h1 id="js-nombre-canal" class="font-weight-bold mb-0 display-4 mb-3 text-danger">401</h1></div><p>No autorizadoSe ha producido un error al verificar el certificado presentadoLas causas m&aacute;s probables de este error son:</p><ul><li>El certificado no ha sido firmado por una autoridad reconocida.</li><li>El tipo de certificado no es v&aacute;lido para el servicio al que se quiere acceder.</li><li>El certificado ha expirado.</li></ul><p>Puede contactar con el servicio de atenci&oacute;n al contribuyente indicando el c&oacute;digo de error 401.</p><a target="_self" title="Comunicar incidencia" href="https://www2.agenciatributaria.gob.es/soporteaeat/Formularios.nsf/soporteOnline.php?OpenForm&codigoError=401" class="d-block">Comunicar incidencia</a></main>
            </
div
        </
div>
        
        <!--
googleoffindex-->
        <!-- 
<
section>
    <
div id="last-update" class="container pb-3">
        <
class="small text-center mb-0">
            
Página actualizada: <time datetime="2022-09-28">28/septiembre/2022</time>
        </
p>
    </
div>
</
section>
 -->
<
footer class="bg-primary d-print-none d-app-none" >
<
noscript><div class="container py-3"><div class="row"><class="text-white"   href="https://sede.agenciatributaria.gob.es/Sede/condiciones-uso-sede-electronica/accesibilidad/declaracion-accesibilidad.html">Accesibilidad</a></div></div></noscript><div id="last-update" class="container pb-3"><class="small text-center text-white mb-0">Página actualizada: <time datetime="2022-09-28">28/septiembre/2022</time></p></div></footer><!--googleonindex-->

    </
div>
    <
div class="d-none">
   <
span class="js-responsive d-xs-block"></span>
   <
span class="js-responsive d-sm-block"></span>
   <
span class="js-responsive d-md-block"></span>
   <
span class="js-responsive d-lg-block"></span>
   <
span class="js-responsive d-xl-block"></span>
   <
span class="js-responsive d-xxl-block"></span>
   <
span class="js-responsive d-xxxl-block"></span>
 </
div>
<
link href="/static_files/common/css/aeat.07.print.css" rel="stylesheet" type="text/css">
<
script type="text/javascript" src="/static_files/common/script/aeat.07.js"></script>
</body>
</html> 



Hola ermendalenda como lo haces para obtener toda la respuesta html completa en el caso de un error en el certificado?

Acabo de hacer la prueba con un certificado no válido y ha saltado una excepción al enviar, de tipo ProtocolException y en el mensaje hay los primeros 1024 caracteres de lo que has posteado tu pero no está entero y no puedo ver la parte del error de certificado.
Dice solo esto:

Código PHP:

El tipo de contenido text/html del mensaje de respuesta no coincide con el tipo de contenido del enlace (text/xmlcharset=utf-8). Si usa un codificador personalizadoasegúrese de que el método IsContentTypeSupported se implemente correctamente
Los primeros 1024 bytes de la respuesta fueron

<!
DOCTYPE html>
<
html lang="es">
<
head>        
    <
meta name="site" content="Sede"/>
    <
link href="/static_files/common/css/aeat.07.css" rel="stylesheet" type="text/css">
<
meta title="AEATviewport" content="width=device-width, initial-scale=1.0" name="viewport">
<
title>Agencia Tributaria403</title><meta name="ObjectId" content="84837bde849c7710VgnVCM100000dc381e0aRCRD"/><meta name="keyword" content="erro4033"/>
<
meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <
meta name="detalle" content="errores"/>
</
head>
<
body>
    <
div id="body" data-template-id="cc1c55b3cac46710VgnVCM100000dc381e0aRCRD" class="s_Sede p_estandar t_informacion">
        
        <
header id="AEAT_header" class="header_aeat d-print-none d-app-none" >
    <!-- 
CABECERA -->
       <!--
googleoffindex-->
    <
class="focus-only" href="#acc-main">Saltar al contenido principal</a>
    <
div class="header-sup_aeat w-100">
      <
div class="container">              
        <
div class="row">
        <!-- 
CABECERA IZQUIERDA-->
           <
div id="topIzquie'. 


Yo utilizo C# .Net

Muchas gracias

bmfranky 22-10-2024 10:36:24

Cita:

Empezado por rci (Mensaje 558583)
Hola ermendalenda como lo haces para obtener toda la respuesta html completa en el caso de un error en el certificado?

Acabo de hacer la prueba con un certificado no válido y ha saltado una excepción al enviar, de tipo ProtocolException y en el mensaje hay los primeros 1024 caracteres de lo que has posteado tu pero no está entero y no puedo ver la parte del error de certificado.
Dice solo esto:

Código PHP:

El tipo de contenido text/html del mensaje de respuesta no coincide con el tipo de contenido del enlace (text/xmlcharset=utf-8). Si usa un codificador personalizadoasegúrese de que el método IsContentTypeSupported se implemente correctamente
Los primeros 1024 bytes de la respuesta fueron

<!
DOCTYPE html>
<
html lang="es">
<
head>        
    <
meta name="site" content="Sede"/>
    <
link href="/static_files/common/css/aeat.07.css" rel="stylesheet" type="text/css">
<
meta title="AEATviewport" content="width=device-width, initial-scale=1.0" name="viewport">
<
title>Agencia Tributaria403</title><meta name="ObjectId" content="84837bde849c7710VgnVCM100000dc381e0aRCRD"/><meta name="keyword" content="erro4033"/>
<
meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <
meta name="detalle" content="errores"/>
</
head>
<
body>
    <
div id="body" data-template-id="cc1c55b3cac46710VgnVCM100000dc381e0aRCRD" class="s_Sede p_estandar t_informacion">
        
        <
header id="AEAT_header" class="header_aeat d-print-none d-app-none" >
    <!-- 
CABECERA -->
       <!--
googleoffindex-->
    <
class="focus-only" href="#acc-main">Saltar al contenido principal</a>
    <
div class="header-sup_aeat w-100">
      <
div class="container">              
        <
div class="row">
        <!-- 
CABECERA IZQUIERDA-->
           <
div id="topIzquie'. 

Yo utilizo C# .Net

Muchas gracias

Hola, eso es porque usa, las funciones del xmls generadas por la aeat, la respuesta que observa es el error al intentar serializar la respuesta, la unica forma de capturar la respuesta completa es realizando la consulta con el xml, a pelo, si quiere le paso como lo hago yo, cuando me da error, reenvio por http y capturo la respuesta completa.
Sobretodo ahora que aveces responden el nuevo campo type vacio y da error.

marccabra 22-10-2024 11:01:18

Cita:

Empezado por bmfranky (Mensaje 558584)
Hola, eso es porque usa, las funciones del xmls generadas por la aeat, la respuesta que observa es el error al intentar serializar la respuesta, la unica forma de capturar la respuesta completa es realizando la consulta con el xml, a pelo, si quiere le paso como lo hago yo, cuando me da error, reenvio por http y capturo la respuesta completa.
Sobretodo ahora que aveces responden el nuevo campo type vacio y da error.

SI pudieras explicar como haces esto sería genial. Justo ahora obtengo errores al deserializar la respuesta y me vendría bien poderla ver en crudo.

Gracias

[email protected] 22-10-2024 11:04:18

Cita:

Empezado por marccabra (Mensaje 558585)
SI pudieras explicar como haces esto sería genial. Justo ahora obtengo errores al deserializar la respuesta y me vendría bien poderla ver en crudo.

Gracias

Me apunto a la explicacion

pablog2k 22-10-2024 11:20:57

si trabajais con Delphi, en el componente HTTPRIO que hace el envío, evento OnAfterExecute:
Código Delphi [-]
  ss:=TFileStream.Create(ruta+'\respuesta.xml', fmCreate);
  SOAPResponse.Position := 0;
  ss.CopyFrom(SOAPResponse,SOAPResponse.Size);
  ss.Free;
  SOAPResponse.Position := 0;

ahí os guarda la respuesta en la ruta que indiquéis

[email protected] 22-10-2024 11:30:08

Sabéis si han cambiado algo respecto al calculo de la huella digital. ???

De repente me dice que no coincide.


esto es lo que indica la respuesta de AEAT


DescripcionErrorRegistro>El cálculo de la huella suministrada es incorrecta.
Datos de entrada cálculo huella:
IDEmisorFactura=33461360V&amp;NumSerieFactura=02-2024-13784&amp;FechaExpedicionFactura=21-10-2024&amp;TipoFactura=F1&amp;CuotaTotal=1.81&amp;ImporteTotal=10.45&amp;Huella=C0E0A0F622EFF0A77A41E4 7C13972AEAB8E05A85E81D6B873942238A1A670BC5&amp;FechaHoraHusoGenRegistro=2024-10-22T11:15:05+02:00
Huella calculada: 12EB4A6836E721DB0737F03A04EBDFF73D35B3A3D097F549548F00A259C65AFD

bmfranky 22-10-2024 12:33:30

Cita:

Empezado por marccabra (Mensaje 558585)
SI pudieras explicar como haces esto sería genial. Justo ahora obtengo errores al deserializar la respuesta y me vendría bien poderla ver en crudo.

Gracias

Cita:

Empezado por [email protected] (Mensaje 558586)
Me apunto a la explicacion


Hola, he implementado esta funcion , si la de aeat da error de serializacion o otro no interpretable uso esta.
Código:

      /// <summary>
        /// Envio del registro de la factura a la aeat
        /// </summary>
        /// <param name="facAPresentar">Registro creado con las funciones de la aeat.</param>
        /// <param name="action">Accion a realizar por defecto registro alta</param>
        /// <returns>Devuelve la respuesta completa de la aeat</returns>
        public string consultaAEAT(RegFactuSistemaFacturacion facAPresentar,string action = "RegistroAlta")
        {
            XmlSerializer serializaFactura = new XmlSerializer(typeof(RegFactuSistemaFacturacion));
            string facXml = "";
            using (var xmlw = new StringWriter())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.Encoding = Encoding.UTF8;

                using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                {
                    serializaFactura.Serialize(writter, facAPresentar);
                    facXml = xmlw.ToString();
                }

            }
            facXml = facXml.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soapenv:Header /> <soapenv:Body >");
            facXml = facXml.Replace("<RegFactuSistemaFacturacion xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", "<RegFactuSistemaFacturacion xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd\">");
            //facXml = facXml.Replace("</SistemaInformatico>", "</SistemaInformatico> \r\n <FechaHoraHusoGenRegistro xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd\">" + timeStamp +"</FechaHoraHusoGenRegistro>");
            facXml = facXml + " </soapenv:Body> </soapenv:Envelope > ";
            ////////////////////////// <----- Prueba extraccion sin serializar


            XmlDocument doc = new XmlDocument();

            doc.LoadXml(facXml.Replace("utf-16", "utf-8"));
            HttpWebRequest webRequest = CreateWebRequest("https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP", action);
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            X509Certificate2 certificate = new X509Certificate2(Program.certificado, Program.passcertificado); //requiere estas variables globalescontienen el certificado y su clave
            var tiempo = certificate.SubjectName;
            if (certificate == null)
                throw new ArgumentNullException(
                    "Certificate is null. Maybe serial number in configuration was wrong.");

            if (certificate.NotAfter < DateTime.Now)
                throw new ArgumentNullException(
                  $"Certificate is out of date. NotAfter: {certificate.NotAfter}.");

            webRequest.ClientCertificates.Add(certificate);

            using (Stream stream = webRequest.GetRequestStream())
            {
                doc.Save(stream);
            }

            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

            string statusDescription = response.StatusDescription;

            Stream dataStream = response.GetResponseStream();

            string responseFromServer;

            using (StreamReader reader = new StreamReader(dataStream))
            {
                responseFromServer = reader.ReadToEnd();
                reader.Close();
                dataStream.Close();
                response.Close();
            }

            return responseFromServer;

            //retorno = client.RegFactuSistemaFacturacion(facAPresentar);
        }


bmfranky 22-10-2024 12:36:13

Cita:

Empezado por [email protected] (Mensaje 558588)
Sabéis si han cambiado algo respecto al calculo de la huella digital. ???

De repente me dice que no coincide.


esto es lo que indica la respuesta de AEAT


DescripcionErrorRegistro>El cálculo de la huella suministrada es incorrecta.
Datos de entrada cálculo huella:
IDEmisorFactura=33461360V&amp;NumSerieFactura=02-2024-13784&amp;FechaExpedicionFactura=21-10-2024&amp;TipoFactura=F1&amp;CuotaTotal=1.81&amp;ImporteTotal=10.45&amp;Huella=C0E0A0F622EFF0A77A41E4 7C13972AEAB8E05A85E81D6B873942238A1A670BC5&amp;FechaHoraHusoGenRegistro=2024-10-22T11:15:05+02:00
Huella calculada: 12EB4A6836E721DB0737F03A04EBDFF73D35B3A3D097F549548F00A259C65AFD

Esta teniendo en cuaenta la huella anteriormente enviada? , me he dado cuenta que desde ayer si la tienen en cuenta, antes no calculaban ellos sobre el registro que tenian anterior de huella , ahora si.

bmfranky 22-10-2024 14:02:25

Cita:

Empezado por bmfranky (Mensaje 558590)
Hola, he implementado esta funcion , si la de aeat da error de serializacion o otro no interpretable uso esta.
Código:

      /// <summary>
        /// Envio del registro de la factura a la aeat
        /// </summary>
        /// <param name="facAPresentar">Registro creado con las funciones de la aeat.</param>
        /// <param name="action">Accion a realizar por defecto registro alta</param>
        /// <returns>Devuelve la respuesta completa de la aeat</returns>
        public string consultaAEAT(RegFactuSistemaFacturacion facAPresentar,string action = "RegistroAlta")
        {
            XmlSerializer serializaFactura = new XmlSerializer(typeof(RegFactuSistemaFacturacion));
            string facXml = "";
            using (var xmlw = new StringWriter())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.Encoding = Encoding.UTF8;

                using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                {
                    serializaFactura.Serialize(writter, facAPresentar);
                    facXml = xmlw.ToString();
                }

            }
            facXml = facXml.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soapenv:Header /> <soapenv:Body >");
            facXml = facXml.Replace("<RegFactuSistemaFacturacion xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", "<RegFactuSistemaFacturacion xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd\">");
            //facXml = facXml.Replace("</SistemaInformatico>", "</SistemaInformatico> \r\n <FechaHoraHusoGenRegistro xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd\">" + timeStamp +"</FechaHoraHusoGenRegistro>");
            facXml = facXml + " </soapenv:Body> </soapenv:Envelope > ";
            ////////////////////////// <----- Prueba extraccion sin serializar


            XmlDocument doc = new XmlDocument();

            doc.LoadXml(facXml.Replace("utf-16", "utf-8"));
            HttpWebRequest webRequest = CreateWebRequest("https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP", action);
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            X509Certificate2 certificate = new X509Certificate2(Program.certificado, Program.passcertificado); //requiere estas variables globalescontienen el certificado y su clave
            var tiempo = certificate.SubjectName;
            if (certificate == null)
                throw new ArgumentNullException(
                    "Certificate is null. Maybe serial number in configuration was wrong.");

            if (certificate.NotAfter < DateTime.Now)
                throw new ArgumentNullException(
                  $"Certificate is out of date. NotAfter: {certificate.NotAfter}.");

            webRequest.ClientCertificates.Add(certificate);

            using (Stream stream = webRequest.GetRequestStream())
            {
                doc.Save(stream);
            }

            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

            string statusDescription = response.StatusDescription;

            Stream dataStream = response.GetResponseStream();

            string responseFromServer;

            using (StreamReader reader = new StreamReader(dataStream))
            {
                responseFromServer = reader.ReadToEnd();
                reader.Close();
                dataStream.Close();
                response.Close();
            }

            return responseFromServer;

            //retorno = client.RegFactuSistemaFacturacion(facAPresentar);
        }


Hola, @rci, perdona no he seleccionado al copiar esto.


Código:

      /// <summary>
        /// Crea la instancia WebRequest para enviar la petición
        /// al web service de la AEAT.
        /// </summary>
        /// <param name="url">Url del web service.</param>
        /// <param name="action">Acción del web service.</param>
        /// <returns></returns>
        private static HttpWebRequest CreateWebRequest(string url, string action)
        {

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);

            webRequest.Headers.Add("SOAPAction", action);

            webRequest.ContentType = "text/xml;charset=\"utf-8\"";
            webRequest.Accept = "text/xml";
            webRequest.Method = "POST";
            return webRequest;

        }

simplemente uselo asi
Código:

string retornoConsulta = consultaAEAT(facAPresentar);

bmfranky 22-10-2024 14:21:50

Funcion para pasar el xml generado a la AEAT
 
Hola, adjunto la funcion en c# par aenviar la consulta a la aeat, sin usar la funcion del xlsd, para evitar los fallos de parseo.
Código:

        /// <summary>
        /// Envio el registro de la factura a la aeat
        /// </summary>
        /// <param name="facAPresentar">Registro creado con las funciones de la aeat.</param>
        /// <param name="certificado">arreglo en bytes que contiene el certificado</param>
        /// <param name="passcertificado">Password necesario para acceder al certificado</param>
        /// <returns>Devuelve la respuesta completa de la aeat</returns>
        public string consultaAEAT(RegFactuSistemaFacturacion facAPresentar,byte[] certificado,string passcertificado)
        {
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            XmlSerializer serializaFactura = new XmlSerializer(typeof(RegFactuSistemaFacturacion));
            string facXml = "";
            using (var xmlw = new StringWriter())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.Encoding = Encoding.UTF8;

                using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                {
                    serializaFactura.Serialize(writter, facAPresentar);
                    facXml = xmlw.ToString();
                }

            }
            facXml = facXml.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soapenv:Header /> <soapenv:Body >");
            facXml = facXml.Replace("<RegFactuSistemaFacturacion xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", "<RegFactuSistemaFacturacion xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd\">");
            //facXml = facXml.Replace("</SistemaInformatico>", "</SistemaInformatico> \r\n <FechaHoraHusoGenRegistro xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd\">" + timeStamp +"</FechaHoraHusoGenRegistro>");
            facXml = facXml + " </soapenv:Body> </soapenv:Envelope > ";
            ////////////////////////// <----- Prueba extraccion sin serializar
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(facXml.Replace("utf-16", "utf-8"));
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP");
            //webRequest.Headers.Add("SOAPAction", action);
            webRequest.ContentType = "text/xml;charset=\"utf-8\"";
            webRequest.Accept = "text/xml";
            webRequest.Method = "POST";
            X509Certificate2 certificate = new X509Certificate2(certificado, passcertificado); //requiere estas variables globalescontienen el certificado y su clave
            var tiempo = certificate.SubjectName;
            if (certificate == null)
                throw new ArgumentNullException(
                    "Certificate is null. Maybe serial number in configuration was wrong.");

            if (certificate.NotAfter < DateTime.Now)
                throw new ArgumentNullException(
                  $"Certificate is out of date. NotAfter: {certificate.NotAfter}.");

            webRequest.ClientCertificates.Add(certificate);

            using (Stream stream = webRequest.GetRequestStream())
            {
                doc.Save(stream);
            }
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            string statusDescription = response.StatusDescription;
            Stream dataStream = response.GetResponseStream();
            string responseFromServer;
            using (StreamReader reader = new StreamReader(dataStream))
            {
                responseFromServer = reader.ReadToEnd();
                reader.Close();
                dataStream.Close();
                response.Close();
            }
            return responseFromServer;
        }

La funcion se encarga de esxtraer el xml , formatearlo como soap y enviarlo con el certificado que pasamos como parametro.
Se usaria asi.
Cita:

string retornoConsulta = consultaAEAT(facAPresentar, Program.certificado, Program.passcertificado);

Ya me comentais si a alguien le ha resultado util.

Neftali [Germán.Estévez] 22-10-2024 15:25:21

Cita:

Empezado por bmfranky (Mensaje 558594)
Ya me comentais si a alguien le ha resultado util.


Gracias por el aporte.
Actualizo el mensaje#2

rci 22-10-2024 16:02:21

Cita:

Empezado por bmfranky (Mensaje 558594)
Hola, adjunto la funcion en c# par aenviar la consulta a la aeat, sin usar la funcion del xlsd, para evitar los fallos de parseo.
....
Ya me comentais si a alguien le ha resultado util.


Muchas gracias bmfranky, de esta forma si puedo obtener el html completo, que en mi caso es una página web de hacienda con el error 403 indicando que hay algún problema con el certificado. a ver si puedo sacar textos limpios para mostrar al usuario.

Saludos

bmfranky 22-10-2024 16:59:26

Cita:

Empezado por rci (Mensaje 558596)
Muchas gracias bmfranky, de esta forma si puedo obtener el html completo, que en mi caso es una página web de hacienda con el error 403 indicando que hay algún problema con el certificado. a ver si puedo sacar textos limpios para mostrar al usuario.

Saludos


Hola, puede probar esto, a mi me funciona, me devuelve el codigo de error y el texto del mismo.


Cita:

string retornoConsulta = consultaAEAT(facAPresentar, Program.certificado, Program.passcertificado);
string numError = "";
string CodigoError = "";
Match error = Regex.Match(retornoConsulta, "text-danger\">(.*?)</p>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (error.Length > 0)
{
numError = error.Value;
numError = numError.Remove(numError.IndexOf("</h1>"));
numError = numError.Replace("text-danger\">", "");
CodigoError = error.Value;
CodigoError = CodigoError.Remove(0, error.Value.IndexOf("<p>")+3);
CodigoError = CodigoError.Replace("</p>","");
}
MessageBox.Show("Error " + numError + Environment.NewLine + WebUtility.HtmlDecode(CodigoError), "Ha ocurrido un error en la transmision a la AEAT");

rci 22-10-2024 17:16:10

Cita:

Empezado por bmfranky (Mensaje 558597)
Hola, puede probar esto, a mi me funciona, me devuelve el codigo de error y el texto del mismo.

Lo acabo de probar y me funciona. Muchas gracias bmfranky

ISCOPYME 22-10-2024 18:39:23

Error con httprio
 
1 Archivos Adjunto(s)
al ejecutar la siguiente línea de código :

var res:RespuestaRegFactuSistemaFacturacion := RespuestaRegFactuSistemaFacturacion.Create;

try
----> res := GetsfPortTypeVerifactu( False, '', HTTPRIO1 ).RegFactuSistemaFacturacion( RegFactuSisFactu );
except
end;

nincillo 22-10-2024 19:51:24

Cita:

Empezado por bmfranky (Mensaje 558594)
Hola, adjunto la funcion en c# par aenviar la consulta a la aeat, sin usar la funcion del xlsd, para evitar los fallos de parseo.
Código:

        /// <summary>
        /// Envio el registro de la factura a la aeat
        /// </summary>
        /// <param name="facAPresentar">Registro creado con las funciones de la aeat.</param>
        /// <param name="certificado">arreglo en bytes que contiene el certificado</param>
        /// <param name="passcertificado">Password necesario para acceder al certificado</param>
        /// <returns>Devuelve la respuesta completa de la aeat</returns>
        public string consultaAEAT(RegFactuSistemaFacturacion facAPresentar,byte[] certificado,string passcertificado)
        {
            System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            XmlSerializer serializaFactura = new XmlSerializer(typeof(RegFactuSistemaFacturacion));
            string facXml = "";
            using (var xmlw = new StringWriter())
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                settings.Encoding = Encoding.UTF8;

                using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                {
                    serializaFactura.Serialize(writter, facAPresentar);
                    facXml = xmlw.ToString();
                }

            }
            facXml = facXml.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">  <soapenv:Header /> <soapenv:Body >");
            facXml = facXml.Replace("<RegFactuSistemaFacturacion xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">", "<RegFactuSistemaFacturacion xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd\">");
            //facXml = facXml.Replace("</SistemaInformatico>", "</SistemaInformatico> \r\n <FechaHoraHusoGenRegistro xmlns=\"https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd\">" + timeStamp +"</FechaHoraHusoGenRegistro>");
            facXml = facXml + " </soapenv:Body> </soapenv:Envelope > ";
            ////////////////////////// <----- Prueba extraccion sin serializar
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(facXml.Replace("utf-16", "utf-8"));
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP");
            //webRequest.Headers.Add("SOAPAction", action);
            webRequest.ContentType = "text/xml;charset=\"utf-8\"";
            webRequest.Accept = "text/xml";
            webRequest.Method = "POST";
            X509Certificate2 certificate = new X509Certificate2(certificado, passcertificado); //requiere estas variables globalescontienen el certificado y su clave
            var tiempo = certificate.SubjectName;
            if (certificate == null)
                throw new ArgumentNullException(
                    "Certificate is null. Maybe serial number in configuration was wrong.");

            if (certificate.NotAfter < DateTime.Now)
                throw new ArgumentNullException(
                  $"Certificate is out of date. NotAfter: {certificate.NotAfter}.");

            webRequest.ClientCertificates.Add(certificate);

            using (Stream stream = webRequest.GetRequestStream())
            {
                doc.Save(stream);
            }
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            string statusDescription = response.StatusDescription;
            Stream dataStream = response.GetResponseStream();
            string responseFromServer;
            using (StreamReader reader = new StreamReader(dataStream))
            {
                responseFromServer = reader.ReadToEnd();
                reader.Close();
                dataStream.Close();
                response.Close();
            }
            return responseFromServer;
        }

La funcion se encarga de esxtraer el xml , formatearlo como soap y enviarlo con el certificado que pasamos como parametro.
Se usaria asi.


Ya me comentais si a alguien le ha resultado util.


Hola, estoy planteándome hacer el desarrollo en Delphi o en C# (mundo en el que estoy empezando por completo). ¿Podrías poner un pequeño ejemplo de como una vez importado el wsdl se van inicializando y rellenando los nodos de la factura para posteriormente hacer el envío?. Gracias de antemano.

bmfranky 22-10-2024 20:05:17

Ejemplo paso a paso c#
 
Código:

              //creamos la factura paso a paso.
                RegFactuSistemaFacturacion facAPresentar = new RegFactuSistemaFacturacion();

                //rellenamos la cabecera
                ServicioVerifactu.Cabecera cabecera = new ServicioVerifactu.Cabecera();

                //creamos obligado presentador y se lo asignamos.
                ServicioVerifactu.PersonaFisicaJuridicaESType yo = new ServicioVerifactu.PersonaFisicaJuridicaESType();
                yo.NIF = Program.configTaller.Nif;
                yo.NombreRazon = Program.configTaller.NombreTaller;
                //se lo asignamos a la cabecera
                cabecera.ObligadoEmision = yo;

                //Creamos especificacion de remision voluntaria y la asignamos a la cabecera
                ServicioVerifactu.CabeceraRemisionVoluntaria voluntaria = new ServicioVerifactu.CabeceraRemisionVoluntaria();

                voluntaria.Incidencia = ServicioVerifactu.IncidenciaType.N;
                voluntaria.FechaFinVeriFactu = "";
                voluntaria.IncidenciaSpecified = true;
                //voluntaria.IncidenciaSpecified = false;
                cabecera.RemisionVoluntaria = voluntaria;

                // Asignamos la cabecera a la factura.
                facAPresentar.Cabecera = cabecera;

                //creamos el registro de factura podrian ser varios, pero solo asignaremos uno.
                ServicioVerifactu.RegistroFacturaType[] registroFactura = new ServicioVerifactu.RegistroFacturaType[1];

                //Como vamos a dar de alta la factura , creamos el registro de alta
                ServicioVerifactu.RegistroFacturacionAltaType altaFactura = new ServicioVerifactu.RegistroFacturacionAltaType();

                //Asignamos el ID version
                altaFactura.IDVersion = ServicioVerifactu.VersionType.Item10;//de momento la unica accesible

                //Identificamos la factura y lo asignamos a el Alta
                ServicioVerifactu.IDFacturaExpedidaType iDFactura = new ServicioVerifactu.IDFacturaExpedidaType();
                iDFactura.FechaExpedicionFactura = fechaFactura.Text;//Fecha de presentacion de la factura.
                iDFactura.IDEmisorFactura = Program.configTaller.Nif;//NIF del obligado emisor
                iDFactura.NumSerieFactura = numFactura;//numeo de factura y serie
                altaFactura.IDFactura = iDFactura;
                Program.facturaSeleccionada.Fecha = fechaFactura.Text;
                //asignamos nombre expedidor
                altaFactura.NombreRazonEmisor = Program.configTaller.NombreTaller;

                //Es por subsanacion ?
                altaFactura.Subsanacion = ServicioVerifactu.SubsanacionType.N;
                // Se havia rechazado ?
                altaFactura.RechazoPrevio = ServicioVerifactu.RechazoPrevioType.N;
                if(alta == "RECHAZO")
                {
                    //Es por subsanacion ?
                    altaFactura.Subsanacion = ServicioVerifactu.SubsanacionType.S;
                    // Se havia rechazado ?
                    altaFactura.RechazoPrevio = ServicioVerifactu.RechazoPrevioType.X;
                }
                if (alta == "SUBSANACION")
                {
                    //Es por subsanacion ?
                    altaFactura.Subsanacion = ServicioVerifactu.SubsanacionType.S;
                    // Se havia rechazado ?
                    altaFactura.RechazoPrevio = ServicioVerifactu.RechazoPrevioType.N;
                }
                if (alta == "RECHAZO_SUBSANACION")
                {
                    //Es por subsanacion ?
                    altaFactura.Subsanacion = ServicioVerifactu.SubsanacionType.S;
                    // Se havia rechazado ?
                    altaFactura.RechazoPrevio = ServicioVerifactu.RechazoPrevioType.S;
                }
                //Que tipo de factura estamos creando?
                altaFactura.TipoFactura = ServicioVerifactu.ClaveTipoFacturaType.F1;
                /*

                //Si es rectificativa lo indicamos y cumplimentamos los datos requeridos  ya sea rectificada o sustituida son el mismo diseño
                altaFactura.TipoRectificativa = ServicioVerifactu.ClaveTipoRectificativaType.I;
                Program.FacturaSeleccionada.Tipo=
                //aqui consignariamos los datos de las facturas para sutitucion y demas.
                int numeroFaR = 1;
                ServicioVerifactu.IDFacturaARType[] listFacturasRectificadas = new ServicioVerifactu.IDFacturaARType[numeroFaR];// creamos lista de facturas rectificadas.
                ServicioVerifactu.IDFacturaARType facSust = new ServicioVerifactu.IDFacturaARType();//rellenamos la primera
                facSust.FechaExpedicionFactura = "FECHA";          //Fecha de emision factura sustituida
                facSust.IDEmisorFactura = Program.configTaller.Nif;//Nif expedidor.
                facSust.NumSerieFactura = "Num Serie";            //Numero de serie factura sustituida
                listFacturasRectificadas[1] = facSust;            //le asignamos una por una las facturas rectificadas
                altaFactura.FacturasSustituidas = listFacturasRectificadas; //Asignamos al mandato

                //creamos desglose de la rectificacion con los totales a rectificar
                ServicioVerifactu.DesgloseRectificacionType desgloseRectificacion = new ServicioVerifactu.DesgloseRectificacionType();
                desgloseRectificacion.BaseRectificada = "";        // Cuota base a rectificar
                desgloseRectificacion.CuotaRecargoRectificado = "";// Hay recargo equivalencia ??
                desgloseRectificacion.CuotaRectificada = "";      //Cuota iva rectificada;
                altaFactura.ImporteRectificacion = desgloseRectificacion; // asignamos importe de la rectificacion

                */
                //Asignamos la fecha en que se izo la operacion si no es en la misma que se expide la factura, en este caso si es la misma, si fuera de reparacion puede variar
                altaFactura.FechaOperacion = fechaFactura.Text.Replace("/", "-");

                //Asignamos motivo descripcion de la operacion , en este caso venta de materiales;
                altaFactura.DescripcionOperacion = "Venta de materiales servidos.";// en caso de reparacion de vehiculo asignar aqui la reparacion.

                //Es factura simplificada s/n
                altaFactura.FacturaSimplificadaArt7273 = ServicioVerifactu.SimplificadaCualificadaType.N;

                //Es factura sin identificar destinatario? s/n
                altaFactura.FacturaSinIdentifDestinatarioArt61d = ServicioVerifactu.CompletaSinDestinatarioType.N;

                //por tercero ??
                //altaFactura.EmitidaPorTerceroODestinatario = ServicioVerifactu.TercerosODestinatarioType.T;
                /*
                // aqui se consignaria todo lo de terceros.
                ServicioVerifactu.PersonaFisicaJuridicaType Tercero = new ServicioVerifactu.PersonaFisicaJuridicaType();
                Tercero.NombreRazon = "";
                ServicioVerifactu.IDOtroType idTercero = new ServicioVerifactu.IDOtroType();
                idTercero.CodigoPais = ServicioVerifactu.CountryType2.ES;
                idTercero.ID = "nif"; //identificador
                idTercero.IDType = ServicioVerifactu.PersonaFisicaJuridicaIDTypeType.Item02;
                Tercero.Item = idTercero;
                altaFactura.Tercero = Tercero;
                */
                int numDestinatarios = 1;
                ServicioVerifactu.PersonaFisicaJuridicaType[] destinatarios = new ServicioVerifactu.PersonaFisicaJuridicaType[numDestinatarios];//lista de destinatarios, para mi siempre 1
                                                                                                                                                //primer destinatario
                ServicioVerifactu.PersonaFisicaJuridicaType destinatario = new ServicioVerifactu.PersonaFisicaJuridicaType(); //creamos nuevo destinatario
                if (cmbPais.Text != "ESPAÑA" || _documento.Text != "NIF-IVA")
                {
                    ServicioVerifactu.IDOtroType idOtro = new ServicioVerifactu.IDOtroType();
                    destinatario.NombreRazon = nom.Text + " " + Apellido1.Text + " " + Apellido2.Text;
                    idOtro.CodigoPais = ServicioVerifactu.CountryType2.ES;// hallar el pais
                    idOtro.ID = Dni.Text;
                    idOtro.IDType = tipoPersona(_documento.SelectedIndex);
                    destinatario.Item = idOtro;
                }
                else
                {
                    //ServicioVerifactu.PersonaFisicaJuridicaESType cliente = new ServicioVerifactu.PersonaFisicaJuridicaESType();
                    destinatario.NombreRazon = nom.Text + " " + Apellido1.Text + " " + Apellido2.Text;
                    //cliente.NIF = Dni.Text;
                    //cliente.NombreRazon = nom.Text + " " + Apellido1.Text + " " + Apellido2.Text;
                    destinatario.Item = Dni.Text; //cliente;
                }


                destinatarios[0] = destinatario;
                //se los asignamos a la factura
                altaFactura.Destinatarios = destinatarios;
                // ahora creamos el desglose uno por tipo de iva
                ServicioVerifactu.DetalleType[] desgloses = new ServicioVerifactu.DetalleType[numeroIvas];


                //iva 21%
                if (asignado21)
                {
                    ServicioVerifactu.DetalleType tipo21 = new ServicioVerifactu.DetalleType();
                    tipo21.Impuesto = ServicioVerifactu.ImpuestoType.Item01;
                    tipo21.ClaveRegimen = ServicioVerifactu.IdOperacionesTrascendenciaTributariaType.Item01;//Operaciones regimen general
                    tipo21.ClaveRegimenSpecified = true;
                    tipo21.Item = ServicioVerifactu.CalificacionOperacionType.S1;//no esta exenta ni es por inversion del sujeto pasivo...
                    tipo21.TipoImpositivo = "21.00";

                    tipo21.BaseImponibleOimporteNoSujeto = (base21 + totalPortes).ToString("F2").Replace(",", ".");// impon21.Text.Replace(",", ".").Replace(" €", "");
                    bases = bases + base21 + totalPortes;// float.Parse(impon21.Text.Replace(" €",""));
                    tipo21.CuotaRepercutida = float.Parse(cuota21.Text.Replace(" €", "")).ToString("F2").Replace(",", ".");// cuota21.Text.Replace(",", ".").Replace(" €", "");
                    cuotas = cuotas + float.Parse(cuota21.Text.Replace(" €", ""));
                    desgloses[numDetalle] = tipo21;
                    numDetalle++;
                }
                //iva10%
                if (asignado10)
                {
                    ServicioVerifactu.DetalleType tipo10 = new ServicioVerifactu.DetalleType();
                    tipo10.ClaveRegimenSpecified = true;
                    tipo10.Impuesto = ServicioVerifactu.ImpuestoType.Item01;
                    tipo10.ClaveRegimen = ServicioVerifactu.IdOperacionesTrascendenciaTributariaType.Item01;//Operaciones regimen general
                    tipo10.Item = ServicioVerifactu.CalificacionOperacionType.S1;//no esta exenta ni es por inversion del sujeto pasivo...
                    tipo10.TipoImpositivo = "10.00";
                    tipo10.BaseImponibleOimporteNoSujeto = float.Parse(impon10.Text.Replace(" €", "")).ToString("F2").Replace(",", ".");// impon10.Text.Replace(",", ".").Replace(" €", "");
                    bases = bases + float.Parse(impon10.Text.Replace(" €", ""));
                    tipo10.CuotaRepercutida = float.Parse(cuota10.Text.Replace(" €", "")).ToString("F2").Replace(",", ".");// cuota10.Text.Replace(",", ".").Replace(" €", "");
                    cuotas = cuotas + float.Parse(cuota10.Text.Replace(" €", ""));
                    desgloses[numDetalle] = tipo10;
                    numDetalle++;
                }
                //Iva4%
                if (asignado4)
                {
                    ServicioVerifactu.DetalleType tipo04 = new ServicioVerifactu.DetalleType();
                    tipo04.Impuesto = ServicioVerifactu.ImpuestoType.Item01;
                    tipo04.ClaveRegimen = ServicioVerifactu.IdOperacionesTrascendenciaTributariaType.Item01;//Operaciones regimen general
                    tipo04.ClaveRegimenSpecified = true;
                    tipo04.Item = ServicioVerifactu.CalificacionOperacionType.S1;//no esta exenta ni es por inversion del sujeto pasivo...
                    tipo04.TipoImpositivo = "04.00";
                    tipo04.BaseImponibleOimporteNoSujeto = float.Parse(impon4.Text.Replace(" €", "")).ToString("F2").Replace(",", "."); //impon4.Text.Replace(",", ".").Replace(" €", "");
                    bases = bases + float.Parse(impon4.Text.Replace(" €", ""));
                    tipo04.CuotaRepercutida = float.Parse(cuota10.Text.Replace(" €", "")).ToString("F2").Replace(",", "."); // cuota4.Text.Replace(",", ".").Replace(" €", "");
                    cuotas = cuotas + float.Parse(cuota4.Text.Replace(" €", ""));
                    desgloses[numDetalle] = tipo04;
                    numDetalle++;
                }
                //sin iva
                if (asignado0)
                {
                    ServicioVerifactu.DetalleType tipo0 = new ServicioVerifactu.DetalleType();
                    tipo0.Impuesto = ServicioVerifactu.ImpuestoType.Item01;
                    tipo0.ClaveRegimen = ServicioVerifactu.IdOperacionesTrascendenciaTributariaType.Item01;//Operaciones regimen general
                    tipo0.ClaveRegimenSpecified = true;
                    tipo0.Item = ServicioVerifactu.CalificacionOperacionType.N1;//si esta exenta
                    tipo0.TipoImpositivo = "0.00";
                    tipo0.BaseImponibleOimporteNoSujeto = float.Parse(impon0.Text.Replace(" €", "")).ToString("F2").Replace(",", ".");// impon0.Text.Replace(",", ".").Replace(" €", "");
                    bases = bases + float.Parse(impon0.Text.Replace(" €", ""));
                    //tipo0.CuotaRepercutida = cuota0.Text;
                    desgloses[numDetalle] = tipo0;
                    numDetalle++;
                }

                // se lo asignamos a la factura
                altaFactura.Desglose = desgloses;
                //asignamos total impuestos
                altaFactura.CuotaTotal = cuotas.ToString("F2").Replace(",", ".");
                total = cuotas + bases;
                //asignamos total factura;
                altaFactura.ImporteTotal = total.ToString("F2").Replace(",", ".");

                //creamos el encadenamiento
                ServicioVerifactu.RegistroFacturacionAltaTypeEncadenamiento cadenaFactura = new ServicioVerifactu.RegistroFacturacionAltaTypeEncadenamiento();
                Huella huella = new Huella();
                HuellaVerifactu huellaAnterior = new HuellaVerifactu();
                string _huella = "";
                if (huella.numeroHash() > 0)
                {
                    ServicioVerifactu.EncadenamientoFacturaAnteriorType anterior = new ServicioVerifactu.EncadenamientoFacturaAnteriorType();
                    huellaAnterior = huella.huellaAnterior();
                    anterior.FechaExpedicionFactura = huellaAnterior.FechaFactura;
                    anterior.IDEmisorFactura = huellaAnterior.IdEmisor;
                    anterior.NumSerieFactura = huellaAnterior.NumFactura;
                    anterior.Huella = huellaAnterior.Huella;
                    _huella = huellaAnterior.Huella;
                    cadenaFactura.Item = anterior;
                }
                else
                {
                    cadenaFactura.Item = ServicioVerifactu.PrimerRegistroCadenaType.S;
                }
                altaFactura.Encadenamiento = cadenaFactura;
                //ahora identificamos el software
                ServicioVerifactu.SistemaInformaticoType programaFacturacion = new ServicioVerifactu.SistemaInformaticoType();
                programaFacturacion.NombreSistemaInformatico = "Facturas Taller 2024";//nombre programa
                programaFacturacion.NombreRazon = "  ";
                programaFacturacion.Item = " ";
                programaFacturacion.Version = Program.version;
                programaFacturacion.IdSistemaInformatico = "A1";
                programaFacturacion.NumeroInstalacion = Program.numeroInstalacion;//numero de instalacion
                programaFacturacion.TipoUsoPosibleSoloVerifactu = ServicioVerifactu.SiNoType.S;//es verifactu
                programaFacturacion.TipoUsoPosibleMultiOT = ServicioVerifactu.SiNoType.N;//varias empresas a la vez
                programaFacturacion.IndicadorMultiplesOT = ServicioVerifactu.SiNoType.N;
                //ServicioVerifactu.PersonaFisicaJuridicaType[] Destinatarios = new ServicioVerifactu.PersonaFisicaJuridicaType[1];
                altaFactura.SistemaInformatico = programaFacturacion;//asignamos tipo systema
                altaFactura.TipoHuella = ServicioVerifactu.TipoHuellaType.Item01;//solo se admite un tipo
                var fechora = DateTime.Now;
                timeStamp = fechora.ToString("yyyy-MM-dd'T'HH:mm:ssK");
                altaFactura.FechaHoraHusoGenRegistro = timeStamp;// - new TimeSpan(0, 0, 0, 0, fechora.TimeOfDay.Milliseconds);

                //timeStamp = fechora.ToString("yyyy-MM-dd'T'HH:mm:ssK");
                altaFactura.Huella = huella.creaHuellaFactura(Program.configTaller.Nif, numFactura, fechaFactura.Text, tipoFactura, cuotas.ToString("F2").Replace(",", "."), total.ToString("F2").Replace(",", "."), _huella, timeStamp);//asignamos huella

                //registroFactura[1].Item = altaFactura;
                ServicioVerifactu.RegistroFacturaType[] fac = new ServicioVerifactu.RegistroFacturaType[1];
                //fac.Initialize();
                fac[0] = new ServicioVerifactu.RegistroFacturaType();
                fac[0].Item = altaFactura;
                facAPresentar.RegistroFactura = fac;

                //ServicioVerifactu.RegFactuSistemaFacturacionRequest envio = new ServicioVerifactu.RegFactuSistemaFacturacionRequest();
                //envio.RegFactuSistemaFacturacion = facAPresentar;
                /////////////////////////////  <--- Prueba de serializar la factura --->
                XmlSerializer serializaFactura = new XmlSerializer(typeof(ServicioVerifactu.RegFactuSistemaFacturacion));
                SoapAttributes soap = new SoapAttributes();

                string facXml = "";
                using (var xmlw = new StringWriter())
                {
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Indent = true;
                    settings.Encoding = Encoding.UTF8;

                    using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                    {
                        serializaFactura.Serialize(writter, facAPresentar);
                        facXml = xmlw.ToString();
                    }

                }

                System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

                RegFactuSistemaFacturacionResponse response = new ServicioVerifactu.RegFactuSistemaFacturacionResponse();
                // response.RespuestaRegFactuSistemaFacturacion = respuesta;
                ServicioVerifactu.RegFactuSistemaFacturacionRequest req = new ServicioVerifactu.RegFactuSistemaFacturacionRequest(facAPresentar);
                req.RegFactuSistemaFacturacion.Cabecera = cabecera;

                ServicioVerifactu.X509DataType x59 = new ServicioVerifactu.X509DataType();


                ServicioVerifactu.RSAKeyValueType key = new ServicioVerifactu.RSAKeyValueType();

                sfPortTypeVerifactuClient client = new ServicioVerifactu.sfPortTypeVerifactuClient(Program.enlaceVerifactu);
                System.ServiceModel.Description.ClientCredentials credenciales = new System.ServiceModel.Description.ClientCredentials();
                //client.ClientCredentials.UserName.UserName = Program.configTaller.Nif;
                client.ClientCredentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Program.certificado, Program.passcertificado);
                client.ClientCredentials.UseIdentityConfiguration = true;
                client.ClientCredentials.SupportInteractive = true;
                //client.ClientCredentials.ServiceCertificate.DefaultCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Program.certificado, Program.passcertificado);

                string test = "";
                //client.Open();
                RespuestaRegFactuSistemaFacturacionType retorno = new RespuestaRegFactuSistemaFacturacionType();
                try
                {
                    retorno = client.RegFactuSistemaFacturacion(facAPresentar);
                }


Recuerden al importar el webservice cambiar en reference System.DateTyme por string, hay un fallo al serializar la fecha , incluye milisegundos lo que da error, esta mas o menos explicado.
Simplemente han de cambiar de donde rellenar los campos, y por ejemplo si envian varios registros a la vez tenerlño en cuenta.

bmfranky 22-10-2024 20:13:54

Hola, esta parte de aqui,
Código:

string facXml = "";
                using (var xmlw = new StringWriter())
                {
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Indent = true;
                    settings.Encoding = Encoding.UTF8;

                    using (XmlWriter writter = XmlWriter.Create(xmlw, settings))
                    {
                        serializaFactura.Serialize(writter, facAPresentar);
                        facXml = xmlw.ToString();
                    }

                }

Es para extraer los registros en texto plano, yo lo uso par ver si se han generado bien los campos, poniendo un punto de interrupcion miramos la variable facXml


La franja horaria es GMT +2. Ahora son las 03:37:57.

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