Updates for Verifactu

This commit is contained in:
David Bomba 2025-05-02 18:06:26 +10:00
parent f155feb641
commit b25a69a5be
19 changed files with 321 additions and 12 deletions

View File

@ -2,18 +2,24 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class Cabecera
{
/** @var PersonaFisicaJuridicaES */
#[SerializedName('sum1:ObligadoEmision')]
protected $ObligadoEmision;
/** @var PersonaFisicaJuridicaES|null */
#[SerializedName('sum1:Representante')]
protected $Representante;
/** @var array{FechaFinVeriFactu?: string}|null */
#[SerializedName('sum1:RemisionVoluntaria')]
protected $RemisionVoluntaria;
/** @var array{RefRequerimiento: string, FinRequerimiento?: string}|null */
#[SerializedName('sum1:RemisionRequerimiento')]
protected $RemisionRequerimiento;
public function getObligadoEmision(): PersonaFisicaJuridicaES

View File

@ -2,15 +2,20 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class DesgloseRectificacion
{
/** @var float */
#[SerializedName('sum1:BaseRectificada')]
protected $BaseRectificada;
/** @var float */
#[SerializedName('sum1:CuotaRectificada')]
protected $CuotaRectificada;
/** @var float|null */
#[SerializedName('sum1:CuotaRecargoRectificada')]
protected $CuotaRecargoRectificada;
public function getBaseRectificada(): float

View File

@ -2,15 +2,20 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class IDFacturaExpedida
{
/** @var string NIF format */
#[SerializedName('sum1:IDEmisorFactura')]
protected $IDEmisorFactura;
/** @var string */
#[SerializedName('sum1:NumSerieFactura')]
protected $NumSerieFactura;
/** @var string Date format YYYY-MM-DD */
#[SerializedName('sum1:FechaExpedicionFactura')]
protected $FechaExpedicionFactura;
public function getIDEmisorFactura(): string

View File

@ -3,12 +3,14 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use App\Services\EDocument\Standards\Verifactu\Types\Common\TextTypes;
use Symfony\Component\Serializer\Annotation\SerializedName;
class ImporteSgn14_2
{
use TextTypes;
/** @var string */
#[SerializedName('sum1:Value')]
protected $Value;
public function __construct(string $value)

View File

@ -2,21 +2,28 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class Incidencia
{
/** @var string */
#[SerializedName('sum1:Codigo')]
protected $Codigo;
/** @var string */
#[SerializedName('sum1:Descripcion')]
protected $Descripcion;
/** @var string|null Max length 120 characters */
#[SerializedName('sum1:NombreRazon')]
protected $NombreRazon;
/** @var string|null NIF format */
#[SerializedName('sum1:NIF')]
protected $NIF;
/** @var string|null */
#[SerializedName('sum1:FechaHora')]
protected $FechaHora;
public function getCodigo(): string

View File

@ -2,39 +2,52 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class ObligadoEmision extends PersonaFisicaJuridicaES
{
/** @var string|null */
#[SerializedName('sum1:TipoPersona')]
protected $TipoPersona;
/** @var string|null */
#[SerializedName('sum1:RazonSocialCompleta')]
protected $RazonSocialCompleta;
/** @var string|null */
#[SerializedName('sum1:NombreComercial')]
protected $NombreComercial;
/** @var string|null */
#[SerializedName('sum1:CodigoPostal')]
protected $CodigoPostal;
/** @var string|null */
#[SerializedName('sum1:Direccion')]
protected $Direccion;
/** @var string|null */
#[SerializedName('sum1:Poblacion')]
protected $Poblacion;
/** @var string|null */
#[SerializedName('sum1:Provincia')]
protected $Provincia;
/** @var string|null */
#[SerializedName('sum1:Pais')]
protected $Pais;
/** @var string|null */
#[SerializedName('sum1:Telefono')]
protected $Telefono;
/** @var string|null */
#[SerializedName('sum1:Email')]
protected $Email;
/** @var string|null */
#[SerializedName('sum1:Web')]
protected $Web;
public function getTipoPersona(): ?string

View File

@ -2,6 +2,8 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class OperacionExenta
{
public const E1 = 'E1'; // EXENTA por Art. 20
@ -12,6 +14,7 @@ class OperacionExenta
public const E6 = 'E6'; // EXENTA por otros
/** @var string */
#[SerializedName('sum1:Value')]
protected $Value;
public function __construct(string $value)

View File

@ -2,15 +2,20 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class RechazoPrevio
{
/** @var string */
#[SerializedName('sum1:NumRegistroAcuerdoFacturacion')]
protected $NumRegistroAcuerdoFacturacion;
/** @var string */
#[SerializedName('sum1:FechaRegistroAcuerdoFacturacion')]
protected $FechaRegistroAcuerdoFacturacion;
/** @var string */
#[SerializedName('sum1:MotivoRechazo')]
protected $MotivoRechazo;
public function getNumRegistroAcuerdoFacturacion(): string

View File

@ -2,33 +2,37 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class RegFactuSistemaFacturacion
{
/** @var PersonaFisicaJuridicaES */
protected $ObligadoEmision;
/** @var Cabecera */
#[SerializedName('sum:Cabecera')]
protected $Cabecera;
/** @var RegistroAlta */
protected $RegistroAlta;
/** @var RegistroFactura */
#[SerializedName('sum:RegistroFactura')]
protected $RegistroFactura;
public function getObligadoEmision(): PersonaFisicaJuridicaES
public function getCabecera(): Cabecera
{
return $this->ObligadoEmision;
return $this->Cabecera;
}
public function setObligadoEmision(PersonaFisicaJuridicaES $obligadoEmision): self
public function setCabecera(Cabecera $cabecera): self
{
$this->ObligadoEmision = $obligadoEmision;
$this->Cabecera = $cabecera;
return $this;
}
public function getRegistroAlta(): RegistroAlta
public function getRegistroFactura(): RegistroFactura
{
return $this->RegistroAlta;
return $this->RegistroFactura;
}
public function setRegistroAlta(RegistroAlta $registroAlta): self
public function setRegistroFactura(RegistroFactura $registroFactura): self
{
$this->RegistroAlta = $registroAlta;
$this->RegistroFactura = $registroFactura;
return $this;
}
}

View File

@ -2,48 +2,64 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class RegistroAlta
{
/** @var string */
#[SerializedName('sum1:IDVersion')]
protected $IDVersion;
/** @var IDFactura */
#[SerializedName('sum1:IDFactura')]
protected $IDFactura;
/** @var string */
#[SerializedName('sum1:NombreRazonEmisor')]
protected $NombreRazonEmisor;
/** @var string */
#[SerializedName('sum1:TipoFactura')]
protected $TipoFactura;
/** @var string */
#[SerializedName('sum1:DescripcionOperacion')]
protected $DescripcionOperacion;
/** @var Destinatarios */
#[SerializedName('sum1:Destinatarios')]
protected $Destinatarios;
/** @var Desglose */
#[SerializedName('sum1:Desglose')]
protected $Desglose;
/** @var float */
#[SerializedName('sum1:CuotaTotal')]
protected $CuotaTotal;
/** @var float */
#[SerializedName('sum1:ImporteTotal')]
protected $ImporteTotal;
/** @var Encadenamiento|null */
#[SerializedName('sum1:Encadenamiento')]
protected $Encadenamiento;
/** @var SistemaInformatico */
#[SerializedName('sum1:SistemaInformatico')]
protected $SistemaInformatico;
/** @var string */
#[SerializedName('sum1:FechaHoraHusoGenRegistro')]
protected $FechaHoraHusoGenRegistro;
/** @var string */
#[SerializedName('sum1:TipoHuella')]
protected $TipoHuella;
/** @var string */
#[SerializedName('sum1:Huella')]
protected $Huella;
public function getIDVersion(): string

View File

@ -2,9 +2,12 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class RegistroAnterior extends IDFactura
{
/** @var string */
#[SerializedName('sum1:Huella')]
protected $Huella;
public function getHuella(): string

View File

@ -2,42 +2,56 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class RegistroFacturacionAnulacion
{
/** @var string */
#[SerializedName('sum1:IDVersion')]
protected $IDVersion;
/** @var IDFacturaAR */
#[SerializedName('sum1:IDFactura')]
protected $IDFactura;
/** @var string|null Max length 70 characters */
#[SerializedName('sum1:RefExterna')]
protected $RefExterna;
/** @var string Max length 120 characters */
#[SerializedName('sum1:NombreRazonEmisor')]
protected $NombreRazonEmisor;
/** @var string|null Max length 2000 characters */
#[SerializedName('sum1:MotivoAnulacion')]
protected $MotivoAnulacion;
/** @var SistemaInformatico */
#[SerializedName('sum1:SistemaInformatico')]
protected $SistemaInformatico;
/** @var string */
#[SerializedName('sum1:Huella')]
protected $Huella;
/** @var string|null */
#[SerializedName('sum1:Signature')]
protected $Signature;
/** @var string */
#[SerializedName('sum1:FechaHoraHusoGenRegistro')]
protected $FechaHoraHusoGenRegistro;
/** @var string|null Max length 15 characters */
#[SerializedName('sum1:NumRegistroAcuerdoFacturacion')]
protected $NumRegistroAcuerdoFacturacion;
/** @var string|null Max length 16 characters */
#[SerializedName('sum1:IDAcuerdoSistemaInformatico')]
protected $IDAcuerdoSistemaInformatico;
/** @var string */
#[SerializedName('sum1:TipoHuella')]
protected $TipoHuella;
public function getIDVersion(): string

View File

@ -2,27 +2,36 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class SistemaInformatico extends PersonaFisicaJuridicaES
{
/** @var string */
#[SerializedName('sum1:NombreSistemaInformatico')]
protected $NombreSistemaInformatico;
/** @var string */
#[SerializedName('sum1:IdSistemaInformatico')]
protected $IdSistemaInformatico;
/** @var string */
#[SerializedName('sum1:Version')]
protected $Version;
/** @var string */
#[SerializedName('sum1:NumeroInstalacion')]
protected $NumeroInstalacion;
/** @var string */
#[SerializedName('sum1:TipoUsoPosibleSoloVerifactu')]
protected $TipoUsoPosibleSoloVerifactu;
/** @var string */
#[SerializedName('sum1:TipoUsoPosibleMultiOT')]
protected $TipoUsoPosibleMultiOT;
/** @var string */
#[SerializedName('sum1:IndicadorMultiplesOT')]
protected $IndicadorMultiplesOT;
public function getNombreSistemaInformatico(): string

View File

@ -0,0 +1,23 @@
<?php
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class SoapBody
{
/** @var RegFactuSistemaFacturacion */
#[SerializedName('sum:RegFactuSistemaFacturacion')]
protected $RegFactuSistemaFacturacion;
public function getRegFactuSistemaFacturacion(): RegFactuSistemaFacturacion
{
return $this->RegFactuSistemaFacturacion;
}
public function setRegFactuSistemaFacturacion(RegFactuSistemaFacturacion $regFactuSistemaFacturacion): self
{
$this->RegFactuSistemaFacturacion = $regFactuSistemaFacturacion;
return $this;
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class SoapEnvelope
{
/** @var SoapHeader */
#[SerializedName('soapenv:Header')]
protected $Header;
/** @var SoapBody */
#[SerializedName('soapenv:Body')]
protected $Body;
public function getHeader(): SoapHeader
{
return $this->Header;
}
public function setHeader(SoapHeader $header): self
{
$this->Header = $header;
return $this;
}
public function getBody(): SoapBody
{
return $this->Body;
}
public function setBody(SoapBody $body): self
{
$this->Body = $body;
return $this;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class SoapHeader
{
}

View File

@ -2,12 +2,16 @@
namespace App\Services\EDocument\Standards\Verifactu\Types;
use Symfony\Component\Serializer\Annotation\SerializedName;
class Subsanacion
{
/** @var string */
#[SerializedName('sum1:NumRegistroAcuerdoFacturacion')]
protected $NumRegistroAcuerdoFacturacion;
/** @var string */
#[SerializedName('sum1:FechaRegistroAcuerdoFacturacion')]
protected $FechaRegistroAcuerdoFacturacion;
public function getNumRegistroAcuerdoFacturacion(): string

View File

@ -0,0 +1,66 @@
<?php
namespace Tests\Feature\Verifactu;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use App\Services\EDocument\Standards\Verifactu\Types\SoapEnvelope;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
use App\Services\EDocument\Standards\Verifactu\Types\RegFactuSistemaFacturacion;
class SerializerTest extends TestCase
{
public function testDeserialize()
{
$document = file_get_contents(__DIR__ . '/invoice.xml');
$phpDocExtractor = new PhpDocExtractor();
$reflectionExtractor = new ReflectionExtractor();
// list of PropertyTypeExtractorInterface (any iterable)
$typeExtractors = [$reflectionExtractor,$phpDocExtractor];
// list of PropertyDescriptionExtractorInterface (any iterable)
$descriptionExtractors = [$phpDocExtractor];
// list of PropertyInitializableExtractorInterface (any iterable)
$propertyInitializableExtractors = [$reflectionExtractor];
$propertyInfo = new PropertyInfoExtractor(
$propertyInitializableExtractors,
$descriptionExtractors,
$typeExtractors,
);
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
$normalizer = new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null, $propertyInfo);
$normalizers = [new DateTimeNormalizer(), $normalizer, new ArrayDenormalizer()];
$encoders = [new XmlEncoder(['xml_format_output' => true,\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]), new JsonEncoder()];
$serializer = new Serializer($normalizers, $encoders);
$parent_class = SoapEnvelope::class;
$invoice = $serializer->deserialize($document, $parent_class, 'xml', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
// nlog($invoice);
echo(print_r($invoice, true));
}
}

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd"
xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
<soapenv:Header />
<soapenv:Body>
<sum:RegFactuSistemaFacturacion>
<sum:Cabecera>
<sum1:ObligadoEmision>
<sum1:NombreRazon>XXXXX</sum1:NombreRazon>
<sum1:NIF>AAAA</sum1:NIF>
</sum1:ObligadoEmision>
</sum:Cabecera>
<sum:RegistroFactura>
<sum1:RegistroAlta>
<sum1:IDVersion>1.0</sum1:IDVersion>
<sum1:IDFactura>
<sum1:IDEmisorFactura>AAAA</sum1:IDEmisorFactura>
<sum1:NumSerieFactura>12345</sum1:NumSerieFactura>
<sum1:FechaExpedicionFactura>13-09-2024</sum1:FechaExpedicionFactura>
</sum1:IDFactura>
<sum1:NombreRazonEmisor>XXXXX</sum1:NombreRazonEmisor>
<sum1:TipoFactura>F1</sum1:TipoFactura>
<sum1:DescripcionOperacion>Descripc</sum1:DescripcionOperacion>
<sum1:Destinatarios>
<sum1:IDDestinatario>
<sum1:NombreRazon>YYYY</sum1:NombreRazon>
<sum1:NIF>BBBB</sum1:NIF>
</sum1:IDDestinatario>
</sum1:Destinatarios>
<sum1:Desglose>
<sum1:DetalleDesglose>
<sum1:ClaveRegimen>01</sum1:ClaveRegimen>
<sum1:CalificacionOperacion>S1</sum1:CalificacionOperacion>
<sum1:TipoImpositivo>4</sum1:TipoImpositivo>
<sum1:BaseImponibleOimporteNoSujeto>10</sum1:BaseImponibleOimporteNoSujeto>
<sum1:CuotaRepercutida>0.4</sum1:CuotaRepercutida>
</sum1:DetalleDesglose>
<sum1:DetalleDesglose>
<sum1:ClaveRegimen>01</sum1:ClaveRegimen>
<sum1:CalificacionOperacion>S1</sum1:CalificacionOperacion>
<sum1:TipoImpositivo>21</sum1:TipoImpositivo>
<sum1:BaseImponibleOimporteNoSujeto>100</sum1:BaseImponibleOimporteNoSujeto>
<sum1:CuotaRepercutida>21</sum1:CuotaRepercutida>
</sum1:DetalleDesglose>
</sum1:Desglose>
<sum1:CuotaTotal>21.4</sum1:CuotaTotal>
<sum1:ImporteTotal>131.4</sum1:ImporteTotal>
<sum1:Encadenamiento>
<sum1:RegistroAnterior>
<sum1:IDEmisorFactura>AAAA</sum1:IDEmisorFactura>
<sum1:NumSerieFactura>44</sum1:NumSerieFactura>
<sum1:FechaExpedicionFactura>13-09-2024</sum1:FechaExpedicionFactura>
<sum1:Huella>HuellaRegistroAnterior</sum1:Huella>
</sum1:RegistroAnterior>
</sum1:Encadenamiento>
<sum1:SistemaInformatico>
<sum1:NombreRazon>SSSS</sum1:NombreRazon>
<sum1:NIF>NNNN</sum1:NIF>
<sum1:NombreSistemaInformatico>NombreSistemaInformatico</sum1:NombreSistemaInformatico>
<sum1:IdSistemaInformatico>77</sum1:IdSistemaInformatico>
<sum1:Version>1.0.03</sum1:Version>
<sum1:NumeroInstalacion>383</sum1:NumeroInstalacion>
<sum1:TipoUsoPosibleSoloVerifactu>N</sum1:TipoUsoPosibleSoloVerifactu>
<sum1:TipoUsoPosibleMultiOT>S</sum1:TipoUsoPosibleMultiOT>
<sum1:IndicadorMultiplesOT>S</sum1:IndicadorMultiplesOT>
</sum1:SistemaInformatico>
<sum1:FechaHoraHusoGenRegistro>2024-09-13T19:20:30+01:00</sum1:FechaHoraHusoGenRegistro>
<sum1:TipoHuella>01</sum1:TipoHuella>
<sum1:Huella>Huella</sum1:Huella>
</sum1:RegistroAlta>
</sum:RegistroFactura>
</sum:RegFactuSistemaFacturacion>
</soapenv:Body>
</soapenv:Envelope>