Compare commits
25 Commits
v5-stable
...
2025_05_01
| Author | SHA1 | Date |
|---|---|---|
|
|
91a5457ab9 | |
|
|
8ea919f191 | |
|
|
15033f581b | |
|
|
89266be7ae | |
|
|
5f9ff21edf | |
|
|
b7754d1b8f | |
|
|
6bb576b949 | |
|
|
f1b374ee38 | |
|
|
e641825b3a | |
|
|
de690a8be5 | |
|
|
41b2387643 | |
|
|
1dee59bafa | |
|
|
81c8380e0c | |
|
|
7b47634213 | |
|
|
ef4f474f12 | |
|
|
b25a69a5be | |
|
|
e7a63237f2 | |
|
|
e4295332b9 | |
|
|
f155feb641 | |
|
|
99875d2f98 | |
|
|
bd4dddda0c | |
|
|
0886daa78f | |
|
|
e86a3b06db | |
|
|
794a792b39 | |
|
|
2444eacb13 |
|
|
@ -59,8 +59,12 @@ class UpdateExchangeRates implements ShouldQueue
|
|||
|
||||
/* Update all currencies */
|
||||
Currency::all()->each(function ($currency) use ($currency_api) {
|
||||
$currency->exchange_rate = $currency_api->rates->{$currency->code};
|
||||
$currency->save();
|
||||
|
||||
if(isset($currency_api->rates->{$currency->code})) {
|
||||
$currency->exchange_rate = $currency_api->rates->{$currency->code};
|
||||
$currency->save();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/* Rebuild the cache */
|
||||
|
|
@ -76,8 +80,12 @@ class UpdateExchangeRates implements ShouldQueue
|
|||
|
||||
/* Update all currencies */
|
||||
Currency::all()->each(function ($currency) use ($currency_api) {
|
||||
$currency->exchange_rate = $currency_api->rates->{$currency->code};
|
||||
$currency->save();
|
||||
|
||||
if (isset($currency_api->rates->{$currency->code})) {
|
||||
$currency->exchange_rate = $currency_api->rates->{$currency->code};
|
||||
$currency->save();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/* Rebuild the cache */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Invoice;
|
||||
use BaconQrCode\Writer;
|
||||
use App\Services\AbstractService;
|
||||
use BaconQrCode\Renderer\ImageRenderer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Cabecera;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SoapBody;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use App\Services\EDocument\Standards\Verifactu\VerifactuClient;
|
||||
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\RegistroAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SoapEnvelope;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\ObligadoEmision;
|
||||
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\InvoiceninjaToVerifactuMapper;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegFactuSistemaFacturacion;
|
||||
|
||||
class Verifactu
|
||||
{
|
||||
public function __construct(private Invoice $invoice)
|
||||
{
|
||||
}
|
||||
|
||||
public function send(mixed $registro)
|
||||
{
|
||||
|
||||
$client = new VerifactuClient(VerifactuClient::MODE_PROD);
|
||||
$response = $client->sendRegistroAlta($this->getRegistroAlta());
|
||||
|
||||
var_dump($response);
|
||||
|
||||
}
|
||||
|
||||
public function getRegistroAlta(): string
|
||||
{
|
||||
$mapper = new InvoiceninjaToVerifactuMapper();
|
||||
$regAlta = $mapper->mapRegistroAlta($this->invoice);
|
||||
|
||||
$soapEnvelope = new SoapEnvelope();
|
||||
$soapBody = new SoapBody();
|
||||
|
||||
$RegFactuSistemaFacturacion = new RegFactuSistemaFacturacion();
|
||||
|
||||
//The User or Corp that generated AND sends the invoice (ie Invoice Ninja!)
|
||||
$cabecera = new Cabecera();
|
||||
$obligadoEmision = new ObligadoEmision();
|
||||
$obligadoEmision->setNombreRazon($this->invoice->company->present()->name());
|
||||
$obligadoEmision->setNIF($this->invoice->company->settings->vat_number);
|
||||
$cabecera->setObligadoEmision($obligadoEmision);
|
||||
$RegFactuSistemaFacturacion->setCabecera($cabecera);
|
||||
|
||||
//The invoice itself
|
||||
$RegFactuSistemaFacturacion->setRegistroAlta($regAlta);
|
||||
|
||||
$soapBody->setRegFactuSistemaFacturacion($RegFactuSistemaFacturacion);
|
||||
$soapEnvelope->setBody($soapBody);
|
||||
|
||||
return $this->serializeXml($soapEnvelope);
|
||||
}
|
||||
|
||||
public function serializeXml(SoapEnvelope $registro): string
|
||||
{
|
||||
|
||||
$serializer = $this->getSerializer();
|
||||
|
||||
$context = [
|
||||
\Symfony\Component\Serializer\Normalizer\DateTimeNormalizer::FORMAT_KEY => 'd-m-Y',
|
||||
\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true
|
||||
];
|
||||
|
||||
$object = $serializer->normalize($registro, null, [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
|
||||
|
||||
$object = $this->removeEmptyValues($object);
|
||||
|
||||
$data = $serializer->encode($object, 'xml', $context);
|
||||
|
||||
$data = str_replace(['<response>','</response>'], '', $data);
|
||||
$data = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $data);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* getQrCode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQrCode(): string
|
||||
{
|
||||
//testmode
|
||||
$base_url = 'https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR';
|
||||
|
||||
//production
|
||||
// $base_url = https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR
|
||||
|
||||
// Format date to dd-mm-yyyy
|
||||
$fecha = \Carbon\Carbon::parse($this->invoice->date)->format('d-m-Y');
|
||||
|
||||
// Format amount to 2 decimal places without thousands separator
|
||||
$importe = number_format($this->invoice->amount, 2, '.', '');
|
||||
|
||||
// NIF is required for the QR code, if no NIF is present we use 00000000H
|
||||
if(strlen($this->invoice->client->vat_number) > 2 && $this->invoice->client->country->iso_3166_2 === 'ES') {
|
||||
$nif = $this->invoice->client->vat_number;
|
||||
} else {
|
||||
$nif = '00000000H'; //unknown / foreign client
|
||||
}
|
||||
|
||||
$params = [
|
||||
'nif' => $nif,
|
||||
'numserie' => $this->invoice->number,
|
||||
'fecha' => $fecha,
|
||||
'importe' => $importe,
|
||||
];
|
||||
|
||||
// Build URL with properly encoded parameters
|
||||
$query = http_build_query($params);
|
||||
|
||||
try {
|
||||
$renderer = new ImageRenderer(
|
||||
new RendererStyle(200),
|
||||
new SvgImageBackEnd()
|
||||
);
|
||||
$writer = new Writer($renderer);
|
||||
|
||||
$qr = $writer->writeString($base_url . '?' . $query, 'utf-8');
|
||||
|
||||
return htmlspecialchars("<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
|
||||
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>");
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
nlog(" QR failure => ".$e->getMessage());
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* getSerializer
|
||||
*
|
||||
* Returns the Symfony Serializer
|
||||
*
|
||||
* @return Serializer
|
||||
*/
|
||||
public function getSerializer(): Serializer
|
||||
{
|
||||
$phpDocExtractor = new PhpDocExtractor();
|
||||
$reflectionExtractor = new ReflectionExtractor();
|
||||
|
||||
$typeExtractors = [$reflectionExtractor, $phpDocExtractor];
|
||||
$descriptionExtractors = [$phpDocExtractor];
|
||||
$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()];
|
||||
|
||||
$namespaces = [
|
||||
'soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
||||
'sum' => 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd',
|
||||
'sum1' => 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd',
|
||||
'xd' => 'http://www.w3.org/2000/09/xmldsig#'
|
||||
];
|
||||
|
||||
$encoders = [
|
||||
new XmlEncoder([
|
||||
'xml_root_node_name' => 'soapenv:Envelope',
|
||||
'xml_format_output' => true,
|
||||
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
|
||||
'xml_encoding' => 'UTF-8',
|
||||
'xml_version' => '1.0',
|
||||
'namespace_prefix_map' => $namespaces,
|
||||
'default_root_ns' => 'soapenv',
|
||||
'xml_namespaces' => array_combine(
|
||||
array_map(fn($prefix) => "xmlns:$prefix", array_keys($namespaces)),
|
||||
array_values($namespaces)
|
||||
)
|
||||
]),
|
||||
new JsonEncoder()
|
||||
];
|
||||
|
||||
return new Serializer($normalizers, $encoders);
|
||||
}
|
||||
|
||||
/**
|
||||
* removeEmptyValues
|
||||
*
|
||||
* Removes empty values from the array
|
||||
*
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
private function removeEmptyValues(array $array): array
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$array[$key] = $this->removeEmptyValues($value);
|
||||
if (empty($array[$key])) {
|
||||
unset($array[$key]);
|
||||
}
|
||||
} elseif ($value === null || $value === '') {
|
||||
unset($array[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Invoice;
|
||||
use App\DataMapper\Tax\BaseRule;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDOtro;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Detalle;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridica;
|
||||
|
||||
class InvoiceninjaToVerifactuMapper
|
||||
{
|
||||
public array $rechazo_previo = [
|
||||
'S', // No previous rejection by AEAT
|
||||
'N', // Previous rejection
|
||||
'X', // No previous rejection but the registration does not exist
|
||||
];
|
||||
|
||||
public array $subsanacion = [
|
||||
'S', // Correction
|
||||
'N', // No correction
|
||||
];
|
||||
|
||||
/**
|
||||
* F series invoices are for the ORIGINAL / INITIAL version of the invoice.
|
||||
* R series invoices are for the CORRECTED version of the invoice.
|
||||
*
|
||||
* F1 is a standard invoice. Where the full customer details are provided.
|
||||
* F2 is a simplified invoice. Where the customer details are not provided.
|
||||
* F3 is a substitute invoice. Used to replace F2 invoices - we will not implement this!
|
||||
*
|
||||
* R1 Corrective invoice for errors in the original invoice.
|
||||
* R2 Used when customer enters bankruptcy during the invoice lifetime.
|
||||
* R3 Bad debt invoice for VAT refund.
|
||||
* R4 General purpose corrective invoice
|
||||
* R5 Corrective invoice for F2 type invoices.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public array $invoice_types = [
|
||||
'F1', // Invoice
|
||||
'F2', // Simplified Invoice
|
||||
'F3', // Substitute Invoice
|
||||
'R1', // Rectification Invoice
|
||||
'R2', // Rectification Invoice
|
||||
'R3', // Rectification Invoice
|
||||
'R4', // Rectification Invoice
|
||||
'R5', // Rectification Invoice
|
||||
];
|
||||
|
||||
/**
|
||||
* When generateing R type invoices, we will always use values
|
||||
* that substitute the original invoice, this requires settings
|
||||
*
|
||||
* $registroAlta->setTipoRectificativa('S'); // for Substitutive
|
||||
*/
|
||||
|
||||
public function mapRegistroAlta(Invoice $invoice): RegistroAlta // Registration Entry
|
||||
{
|
||||
$registroAlta = new RegistroAlta(); // Registration Entry
|
||||
|
||||
// Set version
|
||||
$registroAlta->setIDVersion('1.0');
|
||||
|
||||
// Set invoice ID (IDFactura)
|
||||
$idFactura = new IDFactura(); // Issued Invoice ID (Invoice)
|
||||
$idFactura->setIDEmisorFactura($invoice->company->settings->vat_number); // Invoice Issuer ID
|
||||
$idFactura->setNumSerieFactura($invoice->number); // Invoice Serial Number
|
||||
$idFactura->setFechaExpedicionFactura(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y')); // Invoice Issue Date
|
||||
$registroAlta->setIDFactura($idFactura);
|
||||
|
||||
// Set external reference (RefExterna) - The clients reference for this document - typically the PO Number, only apply if we have one.
|
||||
if(strlen($invoice->po_number) > 1) {
|
||||
$registroAlta->setRefExterna($invoice->po_number);
|
||||
}
|
||||
|
||||
// Set issuer name (NombreRazonEmisor)
|
||||
$registroAlta->setNombreRazonEmisor($invoice->company->present()->name());
|
||||
|
||||
// Set correction and previous rejection (Subsanacion y RechazoPrevio)
|
||||
//@todo we need to have logic surrounding these two fields if the are applicable to the current doc
|
||||
//@todo these _are_ optional fields
|
||||
// $registroAlta->setSubsanacion('Subsanacion::VALUE_N'); // Correction
|
||||
// $registroAlta->setRechazoPrevio('RechazoPrevio::VALUE_N'); // Previous Rejection
|
||||
|
||||
// Set invoice type (TipoFactura)
|
||||
$registroAlta->setTipoFactura($this->getInvoiceType($invoice));
|
||||
|
||||
// Delivery Date of the goods or services (we force invoice->date for this.)
|
||||
$registroAlta->setFechaOperacion(\Carbon\Carbon::parse($invoice->date)->format('d-m-Y'));
|
||||
|
||||
// Description of the operation (we use invoice->public_notes) BUT only if it's not empty
|
||||
if(strlen($invoice->public_notes ?? '') > 0) {
|
||||
$registroAlta->setDescripcionOperacion($invoice->public_notes);
|
||||
}
|
||||
|
||||
// Set recipients (Destinatarios)
|
||||
$destinatarios = new Destinatarios(); // Recipients
|
||||
$destinatario = new IDDestinatario(); // Natural/Legal Person
|
||||
$destinatario->setNombreRazon($invoice->client->present()->name()); // Business Name
|
||||
|
||||
// For Spanish clients with a VAT, we just need to set the NIF
|
||||
if (strlen($invoice->client->vat_number ?? '') > 2 && $invoice->client->country->iso_3166_2 === 'ES') {
|
||||
$destinatario->setNIF($invoice->client->vat_number); // Tax ID Number
|
||||
} else {
|
||||
// For all other clients, we need to set the IDOtro
|
||||
// this requires some logic to build
|
||||
$idOtro = $this->buildIdOtro($invoice);
|
||||
$destinatario->setIDOtro($idOtro);
|
||||
}
|
||||
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registroAlta->setDestinatarios($destinatarios);
|
||||
|
||||
// Set breakdown (Desglose) MAXIMUM 12 Line items!!!!!!!!
|
||||
$desglose = new Desglose(); // Breakdown
|
||||
|
||||
foreach($invoice->line_items as $item) {
|
||||
$detalle = new Detalle(); // Detail
|
||||
$detalle->setImpuesto('01'); // Tax (IVA) //@todo, need to implement logic for the other tax codes
|
||||
$detalle->setTipoImpositivo($item->tax_rate1);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto($item->line_total); // Taxable Base or Non-Taxable Amount
|
||||
$detalle->setCuotaRepercutida($item->tax_amount); // Charged Tax Amount
|
||||
$desglose->addToDetalleDesglose($detalle);
|
||||
}
|
||||
|
||||
$registroAlta->setDesglose($desglose);
|
||||
|
||||
// Set total amounts (CuotaTotal e ImporteTotal)
|
||||
$registroAlta->setCuotaTotal($invoice->total_taxes); //@todo this is not correct
|
||||
$registroAlta->setImporteTotal($invoice->amount); //@todo this is not correct
|
||||
|
||||
// Set fingerprint type and value (TipoHuella y Huella)
|
||||
$registroAlta->setTipoHuella('01');
|
||||
|
||||
// Set generation date (FechaHoraHusoGenRegistro)
|
||||
$registroAlta->setFechaHoraHusoGenRegistro(\Carbon\Carbon::now()->format('Y-m-d\TH:i:sP')); //@todo set the timezone to the company locale
|
||||
|
||||
$registroAlta->setHuella($this->getHash($invoice, $registroAlta)); // Digital Fingerprint
|
||||
|
||||
return $registroAlta;
|
||||
}
|
||||
|
||||
/**
|
||||
* getHash
|
||||
*
|
||||
* 1. High Billing Record
|
||||
* The fields to include in the string to calculate the footprint are:
|
||||
*
|
||||
* IDEmisorFactura : Identification of the invoice issuer.
|
||||
* NumSerieFactura : Serial number of the invoice.
|
||||
* InvoiceIssueDate : Date the invoice was issued.
|
||||
* TipoFactura : Invoice type code.
|
||||
* TotalQuota : Total amount of tax quotas.
|
||||
* TotalAmount : Total amount of the invoice.
|
||||
* Fingerprint (previous record) : Hash of the immediately preceding billing record (if any).
|
||||
* DateTimeZoneGenRecord : Date and time of record generation.
|
||||
*
|
||||
* 2. Cancellation Billing Record
|
||||
* In this case, the fields used to generate the hash are:
|
||||
*
|
||||
* IDEmisorFacturaAnulada : Identification of the issuer of the cancelled invoice.
|
||||
* NumSerieFacturaAnulada : Serial number of the cancelled invoice.
|
||||
* CancelledInvoiceIssueDate : Date of issue of the canceled invoice.
|
||||
* Fingerprint (previous record) : Hash of the cancelled invoice.
|
||||
* DateTimeZoneGenRecord : Date and time of record generation.
|
||||
*
|
||||
* 3. Event Registration
|
||||
* For event logs, the data string to be processed includes:
|
||||
* NIF of the issuer and the person obliged to issue .
|
||||
* Event ID .
|
||||
* Identification of the computer system .
|
||||
* Billing software version .
|
||||
* Installation number .
|
||||
* Event type .
|
||||
* Trace of the previous event (if applicable).
|
||||
* Date and time of event generation .
|
||||
*
|
||||
* Based on the type of record, the hash will need to be calculated differently.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param RegistroAlta $registroAlta
|
||||
* @return string
|
||||
*/
|
||||
private function getHash(Invoice $invoice, RegistroAlta $registroAlta): string
|
||||
{
|
||||
// $hash = '';
|
||||
// Tipo de factura Invoice type
|
||||
// Número de factura Invoice number
|
||||
// Fecha de emisión Date of issue
|
||||
// NIF del emisor Issuer's Tax Identification Number (NIF)
|
||||
// NIF del receptor Recipient's Tax Identification Number (NIF)
|
||||
// Importe total Total amount
|
||||
// Base imponible Taxable base
|
||||
// IVA aplicado Applied VAT
|
||||
// Tipo impositivo Tax rate
|
||||
// Fecha operación Transaction date
|
||||
// Descripción operación Description of the transaction
|
||||
// Serie Invoice series
|
||||
// Concepto Concept or description of the invoice
|
||||
|
||||
$hash = "IDEmisorFactura=" . $registroAlta->getIDFactura()->getIDEmisorFactura() .
|
||||
"&NumSerieFactura=" . $registroAlta->getIDFactura()->getNumSerieFactura() .
|
||||
"&FechaExpedicionFactura=" . $registroAlta->getIDFactura()->getFechaExpedicionFactura() .
|
||||
"&TipoFactura=" . $registroAlta->getTipoFactura() .
|
||||
"&CuotaTotal=" . $registroAlta->getCuotaTotal() .
|
||||
"&ImporteTotal=" . $registroAlta->getImporteTotal() .
|
||||
"&Huella=" . $registroAlta->getHuella() . // Fingerprint of the previous record
|
||||
"&FechaHoraHusoGenRegistro=" . $registroAlta->getFechaHoraHusoGenRegistro();
|
||||
|
||||
$hash = utf8_encode($hash);
|
||||
|
||||
$hash = strtoupper(hash('sha256', $hash));
|
||||
|
||||
return $hash;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate hash for cancellation records
|
||||
*
|
||||
* The fields used to generate the hash are:
|
||||
* - IDEmisorFacturaAnulada: Identification of the issuer of the cancelled invoice
|
||||
* - NumSerieFacturaAnulada: Serial number of the cancelled invoice
|
||||
* - FechaExpedicionFacturaAnulada: Date of issue of the canceled invoice
|
||||
* - Huella: Hash of the cancelled invoice
|
||||
* - FechaHoraHusoGenRegistro: Date and time of record generation
|
||||
*/
|
||||
// private function getHashForCancellation(RegistroFacturacionAnulacion $registroAnulacion): string
|
||||
// {
|
||||
// $hash = "IDEmisorFacturaAnulada=" . $registroAnulacion->getIDFactura()->getIDEmisorFactura() .
|
||||
// "&NumSerieFacturaAnulada=" . $registroAnulacion->getIDFactura()->getNumSerieFactura() .
|
||||
// "&FechaExpedicionFacturaAnulada=" . $registroAnulacion->getIDFactura()->getFechaExpedicionFactura() .
|
||||
// "&Huella=" . $registroAnulacion->getHuella() . // Hash of the cancelled invoice //@todo, when we init the doc, we need to set this!!
|
||||
// "&FechaHoraHusoGenRegistro=" . $registroAnulacion->getFechaHoraHusoGenRegistro();
|
||||
|
||||
// $hash = utf8_encode($hash);
|
||||
|
||||
// return strtoupper(hash('sha256', $hash));
|
||||
// }
|
||||
/**
|
||||
* getInvoiceType
|
||||
*
|
||||
* We do not yet have any UI for this. We'll need to implement UI
|
||||
* functionality that allows the user to initially select F1/F2
|
||||
*
|
||||
* and then on editting, they'll be able to select R1/R2/R3/R4/R5
|
||||
* be able to select R1/R2/R3/R4/R5
|
||||
* @return string
|
||||
*/
|
||||
private function getInvoiceType(Invoice $invoice): string
|
||||
{
|
||||
//@todo we need to have logic surrounding these two fields if the are applicable to the current doc
|
||||
return match($invoice->status_id) {
|
||||
Invoice::STATUS_DRAFT => 'F1',
|
||||
Invoice::STATUS_SENT => 'R4',
|
||||
Invoice::STATUS_PAID => 'R4',
|
||||
Invoice::STATUS_OVERDUE => 'R4',
|
||||
Invoice::STATUS_CANCELLED => 'R4',
|
||||
default => 'F1',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* buildIdOtro
|
||||
*
|
||||
* Client Identifier mapping
|
||||
* @param Invoice $invoice
|
||||
* @return IDOtro
|
||||
*/
|
||||
private function buildIdOtro(Invoice $invoice): IDOtro
|
||||
{
|
||||
$idOtro = new IDOtro(); // Other ID
|
||||
|
||||
$br = new BaseRule();
|
||||
$eu_countries = $br->eu_country_codes;
|
||||
|
||||
$client_country_code = $invoice->client->country->iso_3166_2;
|
||||
|
||||
if(in_array($client_country_code, $eu_countries)) {
|
||||
|
||||
// Is this B2C or B2B?
|
||||
if(strlen($invoice->client->vat_number ?? '') > 2) {
|
||||
$idOtro->setIDType('02'); // VAT Number
|
||||
$idOtro->setID($invoice->client->vat_number);
|
||||
} else {
|
||||
$idOtro->setIDType('04'); // Legal Entity ID
|
||||
$idOtro->setID($invoice->client->id_number);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//foreign country
|
||||
$idOtro->setIDType('03');
|
||||
$idOtro->setID(strlen($invoice->client->vat_number ?? '') > 2 ? $invoice->client->vat_number : $invoice->client->id_number);
|
||||
}
|
||||
|
||||
return $idOtro;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class Cabecera
|
||||
{
|
||||
/** @var ObligadoEmision */
|
||||
#[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(): ObligadoEmision
|
||||
{
|
||||
return $this->ObligadoEmision;
|
||||
}
|
||||
|
||||
public function setObligadoEmision(ObligadoEmision $obligadoEmision): self
|
||||
{
|
||||
$this->ObligadoEmision = $obligadoEmision;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRepresentante(): ?PersonaFisicaJuridicaES
|
||||
{
|
||||
return $this->Representante;
|
||||
}
|
||||
|
||||
public function setRepresentante(?PersonaFisicaJuridicaES $representante): self
|
||||
{
|
||||
$this->Representante = $representante;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{FechaFinVeriFactu?: string}|null
|
||||
*/
|
||||
public function getRemisionVoluntaria(): ?array
|
||||
{
|
||||
return $this->RemisionVoluntaria;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{FechaFinVeriFactu?: string}|null $remisionVoluntaria
|
||||
*/
|
||||
public function setRemisionVoluntaria(?array $remisionVoluntaria): self
|
||||
{
|
||||
if ($remisionVoluntaria !== null) {
|
||||
if (isset($remisionVoluntaria['FechaFinVeriFactu'])) {
|
||||
// Validate date format DD-MM-YYYY
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $remisionVoluntaria['FechaFinVeriFactu'])) {
|
||||
throw new \InvalidArgumentException('FechaFinVeriFactu must be in DD-MM-YYYY format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($day, $month, $year) = explode('-', $remisionVoluntaria['FechaFinVeriFactu']);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in FechaFinVeriFactu');
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->RemisionVoluntaria = $remisionVoluntaria;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{RefRequerimiento: string, FinRequerimiento?: string}|null
|
||||
*/
|
||||
public function getRemisionRequerimiento(): ?array
|
||||
{
|
||||
return $this->RemisionRequerimiento;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{RefRequerimiento: string, FinRequerimiento?: string}|null $remisionRequerimiento
|
||||
*/
|
||||
public function setRemisionRequerimiento(?array $remisionRequerimiento): self
|
||||
{
|
||||
if ($remisionRequerimiento !== null) {
|
||||
if (strlen($remisionRequerimiento['RefRequerimiento']) > 18) {
|
||||
throw new \InvalidArgumentException('RefRequerimiento must not exceed 18 characters');
|
||||
}
|
||||
if (isset($remisionRequerimiento['FinRequerimiento'])) {
|
||||
// Validate date format DD-MM-YYYY
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $remisionRequerimiento['FinRequerimiento'])) {
|
||||
throw new \InvalidArgumentException('FinRequerimiento must be in DD-MM-YYYY format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($day, $month, $year) = explode('-', $remisionRequerimiento['FinRequerimiento']);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in FinRequerimiento');
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->RemisionRequerimiento = $remisionRequerimiento;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types\Common;
|
||||
|
||||
trait TextTypes
|
||||
{
|
||||
protected function validateMaxLength(string $value, int $maxLength, string $fieldName): void
|
||||
{
|
||||
if (strlen($value) > $maxLength) {
|
||||
throw new \InvalidArgumentException("$fieldName must not exceed $maxLength characters");
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateExactLength(string $value, int $length, string $fieldName): void
|
||||
{
|
||||
if (strlen($value) !== $length) {
|
||||
throw new \InvalidArgumentException("$fieldName must be exactly $length characters long");
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateNIF(string $nif): void
|
||||
{
|
||||
$this->validateExactLength($nif, 9, 'NIF');
|
||||
// TODO: Add more specific NIF validation rules
|
||||
}
|
||||
|
||||
protected function validateDate(string $date): void
|
||||
{
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $date)) {
|
||||
throw new \InvalidArgumentException('Date must be in DD-MM-YYYY format');
|
||||
}
|
||||
|
||||
list($day, $month, $year) = explode('-', $date);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateTimestamp(string $timestamp): void
|
||||
{
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}$/', $timestamp)) {
|
||||
throw new \InvalidArgumentException('Timestamp must be in DD-MM-YYYY HH:mm:ss format');
|
||||
}
|
||||
|
||||
list($date, $time) = explode(' ', $timestamp);
|
||||
list($day, $month, $year) = explode('-', $date);
|
||||
list($hour, $minute, $second) = explode(':', $time);
|
||||
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in timestamp');
|
||||
}
|
||||
|
||||
if ($hour > 23 || $minute > 59 || $second > 59) {
|
||||
throw new \InvalidArgumentException('Invalid time in timestamp');
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateNumericString(string $value, int $maxIntegerDigits, int $maxDecimalDigits, string $fieldName): void
|
||||
{
|
||||
if (!preg_match('/^[+-]?\d{1,' . $maxIntegerDigits . '}(\.\d{0,' . $maxDecimalDigits . '})?$/', $value)) {
|
||||
throw new \InvalidArgumentException("$fieldName must have at most $maxIntegerDigits digits before decimal point and $maxDecimalDigits after");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class Desglose
|
||||
{
|
||||
/** @var DetalleDesglose[] */
|
||||
#[SerializedName('sum1:DetalleDesglose')]
|
||||
protected $DetalleDesglose = [];
|
||||
|
||||
/**
|
||||
* @return DetalleDesglose[]
|
||||
*/
|
||||
public function getDetalleDesglose(): array
|
||||
{
|
||||
return $this->DetalleDesglose;
|
||||
}
|
||||
|
||||
public function addDetalleDesglose(DetalleDesglose $detalleDesglose): self
|
||||
{
|
||||
$this->DetalleDesglose[] = $detalleDesglose;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<DetalleDesglose> $detalles
|
||||
*/
|
||||
public function setDetalleDesglose(array $detalles): self
|
||||
{
|
||||
$this->DetalleDesglose = $detalles;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
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:CuotaRecargoRectificado')]
|
||||
protected $CuotaRecargoRectificado;
|
||||
|
||||
public function getBaseRectificada(): float
|
||||
{
|
||||
return $this->BaseRectificada;
|
||||
}
|
||||
|
||||
public function setBaseRectificada(float $baseRectificada): self
|
||||
{
|
||||
// Validate format: max 12 digits before decimal point, 2 after
|
||||
$parts = explode('.', (string)$baseRectificada);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseRectificada must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseRectificada must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->BaseRectificada = $baseRectificada;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRectificada(): float
|
||||
{
|
||||
return $this->CuotaRectificada;
|
||||
}
|
||||
|
||||
public function setCuotaRectificada(float $cuotaRectificada): self
|
||||
{
|
||||
// Validate format: max 12 digits before decimal point, 2 after
|
||||
$parts = explode('.', (string)$cuotaRectificada);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaRectificada must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaRectificada must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->CuotaRectificada = $cuotaRectificada;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRecargoRectificado(): ?float
|
||||
{
|
||||
return $this->CuotaRecargoRectificado;
|
||||
}
|
||||
|
||||
public function setCuotaRecargoRectificado(?float $cuotaRecargoRectificado): self
|
||||
{
|
||||
if ($cuotaRecargoRectificado !== null) {
|
||||
// Validate format: max 12 digits before decimal point, 2 after
|
||||
$parts = explode('.', (string)$cuotaRecargoRectificado);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaRecargoRectificado must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaRecargoRectificado must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
|
||||
$this->CuotaRecargoRectificado = $cuotaRecargoRectificado;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class Destinatarios
|
||||
{
|
||||
/** @var IDDestinatario[] */
|
||||
#[SerializedName('sum1:IDDestinatario')]
|
||||
protected $IDDestinatario = [];
|
||||
|
||||
/**
|
||||
* @return IDDestinatario[]
|
||||
*/
|
||||
public function getIDDestinatario(): array
|
||||
{
|
||||
return $this->IDDestinatario;
|
||||
}
|
||||
|
||||
public function addIDDestinatario(IDDestinatario $idDestinatario): self
|
||||
{
|
||||
if (count($this->IDDestinatario) >= 1000) {
|
||||
throw new \InvalidArgumentException('Maximum number of IDDestinatario (1000) exceeded');
|
||||
}
|
||||
$this->IDDestinatario[] = $idDestinatario;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<IDDestinatario> $destinatarios
|
||||
*/
|
||||
public function setIDDestinatario(array $destinatarios): self
|
||||
{
|
||||
$this->IDDestinatario = $destinatarios;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class Detalle
|
||||
{
|
||||
public array $impuestos = [
|
||||
'01', //IVA
|
||||
'02', //IPSI (Ceuta y Melilla)
|
||||
'03', //IGIC (Canarias)
|
||||
'05', //Otros
|
||||
];
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:Impuesto')]
|
||||
protected $Impuesto;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:ClaveRegimen')]
|
||||
protected $ClaveRegimen;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:CalificacionOperacion')]
|
||||
protected $CalificacionOperacion;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:OperacionExenta')]
|
||||
protected $OperacionExenta;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:TipoImpositivo')]
|
||||
protected $TipoImpositivo;
|
||||
|
||||
/** @var float */
|
||||
#[SerializedName('sum1:BaseImponibleOimporteNoSujeto')]
|
||||
protected $BaseImponibleOimporteNoSujeto;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:BaseImponibleACoste')]
|
||||
protected $BaseImponibleACoste;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:CuotaRepercutida')]
|
||||
protected $CuotaRepercutida;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:TipoRecargoEquivalencia')]
|
||||
protected $TipoRecargoEquivalencia;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:CuotaRecargoEquivalencia')]
|
||||
protected $CuotaRecargoEquivalencia;
|
||||
|
||||
public function getImpuesto(): ?string
|
||||
{
|
||||
return $this->Impuesto;
|
||||
}
|
||||
|
||||
public function setImpuesto(?string $impuesto): self
|
||||
{
|
||||
$this->Impuesto = $impuesto;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClaveRegimen(): ?string
|
||||
{
|
||||
return $this->ClaveRegimen;
|
||||
}
|
||||
|
||||
public function setClaveRegimen(?string $claveRegimen): self
|
||||
{
|
||||
if ($claveRegimen !== null && !preg_match('/^\d{2}$/', $claveRegimen)) {
|
||||
throw new \InvalidArgumentException('ClaveRegimen must be a 2-digit number');
|
||||
}
|
||||
$this->ClaveRegimen = $claveRegimen;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalificacionOperacion(): ?string
|
||||
{
|
||||
return $this->CalificacionOperacion;
|
||||
}
|
||||
|
||||
public function setCalificacionOperacion(?string $calificacionOperacion): self
|
||||
{
|
||||
if ($calificacionOperacion !== null) {
|
||||
if (!preg_match('/^[A-Z]\d$/', $calificacionOperacion)) {
|
||||
throw new \InvalidArgumentException('CalificacionOperacion must be a letter followed by a digit');
|
||||
}
|
||||
if ($this->OperacionExenta !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set CalificacionOperacion when OperacionExenta is set');
|
||||
}
|
||||
}
|
||||
$this->CalificacionOperacion = $calificacionOperacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOperacionExenta(): ?string
|
||||
{
|
||||
return $this->OperacionExenta;
|
||||
}
|
||||
|
||||
public function setOperacionExenta(?string $operacionExenta): self
|
||||
{
|
||||
if ($operacionExenta !== null) {
|
||||
if (!preg_match('/^[A-Z]\d$/', $operacionExenta)) {
|
||||
throw new \InvalidArgumentException('OperacionExenta must be a letter followed by a digit');
|
||||
}
|
||||
if ($this->CalificacionOperacion !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set OperacionExenta when CalificacionOperacion is set');
|
||||
}
|
||||
}
|
||||
$this->OperacionExenta = $operacionExenta;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoImpositivo(): ?float
|
||||
{
|
||||
return $this->TipoImpositivo;
|
||||
}
|
||||
|
||||
public function setTipoImpositivo(?float $tipoImpositivo): self
|
||||
{
|
||||
if ($tipoImpositivo !== null) {
|
||||
$parts = explode('.', (string)$tipoImpositivo);
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('TipoImpositivo must have at most 2 decimal places');
|
||||
}
|
||||
if ($tipoImpositivo < 0 || $tipoImpositivo > 100) {
|
||||
throw new \InvalidArgumentException('TipoImpositivo must be between 0 and 100');
|
||||
}
|
||||
}
|
||||
$this->TipoImpositivo = $tipoImpositivo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseImponibleOimporteNoSujeto(): float
|
||||
{
|
||||
return $this->BaseImponibleOimporteNoSujeto;
|
||||
}
|
||||
|
||||
public function setBaseImponibleOimporteNoSujeto(float $baseImponibleOimporteNoSujeto): self
|
||||
{
|
||||
$parts = explode('.', (string)$baseImponibleOimporteNoSujeto);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->BaseImponibleOimporteNoSujeto = $baseImponibleOimporteNoSujeto;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseImponibleACoste(): ?float
|
||||
{
|
||||
return $this->BaseImponibleACoste;
|
||||
}
|
||||
|
||||
public function setBaseImponibleACoste(?float $baseImponibleACoste): self
|
||||
{
|
||||
if ($baseImponibleACoste !== null) {
|
||||
$parts = explode('.', (string)$baseImponibleACoste);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseImponibleACoste must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseImponibleACoste must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
$this->BaseImponibleACoste = $baseImponibleACoste;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRepercutida(): ?float
|
||||
{
|
||||
return $this->CuotaRepercutida;
|
||||
}
|
||||
|
||||
public function setCuotaRepercutida(?float $cuotaRepercutida): self
|
||||
{
|
||||
if ($cuotaRepercutida !== null) {
|
||||
$parts = explode('.', (string)$cuotaRepercutida);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaRepercutida must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaRepercutida must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
$this->CuotaRepercutida = $cuotaRepercutida;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoRecargoEquivalencia(): ?float
|
||||
{
|
||||
return $this->TipoRecargoEquivalencia;
|
||||
}
|
||||
|
||||
public function setTipoRecargoEquivalencia(?float $tipoRecargoEquivalencia): self
|
||||
{
|
||||
if ($tipoRecargoEquivalencia !== null) {
|
||||
$parts = explode('.', (string)$tipoRecargoEquivalencia);
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('TipoRecargoEquivalencia must have at most 2 decimal places');
|
||||
}
|
||||
if ($tipoRecargoEquivalencia < 0 || $tipoRecargoEquivalencia > 100) {
|
||||
throw new \InvalidArgumentException('TipoRecargoEquivalencia must be between 0 and 100');
|
||||
}
|
||||
}
|
||||
$this->TipoRecargoEquivalencia = $tipoRecargoEquivalencia;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRecargoEquivalencia(): ?float
|
||||
{
|
||||
return $this->CuotaRecargoEquivalencia;
|
||||
}
|
||||
|
||||
public function setCuotaRecargoEquivalencia(?float $cuotaRecargoEquivalencia): self
|
||||
{
|
||||
if ($cuotaRecargoEquivalencia !== null) {
|
||||
$parts = explode('.', (string)$cuotaRecargoEquivalencia);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaRecargoEquivalencia must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaRecargoEquivalencia must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
$this->CuotaRecargoEquivalencia = $cuotaRecargoEquivalencia;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class DetalleDesglose
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:ClaveRegimen')]
|
||||
protected $ClaveRegimen;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:CalificacionOperacion')]
|
||||
protected $CalificacionOperacion;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:OperacionExenta')]
|
||||
protected $OperacionExenta;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:TipoImpositivo')]
|
||||
protected $TipoImpositivo;
|
||||
|
||||
/** @var float */
|
||||
#[SerializedName('sum1:BaseImponibleOimporteNoSujeto')]
|
||||
protected $BaseImponibleOimporteNoSujeto;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:BaseImponibleACoste')]
|
||||
protected $BaseImponibleACoste;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:CuotaRepercutida')]
|
||||
protected $CuotaRepercutida;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:TipoRecargoEquivalencia')]
|
||||
protected $TipoRecargoEquivalencia;
|
||||
|
||||
/** @var float|null */
|
||||
#[SerializedName('sum1:CuotaRecargoEquivalencia')]
|
||||
protected $CuotaRecargoEquivalencia;
|
||||
|
||||
public function getClaveRegimen(): string
|
||||
{
|
||||
return $this->ClaveRegimen;
|
||||
}
|
||||
|
||||
public function setClaveRegimen(string $claveRegimen): self
|
||||
{
|
||||
if (!preg_match('/^\d{2}$/', $claveRegimen)) {
|
||||
throw new \InvalidArgumentException('ClaveRegimen must be a 2-digit number');
|
||||
}
|
||||
$this->ClaveRegimen = $claveRegimen;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalificacionOperacion(): string
|
||||
{
|
||||
return $this->CalificacionOperacion;
|
||||
}
|
||||
|
||||
public function setCalificacionOperacion(string $calificacionOperacion): self
|
||||
{
|
||||
if (!preg_match('/^[A-Z]\d$/', $calificacionOperacion)) {
|
||||
throw new \InvalidArgumentException('CalificacionOperacion must be a letter followed by a digit');
|
||||
}
|
||||
if ($this->OperacionExenta !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set CalificacionOperacion when OperacionExenta is set');
|
||||
}
|
||||
$this->CalificacionOperacion = $calificacionOperacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOperacionExenta(): ?string
|
||||
{
|
||||
return $this->OperacionExenta;
|
||||
}
|
||||
|
||||
public function setOperacionExenta(?string $operacionExenta): self
|
||||
{
|
||||
if ($operacionExenta !== null) {
|
||||
if (!preg_match('/^[A-Z]\d$/', $operacionExenta)) {
|
||||
throw new \InvalidArgumentException('OperacionExenta must be a letter followed by a digit');
|
||||
}
|
||||
if ($this->CalificacionOperacion !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set OperacionExenta when CalificacionOperacion is set');
|
||||
}
|
||||
}
|
||||
$this->OperacionExenta = $operacionExenta;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoImpositivo(): ?float
|
||||
{
|
||||
return $this->TipoImpositivo;
|
||||
}
|
||||
|
||||
public function setTipoImpositivo(?float $tipoImpositivo): self
|
||||
{
|
||||
if ($tipoImpositivo !== null && ($tipoImpositivo < 0 || $tipoImpositivo > 100)) {
|
||||
throw new \InvalidArgumentException('TipoImpositivo must be between 0 and 100');
|
||||
}
|
||||
$this->TipoImpositivo = $tipoImpositivo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseImponibleOimporteNoSujeto(): float
|
||||
{
|
||||
return $this->BaseImponibleOimporteNoSujeto;
|
||||
}
|
||||
|
||||
public function setBaseImponibleOimporteNoSujeto(float $baseImponibleOimporteNoSujeto): self
|
||||
{
|
||||
$parts = explode('.', (string)$baseImponibleOimporteNoSujeto);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->BaseImponibleOimporteNoSujeto = $baseImponibleOimporteNoSujeto;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseImponibleACoste(): ?float
|
||||
{
|
||||
return $this->BaseImponibleACoste;
|
||||
}
|
||||
|
||||
public function setBaseImponibleACoste(?float $baseImponibleACoste): self
|
||||
{
|
||||
if ($baseImponibleACoste !== null) {
|
||||
$parts = explode('.', (string)$baseImponibleACoste);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseImponibleACoste must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseImponibleACoste must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
$this->BaseImponibleACoste = $baseImponibleACoste;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRepercutida(): ?float
|
||||
{
|
||||
return $this->CuotaRepercutida;
|
||||
}
|
||||
|
||||
public function setCuotaRepercutida(?float $cuotaRepercutida): self
|
||||
{
|
||||
if ($cuotaRepercutida !== null) {
|
||||
$parts = explode('.', (string)$cuotaRepercutida);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaRepercutida must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaRepercutida must have at most 2 decimal places');
|
||||
}
|
||||
}
|
||||
$this->CuotaRepercutida = $cuotaRepercutida;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoRecargoEquivalencia(): ?float
|
||||
{
|
||||
return $this->TipoRecargoEquivalencia;
|
||||
}
|
||||
|
||||
public function setTipoRecargoEquivalencia(?float $tipoRecargoEquivalencia): self
|
||||
{
|
||||
if ($tipoRecargoEquivalencia !== null && ($tipoRecargoEquivalencia < 0 || $tipoRecargoEquivalencia > 100)) {
|
||||
throw new \InvalidArgumentException('TipoRecargoEquivalencia must be between 0 and 100');
|
||||
}
|
||||
$this->TipoRecargoEquivalencia = $tipoRecargoEquivalencia;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaRecargoEquivalencia(): ?float
|
||||
{
|
||||
return $this->CuotaRecargoEquivalencia;
|
||||
}
|
||||
|
||||
public function setCuotaRecargoEquivalencia(?float $cuotaRecargoEquivalencia): self
|
||||
{
|
||||
$this->CuotaRecargoEquivalencia = $cuotaRecargoEquivalencia;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class Encadenamiento
|
||||
{
|
||||
/** @var RegistroAnterior */
|
||||
#[SerializedName('sum1:RegistroAnterior')]
|
||||
protected $RegistroAnterior;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:PrimerRegistro')]
|
||||
protected $PrimerRegistro;
|
||||
|
||||
public function getRegistroAnterior(): RegistroAnterior
|
||||
{
|
||||
return $this->RegistroAnterior;
|
||||
}
|
||||
|
||||
public function setRegistroAnterior(RegistroAnterior $registroAnterior): self
|
||||
{
|
||||
$this->RegistroAnterior = $registroAnterior;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimerRegistro(): string
|
||||
{
|
||||
return $this->PrimerRegistro;
|
||||
}
|
||||
|
||||
public function setPrimerRegistro(string $primerRegistro): self
|
||||
{
|
||||
if (strlen($primerRegistro) > 64) {
|
||||
throw new \InvalidArgumentException('HuellaRegistroAnterior must not exceed 64 characters');
|
||||
}
|
||||
$this->PrimerRegistro = $primerRegistro;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class IDDestinatario extends PersonaFisicaJuridica
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class IDFactura
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDEmisorFactura')]
|
||||
protected $IDEmisorFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NumSerieFactura')]
|
||||
protected $NumSerieFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaExpedicionFactura')]
|
||||
protected $FechaExpedicionFactura;
|
||||
|
||||
public function getIDEmisorFactura(): string
|
||||
{
|
||||
return $this->IDEmisorFactura;
|
||||
}
|
||||
|
||||
public function setIDEmisorFactura(string $idEmisorFactura): self
|
||||
{
|
||||
// Validate NIF format (letter or number followed by 8 numbers)
|
||||
if (!preg_match('/^[A-Z0-9][0-9]{8}$/', $idEmisorFactura)) {
|
||||
throw new \InvalidArgumentException('IDEmisorFactura must be a valid NIF (letter/number followed by 8 numbers)');
|
||||
}
|
||||
$this->IDEmisorFactura = $idEmisorFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumSerieFactura(): string
|
||||
{
|
||||
return $this->NumSerieFactura;
|
||||
}
|
||||
|
||||
public function setNumSerieFactura(string $numSerieFactura): self
|
||||
{
|
||||
if (strlen($numSerieFactura) > 60) {
|
||||
throw new \InvalidArgumentException('NumSerieFactura must not exceed 60 characters');
|
||||
}
|
||||
$this->NumSerieFactura = $numSerieFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaExpedicionFactura(): string
|
||||
{
|
||||
return $this->FechaExpedicionFactura;
|
||||
}
|
||||
|
||||
public function setFechaExpedicionFactura(string $fechaExpedicionFactura): self
|
||||
{
|
||||
// Validate date format DD-MM-YYYY
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $fechaExpedicionFactura)) {
|
||||
throw new \InvalidArgumentException('FechaExpedicionFactura must be in DD-MM-YYYY format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($day, $month, $year) = explode('-', $fechaExpedicionFactura);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
|
||||
$this->FechaExpedicionFactura = $fechaExpedicionFactura;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class IDFacturaAR
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDEmisorFactura')]
|
||||
protected $IDEmisorFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NumSerieFactura')]
|
||||
protected $NumSerieFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaExpedicionFactura')]
|
||||
protected $FechaExpedicionFactura;
|
||||
|
||||
/** @var string|null */
|
||||
protected $NumSerieFacturaOrigen;
|
||||
|
||||
/** @var string|null */
|
||||
protected $FechaExpedicionFacturaOrigen;
|
||||
|
||||
public function getIDEmisorFactura(): string
|
||||
{
|
||||
return $this->IDEmisorFactura;
|
||||
}
|
||||
|
||||
public function setIDEmisorFactura(string $idEmisorFactura): self
|
||||
{
|
||||
// Validate NIF format
|
||||
if (!preg_match('/^[A-Z0-9]{9}$/', $idEmisorFactura)) {
|
||||
throw new \InvalidArgumentException('IDEmisorFactura must be a valid NIF (9 alphanumeric characters)');
|
||||
}
|
||||
$this->IDEmisorFactura = $idEmisorFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumSerieFactura(): string
|
||||
{
|
||||
return $this->NumSerieFactura;
|
||||
}
|
||||
|
||||
public function setNumSerieFactura(string $numSerieFactura): self
|
||||
{
|
||||
if (strlen($numSerieFactura) > 60) {
|
||||
throw new \InvalidArgumentException('NumSerieFactura must not exceed 60 characters');
|
||||
}
|
||||
$this->NumSerieFactura = $numSerieFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaExpedicionFactura(): string
|
||||
{
|
||||
return $this->FechaExpedicionFactura;
|
||||
}
|
||||
|
||||
public function setFechaExpedicionFactura(string $fechaExpedicionFactura): self
|
||||
{
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $fechaExpedicionFactura)) {
|
||||
throw new \InvalidArgumentException('FechaExpedicionFactura must be in DD-MM-YYYY format');
|
||||
}
|
||||
list($day, $month, $year) = explode('-', $fechaExpedicionFactura);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in FechaExpedicionFactura');
|
||||
}
|
||||
$this->FechaExpedicionFactura = $fechaExpedicionFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumSerieFacturaOrigen(): ?string
|
||||
{
|
||||
return $this->NumSerieFacturaOrigen;
|
||||
}
|
||||
|
||||
public function setNumSerieFacturaOrigen(?string $numSerieFacturaOrigen): self
|
||||
{
|
||||
if ($numSerieFacturaOrigen !== null && strlen($numSerieFacturaOrigen) > 60) {
|
||||
throw new \InvalidArgumentException('NumSerieFacturaOrigen must not exceed 60 characters');
|
||||
}
|
||||
$this->NumSerieFacturaOrigen = $numSerieFacturaOrigen;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaExpedicionFacturaOrigen(): ?string
|
||||
{
|
||||
return $this->FechaExpedicionFacturaOrigen;
|
||||
}
|
||||
|
||||
public function setFechaExpedicionFacturaOrigen(?string $fechaExpedicionFacturaOrigen): self
|
||||
{
|
||||
if ($fechaExpedicionFacturaOrigen !== null) {
|
||||
// Validate date format DD-MM-YYYY
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $fechaExpedicionFacturaOrigen)) {
|
||||
throw new \InvalidArgumentException('FechaExpedicionFacturaOrigen must be in DD-MM-YYYY format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($day, $month, $year) = explode('-', $fechaExpedicionFacturaOrigen);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
}
|
||||
|
||||
$this->FechaExpedicionFacturaOrigen = $fechaExpedicionFacturaOrigen;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class IDFacturaExpedida
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDEmisorFactura')]
|
||||
protected $IDEmisorFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NumSerieFactura')]
|
||||
protected $NumSerieFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaExpedicionFactura')]
|
||||
protected $FechaExpedicionFactura;
|
||||
|
||||
public function getIDEmisorFactura(): string
|
||||
{
|
||||
return $this->IDEmisorFactura;
|
||||
}
|
||||
|
||||
public function setIDEmisorFactura(string $idEmisorFactura): self
|
||||
{
|
||||
// Validate NIF format
|
||||
if (!preg_match('/^[A-Z0-9]{9}$/', $idEmisorFactura)) {
|
||||
throw new \InvalidArgumentException('IDEmisorFactura must be a valid NIF (9 alphanumeric characters)');
|
||||
}
|
||||
$this->IDEmisorFactura = $idEmisorFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumSerieFactura(): string
|
||||
{
|
||||
return $this->NumSerieFactura;
|
||||
}
|
||||
|
||||
public function setNumSerieFactura(string $numSerieFactura): self
|
||||
{
|
||||
if (strlen($numSerieFactura) > 60) {
|
||||
throw new \InvalidArgumentException('NumSerieFactura must not exceed 60 characters');
|
||||
}
|
||||
$this->NumSerieFactura = $numSerieFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaExpedicionFactura(): string
|
||||
{
|
||||
return $this->FechaExpedicionFactura;
|
||||
}
|
||||
|
||||
public function setFechaExpedicionFactura(string $fechaExpedicionFactura): self
|
||||
{
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $fechaExpedicionFactura)) {
|
||||
throw new \InvalidArgumentException('FechaExpedicionFactura must be in DD-MM-YYYY format');
|
||||
}
|
||||
list($day, $month, $year) = explode('-', $fechaExpedicionFactura);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in FechaExpedicionFactura');
|
||||
}
|
||||
$this->FechaExpedicionFactura = $fechaExpedicionFactura;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class IDOtro
|
||||
{
|
||||
|
||||
// 01 NIFContraparte Spanish Tax ID (NIF) of the counterparty NIF de la contraparte (solo válido con NIF, no en IDOtro)
|
||||
// 02 VATNumber EU VAT Number Número de IVA de operadores intracomunitarios
|
||||
// 03 Passport/Foreign ID National ID, passport, or similar from non-EU countries Documento oficial de identificación expedido por otro país
|
||||
// 04 Legal Entity ID Tax ID for foreign legal entities Código de identificación fiscal de personas jurídicas extranjeras
|
||||
// 05 Residence Cert. Certificate of residence issued by a tax authority Certificado de residencia fiscal
|
||||
// 06 Other Other officially recognized identifier Otro documento reconocido oficialmente
|
||||
public array $id_types = [
|
||||
'01',
|
||||
'02',
|
||||
'03',
|
||||
'04',
|
||||
'05',
|
||||
'06',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:CodigoPais')] // iso 2 country code
|
||||
protected $CodigoPais;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDType')]
|
||||
protected $IDType;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:ID')]
|
||||
protected $ID;
|
||||
|
||||
public function getCodigoPais(): string
|
||||
{
|
||||
return $this->CodigoPais;
|
||||
}
|
||||
|
||||
public function setCodigoPais(string $codigoPais): self
|
||||
{
|
||||
if (strlen($codigoPais) !== 2) {
|
||||
throw new \InvalidArgumentException('CodigoPais must be a 2-character ISO country code');
|
||||
}
|
||||
$this->CodigoPais = $codigoPais;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDType(): string
|
||||
{
|
||||
return $this->IDType;
|
||||
}
|
||||
|
||||
public function setIDType(string $idType): self
|
||||
{
|
||||
$validTypes = ['02', '03', '04', '05', '06', '07'];
|
||||
if (!in_array($idType, $validTypes)) {
|
||||
throw new \InvalidArgumentException('Invalid IDType value');
|
||||
}
|
||||
$this->IDType = $idType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getID(): string
|
||||
{
|
||||
return $this->ID;
|
||||
}
|
||||
|
||||
public function setID(string $id): self
|
||||
{
|
||||
if (strlen($id) > 20) {
|
||||
throw new \InvalidArgumentException('ID must not exceed 20 characters');
|
||||
}
|
||||
$this->ID = $id;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
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)
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->Value;
|
||||
}
|
||||
|
||||
public function setValue(string $value): self
|
||||
{
|
||||
$this->validateNumericString($value, 14, 2, 'Amount');
|
||||
$this->Value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->Value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:CodigoIncidencia')]
|
||||
protected $CodigoIncidencia;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:DescripcionIncidencia')]
|
||||
protected $DescripcionIncidencia;
|
||||
|
||||
public function getCodigo(): string
|
||||
{
|
||||
return $this->Codigo;
|
||||
}
|
||||
|
||||
public function setCodigo(string $codigo): self
|
||||
{
|
||||
if (!preg_match('/^\d{3}$/', $codigo)) {
|
||||
throw new \InvalidArgumentException('Codigo must be a 3-digit number');
|
||||
}
|
||||
$this->Codigo = $codigo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescripcion(): string
|
||||
{
|
||||
return $this->Descripcion;
|
||||
}
|
||||
|
||||
public function setDescripcion(string $descripcion): self
|
||||
{
|
||||
if (strlen($descripcion) > 500) {
|
||||
throw new \InvalidArgumentException('Descripcion must not exceed 500 characters');
|
||||
}
|
||||
$this->Descripcion = $descripcion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazon(): ?string
|
||||
{
|
||||
return $this->NombreRazon;
|
||||
}
|
||||
|
||||
public function setNombreRazon(?string $nombreRazon): self
|
||||
{
|
||||
if ($nombreRazon !== null && strlen($nombreRazon) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazon must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazon = $nombreRazon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNIF(): ?string
|
||||
{
|
||||
return $this->NIF;
|
||||
}
|
||||
|
||||
public function setNIF(?string $nif): self
|
||||
{
|
||||
// TODO: Add NIF validation
|
||||
$this->NIF = $nif;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHora(): ?string
|
||||
{
|
||||
return $this->FechaHora;
|
||||
}
|
||||
|
||||
public function setFechaHora(?string $fechaHora): self
|
||||
{
|
||||
if ($fechaHora !== null) {
|
||||
if (!\DateTime::createFromFormat('Y-m-d H:i:s', $fechaHora)) {
|
||||
throw new \InvalidArgumentException('FechaHora must be in YYYY-MM-DD HH:mm:ss format');
|
||||
}
|
||||
}
|
||||
$this->FechaHora = $fechaHora;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCodigoIncidencia(): string
|
||||
{
|
||||
return $this->CodigoIncidencia;
|
||||
}
|
||||
|
||||
public function setCodigoIncidencia(string $codigoIncidencia): self
|
||||
{
|
||||
if (!preg_match('/^\d{3}$/', $codigoIncidencia)) {
|
||||
throw new \InvalidArgumentException('CodigoIncidencia must be a 3-digit number');
|
||||
}
|
||||
$this->CodigoIncidencia = $codigoIncidencia;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescripcionIncidencia(): string
|
||||
{
|
||||
return $this->DescripcionIncidencia;
|
||||
}
|
||||
|
||||
public function setDescripcionIncidencia(string $descripcionIncidencia): self
|
||||
{
|
||||
if (strlen($descripcionIncidencia) > 500) {
|
||||
throw new \InvalidArgumentException('DescripcionIncidencia must not exceed 500 characters');
|
||||
}
|
||||
$this->DescripcionIncidencia = $descripcionIncidencia;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
/**
|
||||
* ObligadoEmision represents a required entity with NombreRazon and NIF.
|
||||
* Extends PersonaFisicaJuridicaES but enforces both properties to be required at construction time.
|
||||
*/
|
||||
class ObligadoEmision
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NombreRazon')]
|
||||
protected $NombreRazon;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:NIF')]
|
||||
protected $NIF;
|
||||
|
||||
public function setNombreRazon(?string $nombreRazon): self
|
||||
{
|
||||
if (empty($nombreRazon)) {
|
||||
throw new \InvalidArgumentException('NombreRazon is required for ObligadoEmision');
|
||||
}
|
||||
|
||||
$this->NombreRazon = $nombreRazon;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazon(): string
|
||||
{
|
||||
return $this->NombreRazon;
|
||||
}
|
||||
|
||||
public function getNIF(): string
|
||||
{
|
||||
return $this->NIF;
|
||||
}
|
||||
|
||||
public function setNIF(string $nif): self
|
||||
{
|
||||
if (empty($nif)) {
|
||||
throw new \InvalidArgumentException('NIF is required for ObligadoEmision');
|
||||
}
|
||||
|
||||
$this->NIF = $nif;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class OperacionExenta
|
||||
{
|
||||
public const E1 = 'E1'; // EXENTA por Art. 20
|
||||
public const E2 = 'E2'; // EXENTA por Art. 21
|
||||
public const E3 = 'E3'; // EXENTA por Art. 22
|
||||
public const E4 = 'E4'; // EXENTA por Art. 24
|
||||
public const E5 = 'E5'; // EXENTA por Art. 25
|
||||
public const E6 = 'E6'; // EXENTA por otros
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:Value')]
|
||||
protected $Value;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:CausaExencion')]
|
||||
protected $CausaExencion;
|
||||
|
||||
/** @var float */
|
||||
#[SerializedName('sum1:BaseImponible')]
|
||||
protected $BaseImponible;
|
||||
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->Value;
|
||||
}
|
||||
|
||||
public function setValue(string $value): self
|
||||
{
|
||||
$validValues = [
|
||||
self::E1,
|
||||
self::E2,
|
||||
self::E3,
|
||||
self::E4,
|
||||
self::E5,
|
||||
self::E6,
|
||||
];
|
||||
|
||||
if (!in_array($value, $validValues)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Invalid OperacionExenta value. Must be one of: %s',
|
||||
implode(', ', $validValues)
|
||||
));
|
||||
}
|
||||
|
||||
$this->Value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->Value;
|
||||
}
|
||||
|
||||
public function getCausaExencion(): string
|
||||
{
|
||||
return $this->CausaExencion;
|
||||
}
|
||||
|
||||
public function setCausaExencion(string $causaExencion): self
|
||||
{
|
||||
if (!preg_match('/^[A-Z]\d{2}$/', $causaExencion)) {
|
||||
throw new \InvalidArgumentException('CausaExencion must be a letter followed by two digits');
|
||||
}
|
||||
$this->CausaExencion = $causaExencion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBaseImponible(): float
|
||||
{
|
||||
return $this->BaseImponible;
|
||||
}
|
||||
|
||||
public function setBaseImponible(float $baseImponible): self
|
||||
{
|
||||
$parts = explode('.', (string)$baseImponible);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('BaseImponible must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('BaseImponible must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->BaseImponible = $baseImponible;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class PersonaFisicaJuridica
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NombreRazon')]
|
||||
protected $NombreRazon;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:NIF')]
|
||||
protected $NIF;
|
||||
|
||||
/** @var IDOtro|null */
|
||||
#[SerializedName('sum1:IDOtro')]
|
||||
protected $IDOtro;
|
||||
|
||||
public function getNombreRazon(): string
|
||||
{
|
||||
return $this->NombreRazon;
|
||||
}
|
||||
|
||||
public function setNombreRazon(string $nombreRazon): self
|
||||
{
|
||||
if (strlen($nombreRazon) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazon must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazon = $nombreRazon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNIF(): ?string
|
||||
{
|
||||
return $this->NIF;
|
||||
}
|
||||
|
||||
public function setNIF(?string $nif): self
|
||||
{
|
||||
if ($nif !== null) {
|
||||
if (!preg_match('/^[A-Z0-9]{9}$/', $nif)) {
|
||||
throw new \InvalidArgumentException('NIF must be a valid NIF (9 alphanumeric characters)');
|
||||
}
|
||||
$this->NIF = $nif;
|
||||
$this->IDOtro = null; // Clear IDOtro as it's a choice
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDOtro(): ?IDOtro
|
||||
{
|
||||
return $this->IDOtro;
|
||||
}
|
||||
|
||||
public function setIDOtro(?IDOtro $idOtro): self
|
||||
{
|
||||
if ($idOtro !== null) {
|
||||
$this->IDOtro = $idOtro;
|
||||
$this->NIF = null; // Clear NIF as it's a choice
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
// User type is a person submitting on behalf of the company.
|
||||
class PersonaFisicaJuridicaES
|
||||
{
|
||||
/** @var string NIF format */
|
||||
#[SerializedName('sum1:NIF')]
|
||||
protected $NIF;
|
||||
|
||||
/** @var string|null Max length 120 characters */
|
||||
#[SerializedName('sum1:NombreRazon')]
|
||||
protected $NombreRazon;
|
||||
|
||||
public function getNIF(): string
|
||||
{
|
||||
return $this->NIF;
|
||||
}
|
||||
|
||||
public function setNIF(string $nif): self
|
||||
{
|
||||
// Validate NIF format (letter or number followed by 8 numbers)
|
||||
if (!preg_match('/^[A-Z0-9][0-9]{8}$/', $nif)) {
|
||||
throw new \InvalidArgumentException('NIF must be a valid format (letter/number followed by 8 numbers)');
|
||||
}
|
||||
$this->NIF = $nif;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazon(): ?string
|
||||
{
|
||||
return $this->NombreRazon;
|
||||
}
|
||||
|
||||
public function setNombreRazon(?string $nombreRazon): self
|
||||
{
|
||||
if ($nombreRazon !== null && strlen($nombreRazon) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazon must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazon = $nombreRazon;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:Codigo')]
|
||||
protected $Codigo;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:Descripcion')]
|
||||
protected $Descripcion;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaHora')]
|
||||
protected $FechaHora;
|
||||
|
||||
public function getNumRegistroAcuerdoFacturacion(): string
|
||||
{
|
||||
return $this->NumRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setNumRegistroAcuerdoFacturacion(string $numRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
if (strlen($numRegistroAcuerdoFacturacion) > 15) {
|
||||
throw new \InvalidArgumentException('NumRegistroAcuerdoFacturacion must not exceed 15 characters');
|
||||
}
|
||||
$this->NumRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaRegistroAcuerdoFacturacion(): string
|
||||
{
|
||||
return $this->FechaRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setFechaRegistroAcuerdoFacturacion(string $fechaRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
// Validate date format YYYY-MM-DD
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaRegistroAcuerdoFacturacion)) {
|
||||
throw new \InvalidArgumentException('FechaRegistroAcuerdoFacturacion must be in YYYY-MM-DD format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($year, $month, $day) = explode('-', $fechaRegistroAcuerdoFacturacion);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
|
||||
$this->FechaRegistroAcuerdoFacturacion = $fechaRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMotivoRechazo(): string
|
||||
{
|
||||
return $this->MotivoRechazo;
|
||||
}
|
||||
|
||||
public function setMotivoRechazo(string $motivoRechazo): self
|
||||
{
|
||||
if (strlen($motivoRechazo) > 2000) {
|
||||
throw new \InvalidArgumentException('MotivoRechazo must not exceed 2000 characters');
|
||||
}
|
||||
$this->MotivoRechazo = $motivoRechazo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCodigo(): string
|
||||
{
|
||||
return $this->Codigo;
|
||||
}
|
||||
|
||||
public function setCodigo(string $codigo): self
|
||||
{
|
||||
if (!preg_match('/^\d{3}$/', $codigo)) {
|
||||
throw new \InvalidArgumentException('Codigo must be a 3-digit number');
|
||||
}
|
||||
$this->Codigo = $codigo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescripcion(): string
|
||||
{
|
||||
return $this->Descripcion;
|
||||
}
|
||||
|
||||
public function setDescripcion(string $descripcion): self
|
||||
{
|
||||
if (strlen($descripcion) > 500) {
|
||||
throw new \InvalidArgumentException('Descripcion must not exceed 500 characters');
|
||||
}
|
||||
$this->Descripcion = $descripcion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHora(): string
|
||||
{
|
||||
return $this->FechaHora;
|
||||
}
|
||||
|
||||
public function setFechaHora(string $fechaHora): self
|
||||
{
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/', $fechaHora)) {
|
||||
throw new \InvalidArgumentException('FechaHora must be in ISO 8601 format with timezone (e.g. 2024-09-13T19:20:30+01:00)');
|
||||
}
|
||||
$this->FechaHora = $fechaHora;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class RegFactuSistemaFacturacion
|
||||
{
|
||||
/** @var Cabecera */
|
||||
#[SerializedName('sum:Cabecera')]
|
||||
protected $Cabecera;
|
||||
|
||||
/** @var RegistroFactura */
|
||||
#[SerializedName('sum:RegistroFactura')]
|
||||
protected $RegistroFactura;
|
||||
|
||||
public function getCabecera(): Cabecera
|
||||
{
|
||||
return $this->Cabecera;
|
||||
}
|
||||
|
||||
public function setCabecera(Cabecera $cabecera): self
|
||||
{
|
||||
$this->Cabecera = $cabecera;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRegistroFactura(): RegistroFactura
|
||||
{
|
||||
return $this->RegistroFactura;
|
||||
}
|
||||
|
||||
public function setRegistroFactura(RegistroFactura $registroFactura): self
|
||||
{
|
||||
$this->RegistroFactura = $registroFactura;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
return $this->IDVersion;
|
||||
}
|
||||
|
||||
public function setIDVersion(string $idVersion): self
|
||||
{
|
||||
$this->IDVersion = $idVersion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDFactura(): IDFactura
|
||||
{
|
||||
return $this->IDFactura;
|
||||
}
|
||||
|
||||
public function setIDFactura(IDFactura $idFactura): self
|
||||
{
|
||||
$this->IDFactura = $idFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazonEmisor(): string
|
||||
{
|
||||
return $this->NombreRazonEmisor;
|
||||
}
|
||||
|
||||
public function setNombreRazonEmisor(string $nombreRazonEmisor): self
|
||||
{
|
||||
if (strlen($nombreRazonEmisor) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazonEmisor must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazonEmisor = $nombreRazonEmisor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoFactura(): string
|
||||
{
|
||||
return $this->TipoFactura;
|
||||
}
|
||||
|
||||
public function setTipoFactura(string $tipoFactura): self
|
||||
{
|
||||
if (!preg_match('/^F[1-4]$/', $tipoFactura)) {
|
||||
throw new \InvalidArgumentException('TipoFactura must be F1, F2, F3, or F4');
|
||||
}
|
||||
$this->TipoFactura = $tipoFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescripcionOperacion(): string
|
||||
{
|
||||
return $this->DescripcionOperacion;
|
||||
}
|
||||
|
||||
public function setDescripcionOperacion(string $descripcionOperacion): self
|
||||
{
|
||||
if (strlen($descripcionOperacion) > 500) {
|
||||
throw new \InvalidArgumentException('DescripcionOperacion must not exceed 500 characters');
|
||||
}
|
||||
$this->DescripcionOperacion = $descripcionOperacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDestinatarios(): Destinatarios
|
||||
{
|
||||
return $this->Destinatarios;
|
||||
}
|
||||
|
||||
public function setDestinatarios(Destinatarios $destinatarios): self
|
||||
{
|
||||
$this->Destinatarios = $destinatarios;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDesglose(): Desglose
|
||||
{
|
||||
return $this->Desglose;
|
||||
}
|
||||
|
||||
public function setDesglose(Desglose $desglose): self
|
||||
{
|
||||
$this->Desglose = $desglose;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaTotal(): float
|
||||
{
|
||||
return $this->CuotaTotal;
|
||||
}
|
||||
|
||||
public function setCuotaTotal(float $cuotaTotal): self
|
||||
{
|
||||
$this->CuotaTotal = $cuotaTotal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImporteTotal(): float
|
||||
{
|
||||
return $this->ImporteTotal;
|
||||
}
|
||||
|
||||
public function setImporteTotal(float $importeTotal): self
|
||||
{
|
||||
$this->ImporteTotal = $importeTotal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncadenamiento(): ?Encadenamiento
|
||||
{
|
||||
return $this->Encadenamiento;
|
||||
}
|
||||
|
||||
public function setEncadenamiento(?Encadenamiento $encadenamiento): self
|
||||
{
|
||||
$this->Encadenamiento = $encadenamiento;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSistemaInformatico(): SistemaInformatico
|
||||
{
|
||||
return $this->SistemaInformatico;
|
||||
}
|
||||
|
||||
public function setSistemaInformatico(SistemaInformatico $sistemaInformatico): self
|
||||
{
|
||||
$this->SistemaInformatico = $sistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHoraHusoGenRegistro(): string
|
||||
{
|
||||
return $this->FechaHoraHusoGenRegistro;
|
||||
}
|
||||
|
||||
public function setFechaHoraHusoGenRegistro(string $fechaHoraHusoGenRegistro): self
|
||||
{
|
||||
// Validate ISO 8601 date format with timezone
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/', $fechaHoraHusoGenRegistro)) {
|
||||
throw new \InvalidArgumentException('FechaHoraHusoGenRegistro must be in ISO 8601 format (YYYY-MM-DDThh:mm:ss±hh:mm)');
|
||||
}
|
||||
$this->FechaHoraHusoGenRegistro = $fechaHoraHusoGenRegistro;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoHuella(): string
|
||||
{
|
||||
return $this->TipoHuella;
|
||||
}
|
||||
|
||||
public function setTipoHuella(string $tipoHuella): self
|
||||
{
|
||||
if (!preg_match('/^\d{2}$/', $tipoHuella)) {
|
||||
throw new \InvalidArgumentException('TipoHuella must be a 2-digit number');
|
||||
}
|
||||
$this->TipoHuella = $tipoHuella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHuella(): string
|
||||
{
|
||||
return $this->Huella;
|
||||
}
|
||||
|
||||
public function setHuella(string $huella): self
|
||||
{
|
||||
$this->Huella = $huella;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class RegistroAnterior
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDEmisorFactura')]
|
||||
protected $IDEmisorFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NumSerieFactura')]
|
||||
protected $NumSerieFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaExpedicionFactura')]
|
||||
protected $FechaExpedicionFactura;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:Huella')]
|
||||
protected $Huella;
|
||||
|
||||
public function getIDEmisorFactura(): string
|
||||
{
|
||||
return $this->IDEmisorFactura;
|
||||
}
|
||||
|
||||
public function setIDEmisorFactura(string $IDEmisorFactura): self
|
||||
{
|
||||
$this->IDEmisorFactura = $IDEmisorFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumSerieFactura(): string
|
||||
{
|
||||
return $this->NumSerieFactura;
|
||||
}
|
||||
|
||||
public function setNumSerieFactura(string $NumSerieFactura): self
|
||||
{
|
||||
$this->NumSerieFactura = $NumSerieFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaExpedicionFactura(): string
|
||||
{
|
||||
return $this->FechaExpedicionFactura;
|
||||
}
|
||||
|
||||
public function setFechaExpedicionFactura(string $FechaExpedicionFactura): self
|
||||
{
|
||||
$this->FechaExpedicionFactura = $FechaExpedicionFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHuella(): string
|
||||
{
|
||||
return $this->Huella;
|
||||
}
|
||||
|
||||
public function setHuella(string $Huella): self
|
||||
{
|
||||
$this->Huella = $Huella;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class RegistroFactura
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:NumRegistroAcuerdoFacturacion')]
|
||||
protected $NumRegistroAcuerdoFacturacion;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:FechaHoraHusoGenRegistro')]
|
||||
protected $FechaHoraHusoGenRegistro;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:Huella')]
|
||||
protected $Huella;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:Signature')]
|
||||
protected $Signature;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:TipoHuella')]
|
||||
protected $TipoHuella;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:IDAcuerdoSistemaInformatico')]
|
||||
protected $IDAcuerdoSistemaInformatico;
|
||||
|
||||
/** @var RegistroAlta */
|
||||
#[SerializedName('sum1:RegistroAlta')]
|
||||
protected $RegistroAlta;
|
||||
|
||||
/** @var RegistroFacturacionAnulacion|null */
|
||||
#[SerializedName('sum1:RegistroAnulacion')]
|
||||
protected $RegistroAnulacion;
|
||||
|
||||
public function getNumRegistroAcuerdoFacturacion(): string
|
||||
{
|
||||
return $this->NumRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setNumRegistroAcuerdoFacturacion(string $numRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
if (strlen($numRegistroAcuerdoFacturacion) > 15) {
|
||||
throw new \InvalidArgumentException('NumRegistroAcuerdoFacturacion must not exceed 15 characters');
|
||||
}
|
||||
$this->NumRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHoraHusoGenRegistro(): string
|
||||
{
|
||||
return $this->FechaHoraHusoGenRegistro;
|
||||
}
|
||||
|
||||
public function setFechaHoraHusoGenRegistro(string $fechaHoraHusoGenRegistro): self
|
||||
{
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/', $fechaHoraHusoGenRegistro)) {
|
||||
throw new \InvalidArgumentException('FechaHoraHusoGenRegistro must be in ISO 8601 format with timezone (e.g. 2024-09-13T19:20:30+01:00)');
|
||||
}
|
||||
$this->FechaHoraHusoGenRegistro = $fechaHoraHusoGenRegistro;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHuella(): string
|
||||
{
|
||||
return $this->Huella;
|
||||
}
|
||||
|
||||
public function setHuella(string $huella): self
|
||||
{
|
||||
if (strlen($huella) > 64) {
|
||||
throw new \InvalidArgumentException('Huella must not exceed 64 characters');
|
||||
}
|
||||
$this->Huella = $huella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSignature(): ?string
|
||||
{
|
||||
return $this->Signature;
|
||||
}
|
||||
|
||||
public function setSignature(?string $signature): self
|
||||
{
|
||||
$this->Signature = $signature;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoHuella(): string
|
||||
{
|
||||
return $this->TipoHuella;
|
||||
}
|
||||
|
||||
public function setTipoHuella(string $tipoHuella): self
|
||||
{
|
||||
if ($tipoHuella !== '01') {
|
||||
throw new \InvalidArgumentException('TipoHuella must be "01" (SHA-256)');
|
||||
}
|
||||
$this->TipoHuella = $tipoHuella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDAcuerdoSistemaInformatico(): ?string
|
||||
{
|
||||
return $this->IDAcuerdoSistemaInformatico;
|
||||
}
|
||||
|
||||
public function setIDAcuerdoSistemaInformatico(?string $idAcuerdoSistemaInformatico): self
|
||||
{
|
||||
if ($idAcuerdoSistemaInformatico !== null && strlen($idAcuerdoSistemaInformatico) > 16) {
|
||||
throw new \InvalidArgumentException('IDAcuerdoSistemaInformatico must not exceed 16 characters');
|
||||
}
|
||||
$this->IDAcuerdoSistemaInformatico = $idAcuerdoSistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRegistroAlta(): RegistroAlta
|
||||
{
|
||||
return $this->RegistroAlta;
|
||||
}
|
||||
|
||||
public function setRegistroAlta(RegistroAlta $registroAlta): self
|
||||
{
|
||||
if ($registroAlta !== null && $this->RegistroAnulacion !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set both RegistroAlta and RegistroAnulacion');
|
||||
}
|
||||
$this->RegistroAlta = $registroAlta;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRegistroAnulacion(): ?RegistroFacturacionAnulacion
|
||||
{
|
||||
return $this->RegistroAnulacion;
|
||||
}
|
||||
|
||||
public function setRegistroAnulacion(?RegistroFacturacionAnulacion $registroAnulacion): self
|
||||
{
|
||||
if ($registroAnulacion !== null && $this->RegistroAlta !== null) {
|
||||
throw new \InvalidArgumentException('Cannot set both RegistroAlta and RegistroAnulacion');
|
||||
}
|
||||
$this->RegistroAnulacion = $registroAnulacion;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,532 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class RegistroFacturacionAlta
|
||||
{
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:IDVersion')]
|
||||
protected $IDVersion;
|
||||
|
||||
/** @var IDFacturaExpedida */
|
||||
#[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 Subsanacion|null */
|
||||
#[SerializedName('sum1:Subsanacion')]
|
||||
protected $Subsanacion;
|
||||
|
||||
/** @var RechazoPrevio|null */
|
||||
#[SerializedName('sum1:RechazoPrevio')]
|
||||
protected $RechazoPrevio;
|
||||
|
||||
/** @var string */
|
||||
#[SerializedName('sum1:TipoFactura')]
|
||||
protected $TipoFactura;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:TipoRectificativa')]
|
||||
protected $TipoRectificativa;
|
||||
|
||||
/** @var IDFacturaAR[]|null */
|
||||
#[SerializedName('sum1:FacturasRectificadas')]
|
||||
protected $FacturasRectificadas = [];
|
||||
|
||||
/** @var IDFacturaAR[]|null */
|
||||
#[SerializedName('sum1:FacturasSustituidas')]
|
||||
protected $FacturasSustituidas = [];
|
||||
|
||||
/** @var DesgloseRectificacion|null */
|
||||
#[SerializedName('sum1:ImporteRectificacion')]
|
||||
protected $ImporteRectificacion;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:FechaOperacion')]
|
||||
protected $FechaOperacion;
|
||||
|
||||
/** @var string Max length 500 characters */
|
||||
#[SerializedName('sum1:DescripcionOperacion')]
|
||||
protected $DescripcionOperacion;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:FacturaSimplificadaArt7273')]
|
||||
protected $FacturaSimplificadaArt7273;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:FacturaSinIdentifDestinatarioArt61d')]
|
||||
protected $FacturaSinIdentifDestinatarioArt61d;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:Macrodato')]
|
||||
protected $Macrodato;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:EmitidaPorTerceroODestinatario')]
|
||||
protected $EmitidaPorTerceroODestinatario;
|
||||
|
||||
/** @var PersonaFisicaJuridica|null */
|
||||
#[SerializedName('sum1:Tercero')]
|
||||
protected $Tercero;
|
||||
|
||||
/** @var PersonaFisicaJuridica[]|null */
|
||||
#[SerializedName('sum1:Destinatarios')]
|
||||
protected $Destinatarios = [];
|
||||
|
||||
/** @var array|null */
|
||||
#[SerializedName('sum1:Cupon')]
|
||||
protected $Cupon;
|
||||
|
||||
/** @var Desglose */
|
||||
#[SerializedName('sum1:Desglose')]
|
||||
protected $Desglose;
|
||||
|
||||
/** @var float */
|
||||
#[SerializedName('sum1:CuotaTotal')]
|
||||
protected $CuotaTotal;
|
||||
|
||||
/** @var float */
|
||||
#[SerializedName('sum1:ImporteTotal')]
|
||||
protected $ImporteTotal;
|
||||
|
||||
/** @var array */
|
||||
#[SerializedName('sum1:Encadenamiento')]
|
||||
protected $Encadenamiento;
|
||||
|
||||
/** @var SistemaInformatico */
|
||||
#[SerializedName('sum1:SistemaInformatico')]
|
||||
protected $SistemaInformatico;
|
||||
|
||||
/** @var \DateTime */
|
||||
#[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;
|
||||
|
||||
/** @var string Max length 64 characters */
|
||||
#[SerializedName('sum1:Huella')]
|
||||
protected $Huella = '';
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('sum1:Signature')]
|
||||
protected $Signature;
|
||||
|
||||
// Getters and setters with validation
|
||||
|
||||
public function getIDVersion(): string
|
||||
{
|
||||
return $this->IDVersion;
|
||||
}
|
||||
|
||||
public function setIDVersion(string $idVersion): self
|
||||
{
|
||||
$this->IDVersion = $idVersion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDFactura(): IDFacturaExpedida
|
||||
{
|
||||
return $this->IDFactura;
|
||||
}
|
||||
|
||||
public function setIDFactura(IDFacturaExpedida $idFactura): self
|
||||
{
|
||||
$this->IDFactura = $idFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRefExterna(): ?string
|
||||
{
|
||||
return $this->RefExterna;
|
||||
}
|
||||
|
||||
public function setRefExterna(?string $refExterna): self
|
||||
{
|
||||
if ($refExterna !== null && strlen($refExterna) > 70) {
|
||||
throw new \InvalidArgumentException('RefExterna must not exceed 70 characters');
|
||||
}
|
||||
$this->RefExterna = $refExterna;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazonEmisor(): string
|
||||
{
|
||||
return $this->NombreRazonEmisor;
|
||||
}
|
||||
|
||||
public function setNombreRazonEmisor(string $nombreRazonEmisor): self
|
||||
{
|
||||
if (strlen($nombreRazonEmisor) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazonEmisor must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazonEmisor = $nombreRazonEmisor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHuella(): string
|
||||
{
|
||||
return $this->Huella;
|
||||
}
|
||||
|
||||
public function setHuella(string $huella): self
|
||||
{
|
||||
if (strlen($huella) > 64) {
|
||||
throw new \InvalidArgumentException('Huella must not exceed 64 characters');
|
||||
}
|
||||
$this->Huella = $huella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSignature(): ?string
|
||||
{
|
||||
return $this->Signature;
|
||||
}
|
||||
|
||||
public function setSignature(?string $signature): self
|
||||
{
|
||||
$this->Signature = $signature;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHoraHusoGenRegistro(): \DateTime
|
||||
{
|
||||
return $this->FechaHoraHusoGenRegistro;
|
||||
}
|
||||
|
||||
public function setFechaHoraHusoGenRegistro(\DateTime $fechaHoraHusoGenRegistro): self
|
||||
{
|
||||
$this->FechaHoraHusoGenRegistro = $fechaHoraHusoGenRegistro;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumRegistroAcuerdoFacturacion(): ?string
|
||||
{
|
||||
return $this->NumRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setNumRegistroAcuerdoFacturacion(?string $numRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
if ($numRegistroAcuerdoFacturacion !== null && strlen($numRegistroAcuerdoFacturacion) > 15) {
|
||||
throw new \InvalidArgumentException('NumRegistroAcuerdoFacturacion must not exceed 15 characters');
|
||||
}
|
||||
$this->NumRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDAcuerdoSistemaInformatico(): ?string
|
||||
{
|
||||
return $this->IDAcuerdoSistemaInformatico;
|
||||
}
|
||||
|
||||
public function setIDAcuerdoSistemaInformatico(?string $idAcuerdoSistemaInformatico): self
|
||||
{
|
||||
if ($idAcuerdoSistemaInformatico !== null && strlen($idAcuerdoSistemaInformatico) > 16) {
|
||||
throw new \InvalidArgumentException('IDAcuerdoSistemaInformatico must not exceed 16 characters');
|
||||
}
|
||||
$this->IDAcuerdoSistemaInformatico = $idAcuerdoSistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoHuella(): string
|
||||
{
|
||||
return $this->TipoHuella;
|
||||
}
|
||||
|
||||
public function setTipoHuella(string $tipoHuella): self
|
||||
{
|
||||
if ($tipoHuella !== '01') {
|
||||
throw new \InvalidArgumentException('TipoHuella must be "01" (SHA-256)');
|
||||
}
|
||||
$this->TipoHuella = $tipoHuella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFacturasRectificadas(): array
|
||||
{
|
||||
return $this->FacturasRectificadas;
|
||||
}
|
||||
|
||||
public function addFacturaRectificada(IDFacturaAR $facturaRectificada): self
|
||||
{
|
||||
$this->FacturasRectificadas[] = $facturaRectificada;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFacturasSustituidas(): array
|
||||
{
|
||||
return $this->FacturasSustituidas;
|
||||
}
|
||||
|
||||
public function addFacturaSustituida(IDFacturaAR $facturaSustituida): self
|
||||
{
|
||||
$this->FacturasSustituidas[] = $facturaSustituida;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImporteRectificacion(): ?DesgloseRectificacion
|
||||
{
|
||||
return $this->ImporteRectificacion;
|
||||
}
|
||||
|
||||
public function setImporteRectificacion(?DesgloseRectificacion $importeRectificacion): self
|
||||
{
|
||||
$this->ImporteRectificacion = $importeRectificacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaOperacion(): ?string
|
||||
{
|
||||
return $this->FechaOperacion;
|
||||
}
|
||||
|
||||
public function setFechaOperacion(?string $fechaOperacion): self
|
||||
{
|
||||
if ($fechaOperacion !== null) {
|
||||
if (!preg_match('/^\d{2}-\d{2}-\d{4}$/', $fechaOperacion)) {
|
||||
throw new \InvalidArgumentException('FechaOperacion must be in DD-MM-YYYY format');
|
||||
}
|
||||
list($day, $month, $year) = explode('-', $fechaOperacion);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date in FechaOperacion');
|
||||
}
|
||||
}
|
||||
$this->FechaOperacion = $fechaOperacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescripcionOperacion(): string
|
||||
{
|
||||
return $this->DescripcionOperacion;
|
||||
}
|
||||
|
||||
public function setDescripcionOperacion(string $descripcionOperacion): self
|
||||
{
|
||||
if (strlen($descripcionOperacion) > 500) {
|
||||
throw new \InvalidArgumentException('DescripcionOperacion must not exceed 500 characters');
|
||||
}
|
||||
$this->DescripcionOperacion = $descripcionOperacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFacturaSimplificadaArt7273(): ?string
|
||||
{
|
||||
return $this->FacturaSimplificadaArt7273;
|
||||
}
|
||||
|
||||
public function setFacturaSimplificadaArt7273(?string $facturaSimplificadaArt7273): self
|
||||
{
|
||||
if ($facturaSimplificadaArt7273 !== null && !in_array($facturaSimplificadaArt7273, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('FacturaSimplificadaArt7273 must be either "S" or "N"');
|
||||
}
|
||||
$this->FacturaSimplificadaArt7273 = $facturaSimplificadaArt7273;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFacturaSinIdentifDestinatarioArt61d(): ?string
|
||||
{
|
||||
return $this->FacturaSinIdentifDestinatarioArt61d;
|
||||
}
|
||||
|
||||
public function setFacturaSinIdentifDestinatarioArt61d(?string $facturaSinIdentifDestinatarioArt61d): self
|
||||
{
|
||||
if ($facturaSinIdentifDestinatarioArt61d !== null && !in_array($facturaSinIdentifDestinatarioArt61d, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('FacturaSinIdentifDestinatarioArt61d must be either "S" or "N"');
|
||||
}
|
||||
$this->FacturaSinIdentifDestinatarioArt61d = $facturaSinIdentifDestinatarioArt61d;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMacrodato(): ?string
|
||||
{
|
||||
return $this->Macrodato;
|
||||
}
|
||||
|
||||
public function setMacrodato(?string $macrodato): self
|
||||
{
|
||||
if ($macrodato !== null && !in_array($macrodato, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('Macrodato must be either "S" or "N"');
|
||||
}
|
||||
$this->Macrodato = $macrodato;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEmitidaPorTerceroODestinatario(): ?string
|
||||
{
|
||||
return $this->EmitidaPorTerceroODestinatario;
|
||||
}
|
||||
|
||||
public function setEmitidaPorTerceroODestinatario(?string $emitidaPorTerceroODestinatario): self
|
||||
{
|
||||
if ($emitidaPorTerceroODestinatario !== null && !in_array($emitidaPorTerceroODestinatario, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('EmitidaPorTerceroODestinatario must be either "S" or "N"');
|
||||
}
|
||||
$this->EmitidaPorTerceroODestinatario = $emitidaPorTerceroODestinatario;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTercero(): ?PersonaFisicaJuridica
|
||||
{
|
||||
return $this->Tercero;
|
||||
}
|
||||
|
||||
public function setTercero(?PersonaFisicaJuridica $tercero): self
|
||||
{
|
||||
$this->Tercero = $tercero;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDestinatarios(): array
|
||||
{
|
||||
return $this->Destinatarios;
|
||||
}
|
||||
|
||||
public function addDestinatario(PersonaFisicaJuridica $destinatario): self
|
||||
{
|
||||
if (count($this->Destinatarios) >= 1000) {
|
||||
throw new \InvalidArgumentException('Maximum number of Destinatarios (1000) exceeded');
|
||||
}
|
||||
$this->Destinatarios[] = $destinatario;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCupon(): ?array
|
||||
{
|
||||
return $this->Cupon;
|
||||
}
|
||||
|
||||
public function setCupon(?array $cupon): self
|
||||
{
|
||||
$this->Cupon = $cupon;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDesglose(): Desglose
|
||||
{
|
||||
return $this->Desglose;
|
||||
}
|
||||
|
||||
public function setDesglose(Desglose $desglose): self
|
||||
{
|
||||
$this->Desglose = $desglose;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCuotaTotal(): float
|
||||
{
|
||||
return $this->CuotaTotal;
|
||||
}
|
||||
|
||||
public function setCuotaTotal(float $cuotaTotal): self
|
||||
{
|
||||
$parts = explode('.', (string)$cuotaTotal);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('CuotaTotal must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('CuotaTotal must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->CuotaTotal = $cuotaTotal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImporteTotal(): float
|
||||
{
|
||||
return $this->ImporteTotal;
|
||||
}
|
||||
|
||||
public function setImporteTotal(float $importeTotal): self
|
||||
{
|
||||
$parts = explode('.', (string)$importeTotal);
|
||||
$integerPart = $parts[0];
|
||||
$decimalPart = $parts[1] ?? '';
|
||||
|
||||
if (strlen($integerPart) > 12) {
|
||||
throw new \InvalidArgumentException('ImporteTotal must have at most 12 digits before decimal point');
|
||||
}
|
||||
if (strlen($decimalPart) > 2) {
|
||||
throw new \InvalidArgumentException('ImporteTotal must have at most 2 decimal places');
|
||||
}
|
||||
|
||||
$this->ImporteTotal = $importeTotal;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncadenamiento(): array
|
||||
{
|
||||
return $this->Encadenamiento;
|
||||
}
|
||||
|
||||
public function setEncadenamiento(array $encadenamiento): self
|
||||
{
|
||||
$this->Encadenamiento = $encadenamiento;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSistemaInformatico(): SistemaInformatico
|
||||
{
|
||||
return $this->SistemaInformatico;
|
||||
}
|
||||
|
||||
public function setSistemaInformatico(SistemaInformatico $sistemaInformatico): self
|
||||
{
|
||||
$this->SistemaInformatico = $sistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toRegistroAlta(): RegistroAlta
|
||||
{
|
||||
$registroAlta = new RegistroAlta();
|
||||
$registroAlta->setIDVersion($this->getIDVersion());
|
||||
|
||||
// Convert IDFacturaExpedida to IDFactura
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura($this->getIDFactura()->getIDEmisorFactura());
|
||||
$idFactura->setNumSerieFactura($this->getIDFactura()->getNumSerieFactura());
|
||||
$idFactura->setFechaExpedicionFactura($this->getIDFactura()->getFechaExpedicionFactura());
|
||||
$registroAlta->setIDFactura($idFactura);
|
||||
|
||||
$registroAlta->setNombreRazonEmisor($this->getNombreRazonEmisor());
|
||||
$registroAlta->setTipoFactura($this->getTipoFactura());
|
||||
$registroAlta->setDescripcionOperacion($this->getDescripcionOperacion());
|
||||
|
||||
// Convert array of Destinatarios to Destinatarios object
|
||||
$destinatarios = new Destinatarios();
|
||||
foreach ($this->getDestinatarios() as $destinatario) {
|
||||
$destinatarios->addDestinatario($destinatario);
|
||||
}
|
||||
$registroAlta->setDestinatarios($destinatarios);
|
||||
|
||||
$registroAlta->setDesglose($this->getDesglose());
|
||||
$registroAlta->setCuotaTotal($this->getCuotaTotal());
|
||||
$registroAlta->setImporteTotal($this->getImporteTotal());
|
||||
$registroAlta->setSistemaInformatico($this->getSistemaInformatico());
|
||||
$registroAlta->setFechaHoraHusoGenRegistro($this->getFechaHoraHusoGenRegistro()->format('Y-m-d\TH:i:sP'));
|
||||
$registroAlta->setTipoHuella($this->getTipoHuella());
|
||||
$registroAlta->setHuella($this->getHuella());
|
||||
|
||||
return $registroAlta;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
return $this->IDVersion;
|
||||
}
|
||||
|
||||
public function setIDVersion(string $idVersion): self
|
||||
{
|
||||
$this->IDVersion = $idVersion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDFactura(): IDFacturaAR
|
||||
{
|
||||
return $this->IDFactura;
|
||||
}
|
||||
|
||||
public function setIDFactura(IDFacturaAR $idFactura): self
|
||||
{
|
||||
$this->IDFactura = $idFactura;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRefExterna(): ?string
|
||||
{
|
||||
return $this->RefExterna;
|
||||
}
|
||||
|
||||
public function setRefExterna(?string $refExterna): self
|
||||
{
|
||||
if ($refExterna !== null && strlen($refExterna) > 70) {
|
||||
throw new \InvalidArgumentException('RefExterna must not exceed 70 characters');
|
||||
}
|
||||
$this->RefExterna = $refExterna;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNombreRazonEmisor(): string
|
||||
{
|
||||
return $this->NombreRazonEmisor;
|
||||
}
|
||||
|
||||
public function setNombreRazonEmisor(string $nombreRazonEmisor): self
|
||||
{
|
||||
if (strlen($nombreRazonEmisor) > 120) {
|
||||
throw new \InvalidArgumentException('NombreRazonEmisor must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreRazonEmisor = $nombreRazonEmisor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMotivoAnulacion(): ?string
|
||||
{
|
||||
return $this->MotivoAnulacion;
|
||||
}
|
||||
|
||||
public function setMotivoAnulacion(?string $motivoAnulacion): self
|
||||
{
|
||||
if ($motivoAnulacion !== null && strlen($motivoAnulacion) > 2000) {
|
||||
throw new \InvalidArgumentException('MotivoAnulacion must not exceed 2000 characters');
|
||||
}
|
||||
$this->MotivoAnulacion = $motivoAnulacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSistemaInformatico(): SistemaInformatico
|
||||
{
|
||||
return $this->SistemaInformatico;
|
||||
}
|
||||
|
||||
public function setSistemaInformatico(SistemaInformatico $sistemaInformatico): self
|
||||
{
|
||||
$this->SistemaInformatico = $sistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHuella(): string
|
||||
{
|
||||
return $this->Huella;
|
||||
}
|
||||
|
||||
public function setHuella(string $huella): self
|
||||
{
|
||||
if (strlen($huella) > 100) {
|
||||
throw new \InvalidArgumentException('Huella must not exceed 100 characters');
|
||||
}
|
||||
$this->Huella = $huella;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSignature(): ?string
|
||||
{
|
||||
return $this->Signature;
|
||||
}
|
||||
|
||||
public function setSignature(?string $signature): self
|
||||
{
|
||||
$this->Signature = $signature;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaHoraHusoGenRegistro(): string
|
||||
{
|
||||
return $this->FechaHoraHusoGenRegistro;
|
||||
}
|
||||
|
||||
public function setFechaHoraHusoGenRegistro(string $fechaHoraHusoGenRegistro): self
|
||||
{
|
||||
// Validate ISO 8601 format with timezone
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/', $fechaHoraHusoGenRegistro)) {
|
||||
throw new \InvalidArgumentException('FechaHoraHusoGenRegistro must be in ISO 8601 format with timezone (e.g. 2024-09-13T19:20:30+01:00)');
|
||||
}
|
||||
$this->FechaHoraHusoGenRegistro = $fechaHoraHusoGenRegistro;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumRegistroAcuerdoFacturacion(): ?string
|
||||
{
|
||||
return $this->NumRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setNumRegistroAcuerdoFacturacion(?string $numRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
if ($numRegistroAcuerdoFacturacion !== null && strlen($numRegistroAcuerdoFacturacion) > 15) {
|
||||
throw new \InvalidArgumentException('NumRegistroAcuerdoFacturacion must not exceed 15 characters');
|
||||
}
|
||||
$this->NumRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIDAcuerdoSistemaInformatico(): ?string
|
||||
{
|
||||
return $this->IDAcuerdoSistemaInformatico;
|
||||
}
|
||||
|
||||
public function setIDAcuerdoSistemaInformatico(?string $idAcuerdoSistemaInformatico): self
|
||||
{
|
||||
if ($idAcuerdoSistemaInformatico !== null && strlen($idAcuerdoSistemaInformatico) > 16) {
|
||||
throw new \InvalidArgumentException('IDAcuerdoSistemaInformatico must not exceed 16 characters');
|
||||
}
|
||||
$this->IDAcuerdoSistemaInformatico = $idAcuerdoSistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoHuella(): string
|
||||
{
|
||||
return $this->TipoHuella;
|
||||
}
|
||||
|
||||
public function setTipoHuella(string $tipoHuella): self
|
||||
{
|
||||
if ($tipoHuella !== '01') {
|
||||
throw new \InvalidArgumentException('TipoHuella must be "01" (SHA-256)');
|
||||
}
|
||||
$this->TipoHuella = $tipoHuella;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
return $this->NombreSistemaInformatico;
|
||||
}
|
||||
|
||||
public function setNombreSistemaInformatico(string $nombreSistemaInformatico): self
|
||||
{
|
||||
if (strlen($nombreSistemaInformatico) > 120) {
|
||||
throw new \InvalidArgumentException('NombreSistemaInformatico must not exceed 120 characters');
|
||||
}
|
||||
$this->NombreSistemaInformatico = $nombreSistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIdSistemaInformatico(): string
|
||||
{
|
||||
return $this->IdSistemaInformatico;
|
||||
}
|
||||
|
||||
public function setIdSistemaInformatico(string $idSistemaInformatico): self
|
||||
{
|
||||
if (strlen($idSistemaInformatico) > 20) {
|
||||
throw new \InvalidArgumentException('IdSistemaInformatico must not exceed 20 characters');
|
||||
}
|
||||
$this->IdSistemaInformatico = $idSistemaInformatico;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVersion(): string
|
||||
{
|
||||
return $this->Version;
|
||||
}
|
||||
|
||||
public function setVersion(string $version): self
|
||||
{
|
||||
if (strlen($version) > 20) {
|
||||
throw new \InvalidArgumentException('Version must not exceed 20 characters');
|
||||
}
|
||||
$this->Version = $version;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNumeroInstalacion(): string
|
||||
{
|
||||
return $this->NumeroInstalacion;
|
||||
}
|
||||
|
||||
public function setNumeroInstalacion(string $numeroInstalacion): self
|
||||
{
|
||||
if (strlen($numeroInstalacion) > 20) {
|
||||
throw new \InvalidArgumentException('NumeroInstalacion must not exceed 20 characters');
|
||||
}
|
||||
$this->NumeroInstalacion = $numeroInstalacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoUsoPosibleSoloVerifactu(): string
|
||||
{
|
||||
return $this->TipoUsoPosibleSoloVerifactu;
|
||||
}
|
||||
|
||||
public function setTipoUsoPosibleSoloVerifactu(string $tipoUsoPosibleSoloVerifactu): self
|
||||
{
|
||||
if (!in_array($tipoUsoPosibleSoloVerifactu, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('TipoUsoPosibleSoloVerifactu must be either "S" or "N"');
|
||||
}
|
||||
$this->TipoUsoPosibleSoloVerifactu = $tipoUsoPosibleSoloVerifactu;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTipoUsoPosibleMultiOT(): string
|
||||
{
|
||||
return $this->TipoUsoPosibleMultiOT;
|
||||
}
|
||||
|
||||
public function setTipoUsoPosibleMultiOT(string $tipoUsoPosibleMultiOT): self
|
||||
{
|
||||
if (!in_array($tipoUsoPosibleMultiOT, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('TipoUsoPosibleMultiOT must be either "S" or "N"');
|
||||
}
|
||||
$this->TipoUsoPosibleMultiOT = $tipoUsoPosibleMultiOT;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIndicadorMultiplesOT(): string
|
||||
{
|
||||
return $this->IndicadorMultiplesOT;
|
||||
}
|
||||
|
||||
public function setIndicadorMultiplesOT(string $indicadorMultiplesOT): self
|
||||
{
|
||||
if (!in_array($indicadorMultiplesOT, ['S', 'N'])) {
|
||||
throw new \InvalidArgumentException('IndicadorMultiplesOT must be either "S" or "N"');
|
||||
}
|
||||
$this->IndicadorMultiplesOT = $indicadorMultiplesOT;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\XmlRoot;
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SoapBody;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SoapHeader;
|
||||
|
||||
class SoapEnvelope
|
||||
{
|
||||
#[SerializedName('@xmlns:soapenv')]
|
||||
public $xmlns_soapenv = 'http://schemas.xmlsoap.org/soap/envelope/';
|
||||
|
||||
#[SerializedName('@xmlns:sum')]
|
||||
public $xmlns_sum = 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd';
|
||||
|
||||
#[SerializedName('@xmlns:sum1')]
|
||||
public $xmlns_sum1 = 'https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd';
|
||||
|
||||
#[SerializedName('@xmlns:xd')]
|
||||
public $xmlns_xd = 'http://www.w3.org/2000/09/xmldsig#';
|
||||
|
||||
/** @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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\EDocument\Standards\Verifactu\Types;
|
||||
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class SoapHeader
|
||||
{
|
||||
/** @var string|null */
|
||||
#[SerializedName('soapenv:Action')]
|
||||
protected $Action;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('soapenv:MessageID')]
|
||||
protected $MessageID;
|
||||
|
||||
/** @var string|null */
|
||||
#[SerializedName('soapenv:To')]
|
||||
protected $To;
|
||||
|
||||
public function getAction(): ?string
|
||||
{
|
||||
return $this->Action;
|
||||
}
|
||||
|
||||
public function setAction(?string $action): self
|
||||
{
|
||||
$this->Action = $action;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessageID(): ?string
|
||||
{
|
||||
return $this->MessageID;
|
||||
}
|
||||
|
||||
public function setMessageID(?string $messageID): self
|
||||
{
|
||||
$this->MessageID = $messageID;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTo(): ?string
|
||||
{
|
||||
return $this->To;
|
||||
}
|
||||
|
||||
public function setTo(?string $to): self
|
||||
{
|
||||
$this->To = $to;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
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
|
||||
{
|
||||
return $this->NumRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setNumRegistroAcuerdoFacturacion(string $numRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
if (strlen($numRegistroAcuerdoFacturacion) > 15) {
|
||||
throw new \InvalidArgumentException('NumRegistroAcuerdoFacturacion must not exceed 15 characters');
|
||||
}
|
||||
$this->NumRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFechaRegistroAcuerdoFacturacion(): string
|
||||
{
|
||||
return $this->FechaRegistroAcuerdoFacturacion;
|
||||
}
|
||||
|
||||
public function setFechaRegistroAcuerdoFacturacion(string $fechaRegistroAcuerdoFacturacion): self
|
||||
{
|
||||
// Validate date format YYYY-MM-DD
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaRegistroAcuerdoFacturacion)) {
|
||||
throw new \InvalidArgumentException('FechaRegistroAcuerdoFacturacion must be in YYYY-MM-DD format');
|
||||
}
|
||||
|
||||
// Validate date components
|
||||
list($year, $month, $day) = explode('-', $fechaRegistroAcuerdoFacturacion);
|
||||
if (!checkdate((int)$month, (int)$day, (int)$year)) {
|
||||
throw new \InvalidArgumentException('Invalid date');
|
||||
}
|
||||
|
||||
$this->FechaRegistroAcuerdoFacturacion = $fechaRegistroAcuerdoFacturacion;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/**
|
||||
* VerifactuClient.php
|
||||
*
|
||||
* SOAP client for sending invoices (facturas) to AEAT Verifactu service.
|
||||
* Supports production and test endpoints via a mode switch.
|
||||
*/
|
||||
namespace App\Services\EDocument\Standards\Verifactu;
|
||||
|
||||
|
||||
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Cabecera;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DesgloseRectificacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Detalle;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DetalleDesglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Encadenamiento;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaAR;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDOtro;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\ImporteSgn14_2;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Incidencia;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\ObligadoEmision;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\OperacionExenta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridica;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridicaES;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RechazoPrevio;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegFactuSistemaFacturacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAnterior;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAnulacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SistemaInformatico;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Subsanacion;
|
||||
|
||||
class VerifactuClient
|
||||
{
|
||||
const MODE_PROD = 'prod';
|
||||
const MODE_TEST = 'test';
|
||||
|
||||
/**
|
||||
* @var array<string,string>
|
||||
*/
|
||||
private static array $endpoints = [
|
||||
self::MODE_PROD => 'https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
|
||||
self::MODE_TEST => 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
|
||||
];
|
||||
|
||||
private \SoapClient $client;
|
||||
private string $mode;
|
||||
|
||||
/**
|
||||
* @param string $mode One of VerifactuClient::MODE_PROD or MODE_TEST
|
||||
* @param string|null $wsdl Path to the WSDL file; defaults to xsd/SistemaFacturacion.wsdl
|
||||
* @param array $options Additional SoapClient options
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \SoapFault
|
||||
*/
|
||||
public function __construct(string $mode = self::MODE_TEST, string $wsdl = null, array $options = [])
|
||||
{
|
||||
if (!isset(self::$endpoints[$mode])) {
|
||||
throw new \InvalidArgumentException("Invalid mode '{$mode}', must be 'prod' or 'test'.");
|
||||
}
|
||||
$this->mode = $mode;
|
||||
$endpoint = self::$endpoints[$mode];
|
||||
$wsdlPath = $wsdl ?: __DIR__ . '/xsd/SistemaFacturacion.wsdl';
|
||||
|
||||
// Default SOAP client options with classmap for generated s
|
||||
$defaultOpts = [
|
||||
'trace' => true,
|
||||
'exceptions' => true,
|
||||
'cache_wsdl' => WSDL_CACHE_NONE,
|
||||
'location' => $endpoint,
|
||||
'soap_version' => SOAP_1_1,
|
||||
'classmap' => [
|
||||
'Cabecera' => Cabecera::class,
|
||||
'Desglose' => Desglose::class,
|
||||
'DesgloseRectificacion' => DesgloseRectificacion::class,
|
||||
'Destinatarios' => Destinatarios::class,
|
||||
'Detalle' => Detalle::class,
|
||||
'DetalleDesglose' => DetalleDesglose::class,
|
||||
'Encadenamiento' => Encadenamiento::class,
|
||||
'IDDestinatario' => IDDestinatario::class,
|
||||
'IDFactura' => IDFactura::class,
|
||||
'IDFacturaAR' => IDFacturaAR::class,
|
||||
'IDFacturaExpedida' => IDFacturaExpedida::class,
|
||||
'IDOtro' => IDOtro::class,
|
||||
'ImporteSgn14_2' => ImporteSgn14_2::class,
|
||||
'Incidencia' => Incidencia::class,
|
||||
'ObligadoEmision' => ObligadoEmision::class,
|
||||
'OperacionExenta' => OperacionExenta::class,
|
||||
'PersonaFisicaJuridica' => PersonaFisicaJuridica::class,
|
||||
'PersonaFisicaJuridicaES' => PersonaFisicaJuridicaES::class,
|
||||
'RechazoPrevio' => RechazoPrevio::class,
|
||||
'RegFactuSistemaFacturacion' => RegFactuSistemaFacturacion::class,
|
||||
'RegistroAlta' => RegistroAlta::class,
|
||||
'RegistroAnterior' => RegistroAnterior::class,
|
||||
'RegistroFactura' => RegistroFactura::class,
|
||||
'RegistroFacturacionAlta' => RegistroFacturacionAlta::class,
|
||||
'RegistroFacturacionAnulacion' => RegistroFacturacionAnulacion::class,
|
||||
'RegistroFacturacionSubsanacion' => Subsanacion::class,
|
||||
'SistemaInformatico' => SistemaInformatico::class,
|
||||
],
|
||||
];
|
||||
|
||||
$opts = array_merge($defaultOpts, $options);
|
||||
|
||||
$this->client = new \SoapClient($wsdlPath, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an invoice registration (alta) request
|
||||
*
|
||||
* @param RegistroAlta $registro
|
||||
* @return mixed The SOAP response
|
||||
* @throws \SoapFault
|
||||
*/
|
||||
public function sendRegistroAlta(RegistroAlta $registro)
|
||||
{
|
||||
$factura = new RegistroFactura();
|
||||
$factura->setRegistroAlta($registro);
|
||||
|
||||
$wrapper = new RegFactuSistemaFacturacion();
|
||||
$wrapper->addToRegistroFactura($factura);
|
||||
|
||||
return $this->sendRegistroFactura($wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an invoice cancellation (anulación) request
|
||||
*
|
||||
* @param RegistroFacturacionAnulacion $registro
|
||||
* @return mixed The SOAP response
|
||||
* @throws \SoapFault
|
||||
*/
|
||||
public function sendRegistroAnulacion(RegistroFacturacionAnulacion $registro)
|
||||
{
|
||||
$factura = new RegistroFactura();
|
||||
$factura->setRegistroAnulacion($registro);
|
||||
|
||||
$wrapper = new RegFactuSistemaFacturacion();
|
||||
$wrapper->addToRegistroFactura($factura);
|
||||
|
||||
return $this->sendRegistroFactura($wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Low-level send: SoapClient marshals the object per classmap
|
||||
*
|
||||
* @param RegFactuSistemaFacturacion $wrapper
|
||||
* @return mixed The SOAP response
|
||||
* @throws \SoapFault
|
||||
*/
|
||||
public function sendRegistroFactura(RegFactuSistemaFacturacion $wrapper)
|
||||
{
|
||||
return $this->client->__soapCall(
|
||||
'RegFactuSistemaFacturacion',
|
||||
['RegFactuSistemaFacturacion' => $wrapper]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the last raw request XML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastRequest(): string
|
||||
{
|
||||
return $this->client->__getLastRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last raw response XML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastResponse(): string
|
||||
{
|
||||
return $this->client->__getLastResponse();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por AEAT (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:sfLRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" elementFormDefault="qualified">
|
||||
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<element name="ConsultaFactuSistemaFacturacion" type="sfLRC:ConsultaFactuSistemaFacturacionType">
|
||||
<annotation>
|
||||
<documentation>Servicio de consulta Registros Facturacion</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<complexType name="ConsultaFactuSistemaFacturacionType">
|
||||
<sequence>
|
||||
<element name="Cabecera" type="sf:CabeceraConsultaSf"/>
|
||||
<element name="FiltroConsulta" type="sfLRC:LRFiltroRegFacturacionType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="LRFiltroRegFacturacionType">
|
||||
<sequence>
|
||||
<!-- <element name="PeriodoImputacion" type="sf:PeriodoImputacionType"/> -->
|
||||
<element name="NumSerieFactura" type="sf:TextoIDFacturaType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Nº Serie+Nº Factura de la Factura del Emisor.</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="Contraparte" type="sf:ContraparteConsultaType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Contraparte del NIF de la cabecera que realiza la consulta.
|
||||
Obligado si la cosulta la realiza el Destinatario de los registros de facturacion.
|
||||
Destinatario si la cosulta la realiza el Obligado dde los registros de facturacion.</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="FechaExpedicionFactura" type="sf:FechaExpedicionConsultaType" minOccurs="0"/>
|
||||
<element name="SistemaInformatico" type="sf:SistemaInformaticoType" minOccurs="0"/>
|
||||
<element name="ClavePaginacion" type="sf:IDFacturaExpedidaBCType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</schema>
|
||||
|
|
@ -0,0 +1,823 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por AEAT (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/EventosSIF.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/EventosSIF.xsd" elementFormDefault="qualified">
|
||||
<import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd"/>
|
||||
<element name="RegistroEvento">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="IDVersion" type="sf:VersionType"/>
|
||||
<element name="Evento" type="sf:EventoType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<complexType name="EventoType">
|
||||
<sequence>
|
||||
<element name="SistemaInformatico" type="sf:SistemaInformaticoType"/>
|
||||
<element name="ObligadoEmision" type="sf:PersonaFisicaJuridicaESType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Obligado a expedir la factura. </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="EmitidaPorTerceroODestinatario" type="sf:TercerosODestinatarioType" minOccurs="0"/>
|
||||
<element name="TerceroODestinatario" type="sf:PersonaFisicaJuridicaType" minOccurs="0"/>
|
||||
<element name="FechaHoraHusoGenEvento" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="TipoEvento" type="sf:TipoEventoType"/>
|
||||
<element name="DatosPropiosEvento" type="sf:DatosPropiosEventoType" minOccurs="0"/>
|
||||
<element name="OtrosDatosEvento" type="sf:TextMax100Type" minOccurs="0"/>
|
||||
<element name="Encadenamiento" type="sf:EncadenamientoType"/>
|
||||
<element name="TipoHuella" type="sf:TipoHuellaType"/>
|
||||
<element name="HuellaEvento" type="sf:TextMax64Type"/>
|
||||
<element ref="ds:Signature"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="SistemaInformaticoType">
|
||||
<sequence>
|
||||
<sequence>
|
||||
<element name="NombreRazon" type="sf:TextMax120Type"/>
|
||||
<choice>
|
||||
<element name="NIF" type="sf:NIFType"/>
|
||||
<element name="IDOtro" type="sf:IDOtroType"/>
|
||||
</choice>
|
||||
</sequence>
|
||||
<element name="NombreSistemaInformatico" type="sf:TextMax30Type" minOccurs="0"/>
|
||||
<element name="IdSistemaInformatico" type="sf:TextMax2Type"/>
|
||||
<element name="Version" type="sf:TextMax50Type"/>
|
||||
<element name="NumeroInstalacion" type="sf:TextMax100Type"/>
|
||||
<element name="TipoUsoPosibleSoloVerifactu" type="sf:SiNoType" minOccurs="0"/>
|
||||
<element name="TipoUsoPosibleMultiOT" type="sf:SiNoType" minOccurs="0"/>
|
||||
<element name="IndicadorMultiplesOT" type="sf:SiNoType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="DatosPropiosEventoType">
|
||||
<choice>
|
||||
<element name="LanzamientoProcesoDeteccionAnomaliasRegFacturacion" type="sf:LanzamientoProcesoDeteccionAnomaliasRegFacturacionType"/>
|
||||
<element name="DeteccionAnomaliasRegFacturacion" type="sf:DeteccionAnomaliasRegFacturacionType"/>
|
||||
<element name="LanzamientoProcesoDeteccionAnomaliasRegEvento" type="sf:LanzamientoProcesoDeteccionAnomaliasRegEventoType"/>
|
||||
<element name="DeteccionAnomaliasRegEvento" type="sf:DeteccionAnomaliasRegEventoType"/>
|
||||
<element name="ExportacionRegFacturacionPeriodo" type="sf:ExportacionRegFacturacionPeriodoType"/>
|
||||
<element name="ExportacionRegEventoPeriodo" type="sf:ExportacionRegEventoPeriodoType"/>
|
||||
<element name="ResumenEventos" type="sf:ResumenEventosType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
<complexType name="EncadenamientoType">
|
||||
<choice>
|
||||
<element name="PrimerEvento" type="sf:TextMax1Type"/>
|
||||
<element name="EventoAnterior" type="sf:RegEventoAntType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
<complexType name="LanzamientoProcesoDeteccionAnomaliasRegFacturacionType">
|
||||
<sequence>
|
||||
<element name="RealizadoProcesoSobreIntegridadHuellasRegFacturacion" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosFacturacionProcesadosSobreIntegridadHuellas" type="sf:DigitosMax7Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreIntegridadFirmasRegFacturacion" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosFacturacionProcesadosSobreIntegridadFirmas" type="sf:DigitosMax7Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreTrazabilidadCadenaRegFacturacion" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosFacturacionProcesadosSobreTrazabilidadCadena" type="sf:DigitosMax7Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreTrazabilidadFechasRegFacturacion" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosFacturacionProcesadosSobreTrazabilidadFechas" type="sf:DigitosMax7Type" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="DeteccionAnomaliasRegFacturacionType">
|
||||
<sequence>
|
||||
<element name="TipoAnomalia" type="sf:TipoAnomaliaType"/>
|
||||
<element name="OtrosDatosAnomalia" type="sf:TextMax100Type" minOccurs="0"/>
|
||||
<element name="RegistroFacturacionAnomalo" type="sf:IDFacturaExpedidaType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="LanzamientoProcesoDeteccionAnomaliasRegEventoType">
|
||||
<sequence>
|
||||
<element name="RealizadoProcesoSobreIntegridadHuellasRegEvento" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosEventoProcesadosSobreIntegridadHuellas" type="sf:DigitosMax5Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreIntegridadFirmasRegEvento" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosEventoProcesadosSobreIntegridadFirmas" type="sf:DigitosMax5Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreTrazabilidadCadenaRegEvento" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosEventoProcesadosSobreTrazabilidadCadena" type="sf:DigitosMax5Type" minOccurs="0"/>
|
||||
<element name="RealizadoProcesoSobreTrazabilidadFechasRegEvento" type="sf:SiNoType"/>
|
||||
<element name="NumeroDeRegistrosEventoProcesadosSobreTrazabilidadFechas" type="sf:DigitosMax5Type" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="DeteccionAnomaliasRegEventoType">
|
||||
<sequence>
|
||||
<element name="TipoAnomalia" type="sf:TipoAnomaliaType"/>
|
||||
<element name="OtrosDatosAnomalia" type="sf:TextMax100Type" minOccurs="0"/>
|
||||
<element name="RegEventoAnomalo" type="sf:RegEventoType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="ExportacionRegFacturacionPeriodoType">
|
||||
<sequence>
|
||||
<element name="FechaHoraHusoInicioPeriodoExport" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="FechaHoraHusoFinPeriodoExport" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="RegistroFacturacionInicialPeriodo" type="sf:IDFacturaExpedidaHuellaType"/>
|
||||
<element name="RegistroFacturacionFinalPeriodo" type="sf:IDFacturaExpedidaHuellaType"/>
|
||||
<element name="NumeroDeRegistrosFacturacionAltaExportados" type="sf:DigitosMax9Type"/>
|
||||
<element name="SumaCuotaTotalAlta" type="sf:ImporteSgn12.2Type"/>
|
||||
<element name="SumaImporteTotalAlta" type="sf:ImporteSgn12.2Type"/>
|
||||
<element name="NumeroDeRegistrosFacturacionAnulacionExportados" type="sf:DigitosMax9Type"/>
|
||||
<element name="RegistrosFacturacionExportadosDejanDeConservarse" type="sf:SiNoType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="ExportacionRegEventoPeriodoType">
|
||||
<sequence>
|
||||
<element name="FechaHoraHusoInicioPeriodoExport" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="FechaHoraHusoFinPeriodoExport" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="RegistroEventoInicialPeriodo" type="sf:RegEventoType"/>
|
||||
<element name="RegistroEventoFinalPeriodo" type="sf:RegEventoType"/>
|
||||
<element name="NumeroDeRegEventoExportados" type="sf:DigitosMax7Type"/>
|
||||
<element name="RegEventoExportadosDejanDeConservarse" type="sf:SiNoType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="ResumenEventosType">
|
||||
<sequence>
|
||||
<element name="TipoEvento" type="sf:TipoEventoAgrType" maxOccurs="20"/>
|
||||
<element name="RegistroFacturacionInicialPeriodo" type="sf:IDFacturaExpedidaHuellaType" minOccurs="0"/>
|
||||
<element name="RegistroFacturacionFinalPeriodo" type="sf:IDFacturaExpedidaHuellaType" minOccurs="0"/>
|
||||
<element name="NumeroDeRegistrosFacturacionAltaGenerados" type="sf:DigitosMax6Type"/>
|
||||
<element name="SumaCuotaTotalAlta" type="sf:ImporteSgn12.2Type"/>
|
||||
<element name="SumaImporteTotalAlta" type="sf:ImporteSgn12.2Type"/>
|
||||
<element name="NumeroDeRegistrosFacturacionAnulacionGenerados" type="sf:DigitosMax6Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="RegEventoType">
|
||||
<sequence>
|
||||
<element name="TipoEvento" type="sf:TipoEventoType"/>
|
||||
<element name="FechaHoraHusoEvento" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="HuellaEvento" type="sf:TextMax64Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="RegEventoAntType">
|
||||
<sequence>
|
||||
<element name="TipoEvento" type="sf:TipoEventoType"/>
|
||||
<element name="FechaHoraHusoGenEvento" type="dateTime">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601)</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="HuellaEvento" type="sf:TextMax64Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="TipoEventoAgrType">
|
||||
<sequence>
|
||||
<element name="TipoEvento" type="sf:TipoEventoType"/>
|
||||
<element name="NumeroDeEventos" type="sf:DigitosMax4Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!-- Datos de persona Física o jurídica : Denominación, representación, identificación (NIF) -->
|
||||
<complexType name="PersonaFisicaJuridicaESType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Datos de una persona física o jurídica Española con un NIF asociado</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="NombreRazon" type="sf:TextMax120Type"/>
|
||||
<element name="NIF" type="sf:NIFType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<simpleType name="NIFType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">NIF</documentation>
|
||||
</annotation>
|
||||
<restriction base="string">
|
||||
<length value="9"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Datos de persona Física o jurídica : Denominación, representación, identificación (NIF/Otro) -->
|
||||
<complexType name="PersonaFisicaJuridicaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Datos de una persona física o jurídica Española o Extranjera</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="NombreRazon" type="sf:TextMax120Type"/>
|
||||
<choice>
|
||||
<element name="NIF" type="sf:NIFType"/>
|
||||
<element name="IDOtro" type="sf:IDOtroType"/>
|
||||
</choice>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!-- Datos de persona Física o jurídica : Denominación, representación, identificación (NIF/Otro) -->
|
||||
<complexType name="IDOtroType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Identificador de persona Física o jurídica distinto del NIF
|
||||
(Código pais, Tipo de Identificador, y hasta 15 caractéres)
|
||||
No se permite CodigoPais=ES e IDType=01-NIFContraparte
|
||||
para ese caso, debe utilizarse NIF en lugar de IDOtro.
|
||||
</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="CodigoPais" type="sf:CountryType2" minOccurs="0"/>
|
||||
<element name="IDType" type="sf:PersonaFisicaJuridicaIDTypeType"/>
|
||||
<element name="ID" type="sf:TextMax20Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!-- Tercero o Destinatario -->
|
||||
<simpleType name="TercerosODestinatarioType">
|
||||
<restriction base="string">
|
||||
<enumeration value="D">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Destinatario</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="T">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Tercero</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="SiNoType">
|
||||
<restriction base="string">
|
||||
<enumeration value="S"/>
|
||||
<enumeration value="N"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="VersionType">
|
||||
<restriction base="string">
|
||||
<enumeration value="1.0"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 120 caracteres -->
|
||||
<simpleType name="TextMax120Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="120"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 100 caracteres -->
|
||||
<simpleType name="TextMax100Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="100"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 64 caracteres -->
|
||||
<simpleType name="TextMax64Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="64"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 60 caracteres -->
|
||||
<simpleType name="TextMax60Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="60"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 50 caracteres -->
|
||||
<simpleType name="TextMax50Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="50"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 30 caracteres -->
|
||||
<simpleType name="TextMax30Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="30"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 20 caracteres -->
|
||||
<simpleType name="TextMax20Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="20"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 2 caracteres -->
|
||||
<simpleType name="TextMax2Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="2"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Cadena de 1 caracteres -->
|
||||
<simpleType name="TextMax1Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="1"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Definición de un tipo simple restringido a 9 dígitos -->
|
||||
<simpleType name="DigitosMax9Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="9"/>
|
||||
<pattern value="\d{1,9}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Definición de un tipo simple restringido a 7 dígitos -->
|
||||
<simpleType name="DigitosMax7Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="7"/>
|
||||
<pattern value="\d{1,7}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Definición de un tipo simple restringido a 6 dígitos -->
|
||||
<simpleType name="DigitosMax6Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="6"/>
|
||||
<pattern value="\d{1,6}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Definición de un tipo simple restringido a 5 dígitos -->
|
||||
<simpleType name="DigitosMax5Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="5"/>
|
||||
<pattern value="\d{1,5}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Definición de un tipo simple restringido a 4 dígitos -->
|
||||
<simpleType name="DigitosMax4Type">
|
||||
<restriction base="string">
|
||||
<maxLength value="4"/>
|
||||
<pattern value="\d{1,4}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Fecha (dd-mm-yyyy) -->
|
||||
<simpleType name="fecha">
|
||||
<restriction base="string">
|
||||
<length value="10"/>
|
||||
<pattern value="\d{2,2}-\d{2,2}-\d{4,4}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Importe de 15 dígitos (12+2) "." como separador decimal -->
|
||||
<simpleType name="ImporteSgn12.2Type">
|
||||
<restriction base="string">
|
||||
<pattern value="(\+|-)?\d{1,12}(\.\d{0,2})?"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Tipo de identificador fiscal de persona Física o jurídica -->
|
||||
<simpleType name="PersonaFisicaJuridicaIDTypeType">
|
||||
<restriction base="string">
|
||||
<enumeration value="02">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">NIF-IVA</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="03">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Pasaporte</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="04">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">IDEnPaisResidencia</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="05">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Certificado Residencia</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="06">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Otro documento Probatorio</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="07">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">No Censado</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<!-- Tipo Hash -->
|
||||
<simpleType name="TipoHuellaType">
|
||||
<restriction base="string">
|
||||
<enumeration value="01">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">SHA-256</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="TipoEventoType">
|
||||
<restriction base="string">
|
||||
<enumeration value="01">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Inicio del funcionamiento del sistema informático como «NO VERI*FACTU».</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="02">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Fin del funcionamiento del sistema informático como «NO VERI*FACTU».</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="03">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Lanzamiento del proceso de detección de anomalías en los registros de facturación.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="04">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Detección de anomalías en la integridad, inalterabilidad y trazabilidad de registros de facturación.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="05">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Lanzamiento del proceso de detección de anomalías en los registros de evento.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="06">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Detección de anomalías en la integridad, inalterabilidad y trazabilidad de registros de evento.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="07">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Restauración de copia de seguridad, cuando ésta se gestione desde el propio sistema informático de facturación.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="08">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Exportación de registros de facturación generados en un periodo.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="09">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Exportación de registros de evento generados en un periodo.</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="10">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Registro resumen de eventos</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="90">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Otros tipos de eventos a registrar voluntariamente por la persona o entidad productora del sistema informático.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="TipoAnomaliaType">
|
||||
<restriction base="string">
|
||||
<enumeration value="01">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Integridad-huella</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="02">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Integridad-firma</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="03">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Integridad - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="04">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-registro - Reg. no primero pero con reg. anterior no anotado o inexistente</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="05">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-registro - Reg. no último pero con reg. posterior no anotado o inexistente</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="06">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-registro - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="07">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-huella - Huella del reg. no se corresponde con la 'huella del reg. anterior' almacenada en el registro posterior</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="08">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-huella - Campo 'huella del reg. anterior' no se corresponde con la huella del reg. anterior</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="09">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena-huella - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="10">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-cadena - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="11">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-fechas - Fecha-hora anterior a la fecha del reg. anterior</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="12">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-fechas - Fecha-hora posterior a la fecha del reg. posterior</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="13">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-fechas - Reg. con fecha-hora de generación posterior a la fecha-hora actual del sistema</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="14">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad-fechas - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="15">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Trazabilidad - Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="90">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Otros</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<complexType name="IDFacturaExpedidaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Datos de identificación de factura expedida para operaciones de consulta</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDEmisorFactura" type="sf:NIFType"/>
|
||||
<element name="NumSerieFactura" type="sf:TextMax60Type"/>
|
||||
<element name="FechaExpedicionFactura" type="sf:fecha"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="IDFacturaExpedidaHuellaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Datos de encadenamiento </documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDEmisorFactura" type="sf:NIFType"/>
|
||||
<element name="NumSerieFactura" type="sf:TextMax60Type"/>
|
||||
<element name="FechaExpedicionFactura" type="sf:fecha"/>
|
||||
<element name="Huella" type="sf:TextMax64Type"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!-- ISO 3166-1 alpha-2 codes -->
|
||||
<simpleType name="CountryType2">
|
||||
<restriction base="string">
|
||||
<enumeration value="AF"/>
|
||||
<enumeration value="AL"/>
|
||||
<enumeration value="DE"/>
|
||||
<enumeration value="AD"/>
|
||||
<enumeration value="AO"/>
|
||||
<enumeration value="AI"/>
|
||||
<enumeration value="AQ"/>
|
||||
<enumeration value="AG"/>
|
||||
<enumeration value="SA"/>
|
||||
<enumeration value="DZ"/>
|
||||
<enumeration value="AR"/>
|
||||
<enumeration value="AM"/>
|
||||
<enumeration value="AW"/>
|
||||
<enumeration value="AU"/>
|
||||
<enumeration value="AT"/>
|
||||
<enumeration value="AZ"/>
|
||||
<enumeration value="BS"/>
|
||||
<enumeration value="BH"/>
|
||||
<enumeration value="BD"/>
|
||||
<enumeration value="BB"/>
|
||||
<enumeration value="BE"/>
|
||||
<enumeration value="BZ"/>
|
||||
<enumeration value="BJ"/>
|
||||
<enumeration value="BM"/>
|
||||
<enumeration value="BY"/>
|
||||
<enumeration value="BO"/>
|
||||
<enumeration value="BA"/>
|
||||
<enumeration value="BW"/>
|
||||
<enumeration value="BV"/>
|
||||
<enumeration value="BR"/>
|
||||
<enumeration value="BN"/>
|
||||
<enumeration value="BG"/>
|
||||
<enumeration value="BF"/>
|
||||
<enumeration value="BI"/>
|
||||
<enumeration value="BT"/>
|
||||
<enumeration value="CV"/>
|
||||
<enumeration value="KY"/>
|
||||
<enumeration value="KH"/>
|
||||
<enumeration value="CM"/>
|
||||
<enumeration value="CA"/>
|
||||
<enumeration value="CF"/>
|
||||
<enumeration value="CC"/>
|
||||
<enumeration value="CO"/>
|
||||
<enumeration value="KM"/>
|
||||
<enumeration value="CG"/>
|
||||
<enumeration value="CD"/>
|
||||
<enumeration value="CK"/>
|
||||
<enumeration value="KP"/>
|
||||
<enumeration value="KR"/>
|
||||
<enumeration value="CI"/>
|
||||
<enumeration value="CR"/>
|
||||
<enumeration value="HR"/>
|
||||
<enumeration value="CU"/>
|
||||
<enumeration value="TD"/>
|
||||
<enumeration value="CZ"/>
|
||||
<enumeration value="CL"/>
|
||||
<enumeration value="CN"/>
|
||||
<enumeration value="CY"/>
|
||||
<enumeration value="CW"/>
|
||||
<enumeration value="DK"/>
|
||||
<enumeration value="DM"/>
|
||||
<enumeration value="DO"/>
|
||||
<enumeration value="EC"/>
|
||||
<enumeration value="EG"/>
|
||||
<enumeration value="AE"/>
|
||||
<enumeration value="ER"/>
|
||||
<enumeration value="SK"/>
|
||||
<enumeration value="SI"/>
|
||||
<enumeration value="ES"/>
|
||||
<enumeration value="US"/>
|
||||
<enumeration value="EE"/>
|
||||
<enumeration value="ET"/>
|
||||
<enumeration value="FO"/>
|
||||
<enumeration value="PH"/>
|
||||
<enumeration value="FI"/>
|
||||
<enumeration value="FJ"/>
|
||||
<enumeration value="FR"/>
|
||||
<enumeration value="GA"/>
|
||||
<enumeration value="GM"/>
|
||||
<enumeration value="GE"/>
|
||||
<enumeration value="GS"/>
|
||||
<enumeration value="GH"/>
|
||||
<enumeration value="GI"/>
|
||||
<enumeration value="GD"/>
|
||||
<enumeration value="GR"/>
|
||||
<enumeration value="GL"/>
|
||||
<enumeration value="GU"/>
|
||||
<enumeration value="GT"/>
|
||||
<enumeration value="GG"/>
|
||||
<enumeration value="GN"/>
|
||||
<enumeration value="GQ"/>
|
||||
<enumeration value="GW"/>
|
||||
<enumeration value="GY"/>
|
||||
<enumeration value="HT"/>
|
||||
<enumeration value="HM"/>
|
||||
<enumeration value="HN"/>
|
||||
<enumeration value="HK"/>
|
||||
<enumeration value="HU"/>
|
||||
<enumeration value="IN"/>
|
||||
<enumeration value="ID"/>
|
||||
<enumeration value="IR"/>
|
||||
<enumeration value="IQ"/>
|
||||
<enumeration value="IE"/>
|
||||
<enumeration value="IM"/>
|
||||
<enumeration value="IS"/>
|
||||
<enumeration value="IL"/>
|
||||
<enumeration value="IT"/>
|
||||
<enumeration value="JM"/>
|
||||
<enumeration value="JP"/>
|
||||
<enumeration value="JE"/>
|
||||
<enumeration value="JO"/>
|
||||
<enumeration value="KZ"/>
|
||||
<enumeration value="KE"/>
|
||||
<enumeration value="KG"/>
|
||||
<enumeration value="KI"/>
|
||||
<enumeration value="KW"/>
|
||||
<enumeration value="LA"/>
|
||||
<enumeration value="LS"/>
|
||||
<enumeration value="LV"/>
|
||||
<enumeration value="LB"/>
|
||||
<enumeration value="LR"/>
|
||||
<enumeration value="LY"/>
|
||||
<enumeration value="LI"/>
|
||||
<enumeration value="LT"/>
|
||||
<enumeration value="LU"/>
|
||||
<enumeration value="XG"/>
|
||||
<enumeration value="MO"/>
|
||||
<enumeration value="MK"/>
|
||||
<enumeration value="MG"/>
|
||||
<enumeration value="MY"/>
|
||||
<enumeration value="MW"/>
|
||||
<enumeration value="MV"/>
|
||||
<enumeration value="ML"/>
|
||||
<enumeration value="MT"/>
|
||||
<enumeration value="FK"/>
|
||||
<enumeration value="MP"/>
|
||||
<enumeration value="MA"/>
|
||||
<enumeration value="MH"/>
|
||||
<enumeration value="MU"/>
|
||||
<enumeration value="MR"/>
|
||||
<enumeration value="YT"/>
|
||||
<enumeration value="UM"/>
|
||||
<enumeration value="MX"/>
|
||||
<enumeration value="FM"/>
|
||||
<enumeration value="MD"/>
|
||||
<enumeration value="MC"/>
|
||||
<enumeration value="MN"/>
|
||||
<enumeration value="ME"/>
|
||||
<enumeration value="MS"/>
|
||||
<enumeration value="MZ"/>
|
||||
<enumeration value="MM"/>
|
||||
<enumeration value="NA"/>
|
||||
<enumeration value="NR"/>
|
||||
<enumeration value="CX"/>
|
||||
<enumeration value="NP"/>
|
||||
<enumeration value="NI"/>
|
||||
<enumeration value="NE"/>
|
||||
<enumeration value="NG"/>
|
||||
<enumeration value="NU"/>
|
||||
<enumeration value="NF"/>
|
||||
<enumeration value="NO"/>
|
||||
<enumeration value="NC"/>
|
||||
<enumeration value="NZ"/>
|
||||
<enumeration value="IO"/>
|
||||
<enumeration value="OM"/>
|
||||
<enumeration value="NL"/>
|
||||
<enumeration value="BQ"/>
|
||||
<enumeration value="PK"/>
|
||||
<enumeration value="PW"/>
|
||||
<enumeration value="PA"/>
|
||||
<enumeration value="PG"/>
|
||||
<enumeration value="PY"/>
|
||||
<enumeration value="PE"/>
|
||||
<enumeration value="PN"/>
|
||||
<enumeration value="PF"/>
|
||||
<enumeration value="PL"/>
|
||||
<enumeration value="PT"/>
|
||||
<enumeration value="PR"/>
|
||||
<enumeration value="QA"/>
|
||||
<enumeration value="GB"/>
|
||||
<enumeration value="RW"/>
|
||||
<enumeration value="RO"/>
|
||||
<enumeration value="RU"/>
|
||||
<enumeration value="SB"/>
|
||||
<enumeration value="SV"/>
|
||||
<enumeration value="WS"/>
|
||||
<enumeration value="AS"/>
|
||||
<enumeration value="KN"/>
|
||||
<enumeration value="SM"/>
|
||||
<enumeration value="SX"/>
|
||||
<enumeration value="PM"/>
|
||||
<enumeration value="VC"/>
|
||||
<enumeration value="SH"/>
|
||||
<enumeration value="LC"/>
|
||||
<enumeration value="ST"/>
|
||||
<enumeration value="SN"/>
|
||||
<enumeration value="RS"/>
|
||||
<enumeration value="SC"/>
|
||||
<enumeration value="SL"/>
|
||||
<enumeration value="SG"/>
|
||||
<enumeration value="SY"/>
|
||||
<enumeration value="SO"/>
|
||||
<enumeration value="LK"/>
|
||||
<enumeration value="SZ"/>
|
||||
<enumeration value="ZA"/>
|
||||
<enumeration value="SD"/>
|
||||
<enumeration value="SS"/>
|
||||
<enumeration value="SE"/>
|
||||
<enumeration value="CH"/>
|
||||
<enumeration value="SR"/>
|
||||
<enumeration value="TH"/>
|
||||
<enumeration value="TW"/>
|
||||
<enumeration value="TZ"/>
|
||||
<enumeration value="TJ"/>
|
||||
<enumeration value="PS"/>
|
||||
<enumeration value="TF"/>
|
||||
<enumeration value="TL"/>
|
||||
<enumeration value="TG"/>
|
||||
<enumeration value="TK"/>
|
||||
<enumeration value="TO"/>
|
||||
<enumeration value="TT"/>
|
||||
<enumeration value="TN"/>
|
||||
<enumeration value="TC"/>
|
||||
<enumeration value="TM"/>
|
||||
<enumeration value="TR"/>
|
||||
<enumeration value="TV"/>
|
||||
<enumeration value="UA"/>
|
||||
<enumeration value="UG"/>
|
||||
<enumeration value="UY"/>
|
||||
<enumeration value="UZ"/>
|
||||
<enumeration value="VU"/>
|
||||
<enumeration value="VA"/>
|
||||
<enumeration value="VE"/>
|
||||
<enumeration value="VN"/>
|
||||
<enumeration value="VG"/>
|
||||
<enumeration value="VI"/>
|
||||
<enumeration value="WF"/>
|
||||
<enumeration value="YE"/>
|
||||
<enumeration value="DJ"/>
|
||||
<enumeration value="ZM"/>
|
||||
<enumeration value="ZW"/>
|
||||
<enumeration value="QU"/>
|
||||
<enumeration value="XB"/>
|
||||
<enumeration value="XU"/>
|
||||
<enumeration value="XN"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</schema>
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por AEAT (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:sfLRRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaConsultaLR.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaConsultaLR.xsd" elementFormDefault="qualified">
|
||||
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<element name="RespuestaConsultaFactuSistemaFacturacion" type="sfLRRC:RespuestaConsultaFactuSistemaFacturacionType">
|
||||
<annotation>
|
||||
<documentation>Servicio de consulta de regIstros de facturacion</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<complexType name="RespuestaConsultaFactuSistemaFacturacionType">
|
||||
<complexContent>
|
||||
<extension base="sfLRRC:RespuestaConsultaType">
|
||||
<sequence>
|
||||
<element name="RegistroRespuestaConsultaFactuSistemaFacturacion" type="sfLRRC:RegistroRespuestaConsultaRegFacturacionType" minOccurs="0" maxOccurs="10000"/>
|
||||
<element name="ClavePaginacion" type="sf:IDFacturaExpedidaBCType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</extension>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
<complexType name="EstadoRegFactuType">
|
||||
<sequence>
|
||||
<element name="TimestampUltimaModificacion" type="dateTime"/>
|
||||
<element name="EstadoRegistro" type="sfLRRC:EstadoRegistroType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Estado del registro almacenado en el sistema. Los estados posibles son: Correcta, AceptadaConErrores y Anulada
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="CodigoErrorRegistro" type="sfLRRC:ErrorDetalleType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Código del error de registro, en su caso.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="DescripcionErrorRegistro" type="sf:TextMax500Type" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Descripción detallada del error de registro, en su caso.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="RegistroRespuestaConsultaRegFacturacionType">
|
||||
<sequence>
|
||||
<element name="IDFactura" type="sf:IDFacturaExpedidaType"/>
|
||||
<element name="DatosRegistroFacturacion" type="sfLRRC:RespuestaDatosRegistroFacturacionType"/>
|
||||
<element name="DatosPresentacion" type="sf:DatosPresentacion2Type" minOccurs="0"/>
|
||||
<element name="EstadoRegistro" type="sfLRRC:EstadoRegFactuType" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="RespuestaConsultaType">
|
||||
<sequence>
|
||||
<element name="Cabecera" type="sf:CabeceraConsultaSf"/>
|
||||
<element name="PeriodoImputacion">
|
||||
<complexType>
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Período al que corresponden los apuntes. todos los apuntes deben corresponder al mismo período impositivo </documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="Ejercicio" type="sf:YearType"/>
|
||||
<element name="Periodo" type="sf:TipoPeriodoType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="IndicadorPaginacion" type="sfLRRC:IndicadorPaginacionType"/>
|
||||
<element name="ResultadoConsulta" type="sfLRRC:ResultadoConsultaType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!-- Datos del registro de facturacion -->
|
||||
<complexType name="RespuestaDatosRegistroFacturacionType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Apunte correspondiente al libro de facturas expedidas. </documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="RefExterna" type="sf:TextMax70Type" minOccurs="0"/>
|
||||
<element name="Subsanacion" type="sf:SubsanacionType" minOccurs="0"/>
|
||||
<element name="RechazoPrevio" type="sf:RechazoPrevioType" minOccurs="0"/>
|
||||
<element name="SinRegistroPrevio" type="sf:SinRegistroPrevioType" minOccurs="0"/>
|
||||
<element name="GeneradoPor" type="sf:GeneradoPorType" minOccurs="0"/>
|
||||
<element name="Generador" type="sf:PersonaFisicaJuridicaType" minOccurs="0"/>
|
||||
<element name="TipoFactura" type="sf:ClaveTipoFacturaType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Clave del tipo de factura </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="TipoRectificativa" type="sf:ClaveTipoRectificativaType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Identifica si el tipo de factura rectificativa es por sustitución o por diferencia </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="FacturasRectificadas" minOccurs="0">
|
||||
<complexType>
|
||||
<annotation>
|
||||
<documentation xml:lang="es">El ID de las facturas rectificadas, únicamente se rellena en el caso de rectificación de facturas</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDFacturaRectificada" type="sf:IDFacturaARType" maxOccurs="1000"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="FacturasSustituidas" minOccurs="0">
|
||||
<complexType>
|
||||
<annotation>
|
||||
<documentation xml:lang="es">El ID de las facturas sustituidas, únicamente se rellena en el caso de facturas sustituidas</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDFacturaSustituida" type="sf:IDFacturaARType" maxOccurs="1000"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="ImporteRectificacion" type="sf:DesgloseRectificacionType" minOccurs="0"/>
|
||||
<element name="FechaOperacion" type="sf:fecha" minOccurs="0"/>
|
||||
<element name="DescripcionOperacion" type="sf:TextMax500Type" minOccurs="0"/>
|
||||
<element name="FacturaSimplificadaArt7273" type="sf:SimplificadaCualificadaType" minOccurs="0"/>
|
||||
<element name="FacturaSinIdentifDestinatarioArt61d" type="sf:CompletaSinDestinatarioType" minOccurs="0"/>
|
||||
<element name="Macrodato" type="sf:MacrodatoType" minOccurs="0"/>
|
||||
<element name="EmitidaPorTerceroODestinatario" type="sf:TercerosODestinatarioType" minOccurs="0"/>
|
||||
<element name="Tercero" type="sf:PersonaFisicaJuridicaType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Tercero que expida la factura y/o genera el registro de alta. </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="Destinatarios" minOccurs="0">
|
||||
<complexType>
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Contraparte de la operación. Cliente</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDDestinatario" type="sf:PersonaFisicaJuridicaType" maxOccurs="1000"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="Cupon" type="sf:CuponType" minOccurs="0"/>
|
||||
<element name="Desglose" type="sf:DesgloseType" minOccurs="0"/>
|
||||
<element name="CuotaTotal" type="sf:ImporteSgn12.2Type" minOccurs="0"/>
|
||||
<element name="ImporteTotal" type="sf:ImporteSgn12.2Type" minOccurs="0"/>
|
||||
<element name="Encadenamiento" minOccurs="0">
|
||||
<complexType>
|
||||
<choice>
|
||||
<element name="PrimerRegistro" type="sf:PrimerRegistroCadenaType"/>
|
||||
<element name="RegistroAnterior" type="sf:EncadenamientoFacturaAnteriorType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="FechaHoraHusoGenRegistro" type="dateTime" minOccurs="0"/>
|
||||
<element name="NumRegistroAcuerdoFacturacion" type="sf:TextMax15Type" minOccurs="0"/>
|
||||
<element name="IdAcuerdoSistemaInformatico" type="sf:TextMax16Type" minOccurs="0"/>
|
||||
<element name="TipoHuella" type="sf:TipoHuellaType" minOccurs="0"/>
|
||||
<element name="Huella" type="sf:TextMax64Type" minOccurs="0"/>
|
||||
<element name="NifRepresentante" type="sf:NIFType" minOccurs="0"/>
|
||||
<element name="FechaFinVeriFactu" type="sf:fecha" minOccurs="0"/>
|
||||
<element name="Incidencia" type="sf:IncidenciaType" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<simpleType name="IndicadorPaginacionType">
|
||||
<restriction base="string">
|
||||
<enumeration value="S"/>
|
||||
<enumeration value="N"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="ResultadoConsultaType">
|
||||
<restriction base="string">
|
||||
<enumeration value="ConDatos"/>
|
||||
<enumeration value="SinDatos"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="ErrorDetalleType">
|
||||
<restriction base="integer"/>
|
||||
</simpleType>
|
||||
<!-- Estado del registro almacenado en el sistema -->
|
||||
<simpleType name="EstadoRegistroType">
|
||||
<restriction base="string">
|
||||
<enumeration value="Correcta">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">El registro se almacenado sin errores</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="AceptadaConErrores">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">El registro se almacenado tiene algunos errores. Ver detalle del error</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="Anulada">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">El registro almacenado ha sido anulado</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</schema>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por Puesto de Trabajo (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:sfR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" xmlns:sfLR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd" elementFormDefault="qualified">
|
||||
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
||||
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" schemaLocation="SuministroLR.xsd"/>
|
||||
<element name="RespuestaRegFactuSistemaFacturacion" type="sfR:RespuestaRegFactuSistemaFacturacionType"/>
|
||||
<complexType name="RespuestaBaseType">
|
||||
<sequence>
|
||||
<element name="CSV" type="string" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> CSV asociado al envío generado por AEAT. Solo se genera si no hay rechazo del envio</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="DatosPresentacion" type="sf:DatosPresentacionType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Se devuelven datos de la presentacion realizada. Solo se genera si no hay rechazo del envio </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="Cabecera" type="sf:CabeceraType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Se devuelve la cabecera que se incluyó en el envío. </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="TiempoEsperaEnvio" type="sf:Tipo6Type"/>
|
||||
<element name="EstadoEnvio" type="sfR:EstadoEnvioType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Estado del envío en conjunto.
|
||||
Si los datos de cabecera y todos los registros son correctos,el estado es correcto.
|
||||
En caso de estructura y cabecera correctos donde todos los registros son incorrectos, el estado es incorrecto
|
||||
En caso de estructura y cabecera correctos con al menos un registro incorrecto, el estado global es parcialmente correcto.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="RespuestaRegFactuSistemaFacturacionType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Respuesta a un envío de registro de facturacion</documentation>
|
||||
</annotation>
|
||||
<complexContent>
|
||||
<extension base="sfR:RespuestaBaseType">
|
||||
<sequence>
|
||||
<element name="RespuestaLinea" type="sfR:RespuestaExpedidaType" minOccurs="0" maxOccurs="1000">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Estado detallado de cada línea del suministro.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
</sequence>
|
||||
</extension>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
<complexType name="RespuestaExpedidaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> Respuesta a un envío </documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<element name="IDFactura" type="sf:IDFacturaExpedidaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es"> ID Factura Expedida </documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="Operacion" type="sf:OperacionType"/>
|
||||
<element name="RefExterna" type="sf:TextMax70Type" minOccurs="0"/>
|
||||
<element name="EstadoRegistro" type="sfR:EstadoRegistroType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Estado del registro. Correcto o Incorrecto
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="CodigoErrorRegistro" type="sfR:ErrorDetalleType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Código del error de registro, en su caso.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="DescripcionErrorRegistro" type="sf:TextMax1500Type" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Descripción detallada del error de registro, en su caso.
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
<element name="RegistroDuplicado" type="sf:RegistroDuplicadoType" minOccurs="0">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">
|
||||
Solo en el caso de que se rechace el registro por duplicado se devuelve este nodo con la informacion registrada en el sistema para este registro
|
||||
</documentation>
|
||||
</annotation>
|
||||
</element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<simpleType name="EstadoEnvioType">
|
||||
<restriction base="string">
|
||||
<enumeration value="Correcto">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Correcto</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="ParcialmenteCorrecto">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Parcialmente correcto. Ver detalle de errores</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="Incorrecto">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Incorrecto</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="EstadoRegistroType">
|
||||
<restriction base="string">
|
||||
<enumeration value="Correcto">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Correcto</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="AceptadoConErrores">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Aceptado con Errores. Ver detalle del error</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
<enumeration value="Incorrecto">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Incorrecto</documentation>
|
||||
</annotation>
|
||||
</enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<simpleType name="ErrorDetalleType">
|
||||
<restriction base="integer"/>
|
||||
</simpleType>
|
||||
</schema>
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por AEAT (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:sfLR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" xmlns:sfR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd" xmlns:sfLRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sfLRRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaConsultaLR.xsd" xmlns:sfWdsl="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SistemaFacturacion.wsdl" xmlns:ns="http://www.w3.org/2000/09/xmldsig#" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SistemaFacturacion.wsdl">
|
||||
<wsdl:types>
|
||||
<xs:schema targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SistemaFacturacion.wsdl" elementFormDefault="qualified" xmlns:sfWdsl="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SistemaFacturacion.wsdl" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" xmlns:sfLR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" xmlns:sfLRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sfLRRC="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaConsultaLR.xsd">
|
||||
<xs:import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
||||
<xs:import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" schemaLocation="SuministroLR.xsd"/>
|
||||
<xs:import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" schemaLocation="ConsultaLR.xsd"/>
|
||||
<xs:import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaConsultaLR.xsd" schemaLocation="RespuestaConsultaLR.xsd"/>
|
||||
<xs:import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd" schemaLocation="RespuestaSuministro.xsd"/>
|
||||
</xs:schema>
|
||||
</wsdl:types>
|
||||
<wsdl:message name="EntradaRegFactuSistemaFacturacion">
|
||||
<wsdl:part name="RegFactuSistemaFacturacion" element="sfLR:RegFactuSistemaFacturacion"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="EntradaConsultaFactuSistemaFacturacion">
|
||||
<wsdl:part name="ConsultaFactuSistemaFacturacion" element="sfLRC:ConsultaFactuSistemaFacturacion"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="RespuestaRegFactuSistemaFacturacion">
|
||||
<wsdl:part name="RespuestaRegFactuSistemaFacturacion" element="sfR:RespuestaRegFactuSistemaFacturacion"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="RespuestaConsultaFactuSistemaFacturacion">
|
||||
<wsdl:part name="RespuestaConsultaFactuSistemaFacturacion" element="sfLRRC:RespuestaConsultaFactuSistemaFacturacion"/>
|
||||
</wsdl:message>
|
||||
<wsdl:portType name="sfPortTypeVerifactu">
|
||||
<wsdl:operation name="RegFactuSistemaFacturacion">
|
||||
<wsdl:input message="sfWdsl:EntradaRegFactuSistemaFacturacion"/>
|
||||
<wsdl:output message="sfWdsl:RespuestaRegFactuSistemaFacturacion"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="ConsultaFactuSistemaFacturacion">
|
||||
<wsdl:input message="sfWdsl:EntradaConsultaFactuSistemaFacturacion"/>
|
||||
<wsdl:output message="sfWdsl:RespuestaConsultaFactuSistemaFacturacion"/>
|
||||
</wsdl:operation>
|
||||
</wsdl:portType>
|
||||
<wsdl:portType name="sfPortTypePorRequerimiento">
|
||||
<wsdl:operation name="RegFactuSistemaFacturacion">
|
||||
<wsdl:input message="sfWdsl:EntradaRegFactuSistemaFacturacion"/>
|
||||
<wsdl:output message="sfWdsl:RespuestaRegFactuSistemaFacturacion"/>
|
||||
</wsdl:operation>
|
||||
</wsdl:portType>
|
||||
<wsdl:binding name="sfVerifactu" type="sfWdsl:sfPortTypeVerifactu">
|
||||
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
|
||||
<wsdl:operation name="RegFactuSistemaFacturacion">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="ConsultaFactuSistemaFacturacion">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
</wsdl:binding>
|
||||
<wsdl:binding name="sfRequerimiento" type="sfWdsl:sfPortTypePorRequerimiento">
|
||||
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
|
||||
<wsdl:operation name="RegFactuSistemaFacturacion">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
</wsdl:binding>
|
||||
<wsdl:service name="sfVerifactu">
|
||||
<!-- Sistemas que emiten facturas verificables. Entorno de PRODUCCION -->
|
||||
<wsdl:port name="SistemaVerifactu" binding="sfWdsl:sfVerifactu">
|
||||
<soap:address location="https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas verificables. Entorno de PRODUCCION para acceso con certificado de sello -->
|
||||
<wsdl:port name="SistemaVerifactuSello" binding="sfWdsl:sfVerifactu">
|
||||
<soap:address location="https://www10.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas verificables. Entorno de PRUEBAS -->
|
||||
<wsdl:port name="SistemaVerifactuPruebas" binding="sfWdsl:sfVerifactu">
|
||||
<soap:address location="https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas verificables. Entorno de PRUEBAS para acceso con certificado de sello -->
|
||||
<wsdl:port name="SistemaVerifactuSelloPruebas" binding="sfWdsl:sfVerifactu">
|
||||
<soap:address location="https://prewww10.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"/>
|
||||
</wsdl:port>
|
||||
</wsdl:service>
|
||||
<wsdl:service name="sfRequerimiento">
|
||||
<!-- Sistemas que emiten facturas NO verificables. (Remision bajo requerimiento). Entorno de PRODUCCION -->
|
||||
<wsdl:port name="SistemaRequerimiento" binding="sfWdsl:sfRequerimiento">
|
||||
<soap:address location="https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas NO verificables. (Remision bajo requerimiento). Entorno de PRODUCCION para acceso con certificado de sello -->
|
||||
<wsdl:port name="SistemaRequerimientoSello" binding="sfWdsl:sfRequerimiento">
|
||||
<soap:address location="https://www10.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas NO verificables. (Remision bajo requerimiento). Entorno de PRUEBAS -->
|
||||
<wsdl:port name="SistemaRequerimientoPruebas" binding="sfWdsl:sfRequerimiento">
|
||||
<soap:address location="https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP"/>
|
||||
</wsdl:port>
|
||||
<!-- Sistemas que emiten facturas NO verificables. (Remision bajo requerimiento). Entorno de PRUEBAS para acceso con certificado de sello -->
|
||||
<wsdl:port name="SistemaRequerimientoSelloPruebas" binding="sfWdsl:sfRequerimiento">
|
||||
<soap:address location="https://prewww10.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/RequerimientoSOAP"/>
|
||||
</wsdl:port>
|
||||
</wsdl:service>
|
||||
</wsdl:definitions>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por Puesto de Trabajo (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
||||
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:sfLR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" elementFormDefault="qualified">
|
||||
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
||||
<element name="RegFactuSistemaFacturacion">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="Cabecera" type="sf:CabeceraType"/>
|
||||
<element name="RegistroFactura" type="sfLR:RegistroFacturaType" maxOccurs="1000"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<complexType name="RegistroFacturaType">
|
||||
<annotation>
|
||||
<documentation xml:lang="es">Datos correspondientes a los registros de facturacion</documentation>
|
||||
</annotation>
|
||||
<sequence>
|
||||
<choice>
|
||||
<element ref="sf:RegistroAlta"/>
|
||||
<element ref="sf:RegistroAnulacion"/>
|
||||
</choice>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</schema>
|
||||
|
|
@ -0,0 +1,584 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Verifactu;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use App\Services\EDocument\Standards\Verifactu\VerifactuClient;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Cabecera;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DesgloseRectificacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DetalleDesglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Encadenamiento;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaAR;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDOtro;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\ObligadoEmision;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegFactuSistemaFacturacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAnulacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SistemaInformatico;
|
||||
|
||||
class InvoiceSchemaTestsAeat extends TestCase
|
||||
{
|
||||
private VerifactuClient $client;
|
||||
private const TEST_COMPANY_NIF = 'B12345678';
|
||||
private const TEST_COMPANY_NAME = 'TEST COMPANY SL';
|
||||
private const TEST_CLIENT_NIF = 'B87654321';
|
||||
private const TEST_CLIENT_NAME = 'TEST CLIENT SL';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->client = new VerifactuClient(VerifactuClient::MODE_TEST);
|
||||
}
|
||||
|
||||
public function testF1StandardInvoice(): void
|
||||
{
|
||||
$registro = $this->createF1StandardInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
// Additional assertions could be added based on expected response structure
|
||||
}
|
||||
|
||||
public function testF2SimplifiedInvoice(): void
|
||||
{
|
||||
$registro = $this->createF2SimplifiedInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testF3SubstituteInvoice(): void
|
||||
{
|
||||
$registro = $this->createF3SubstituteInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testR1CorrectiveInvoiceError(): void
|
||||
{
|
||||
$registro = $this->createR1CorrectiveInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testR2CorrectiveInvoiceBankruptcy(): void
|
||||
{
|
||||
$registro = $this->createR2CorrectiveInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testR3CorrectiveInvoiceBadDebt(): void
|
||||
{
|
||||
$registro = $this->createR3CorrectiveInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testR4CorrectiveInvoiceGeneral(): void
|
||||
{
|
||||
$registro = $this->createR4CorrectiveInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
public function testR5CorrectiveInvoiceSimplified(): void
|
||||
{
|
||||
$registro = $this->createR5CorrectiveInvoice();
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
|
||||
$this->assertNotNull($response);
|
||||
}
|
||||
|
||||
private function createF1StandardInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
// Set invoice ID
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('F1-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('F1');
|
||||
$registro->setDescripcionOperacion('F1 Standard Invoice - Complete invoice with full customer details');
|
||||
|
||||
// Set recipients with full identification
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME);
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
// Set breakdown - F1 must include detailed VAT breakdown
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01'); // General regime
|
||||
$detalle->setCalificacionOperacion('S1'); // Not exempt
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(100.0);
|
||||
$detalle->setCuotaRepercutida(21.0);
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(21.0);
|
||||
$registro->setImporteTotal(121.0);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createF2SimplifiedInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('F2-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('F2');
|
||||
$registro->setDescripcionOperacion('F2 Simplified Invoice - No customer identification required');
|
||||
|
||||
// F2 invoices may not identify the recipient or have minimal identification
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon('CONSUMIDOR FINAL');
|
||||
// For F2, we can use IDOtro instead of NIF for non-Spanish customers
|
||||
$idOtro = new IDOtro();
|
||||
$idOtro->setIDType('04'); // Legal Entity ID
|
||||
$idOtro->setID('NO-IDENTIFICADO');
|
||||
$destinatario->setIDOtro($idOtro);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
// Simplified breakdown - amount must be ≤ 3000€
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(50.0); // Keep under 3000€ limit
|
||||
$detalle->setCuotaRepercutida(10.5);
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(10.5);
|
||||
$registro->setImporteTotal(60.5);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createF3SubstituteInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('F3-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('F3');
|
||||
$registro->setDescripcionOperacion('F3 Substitute Invoice - Replaces simplified invoices with full details');
|
||||
|
||||
// F3 requires full customer identification (similar to F1)
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME);
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(200.0);
|
||||
$detalle->setCuotaRepercutida(42.0);
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(42.0);
|
||||
$registro->setImporteTotal(242.0);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createR1CorrectiveInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('R1-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('R1');
|
||||
$registro->setDescripcionOperacion('R1 Corrective Invoice - Error based on law and Art. 80 LIVA');
|
||||
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME);
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
// R1 corrective invoice with rectified amounts
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(-50.0); // Negative amount for correction
|
||||
$detalle->setCuotaRepercutida(-10.5);
|
||||
|
||||
// Add rectification breakdown
|
||||
$desgloseRectificacion = new DesgloseRectificacion();
|
||||
$desgloseRectificacion->setBaseRectificada(100.0); // Original amount being corrected
|
||||
$desgloseRectificacion->setCuotaRectificada(21.0);
|
||||
$detalle->setDesgloseRectificacion($desgloseRectificacion);
|
||||
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(-10.5);
|
||||
$registro->setImporteTotal(-60.5);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createR2CorrectiveInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('R2-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('R2');
|
||||
$registro->setDescripcionOperacion('R2 Corrective Invoice - Customer bankruptcy (Art. 80.Three LIVA)');
|
||||
|
||||
// R2 requires Spanish NIF for customer identification
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME . ' (EN CONCURSO)');
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(150.0); // Base remains, VAT reduced to 0
|
||||
$detalle->setCuotaRepercutida(0.0); // VAT eliminated due to bankruptcy
|
||||
|
||||
$desgloseRectificacion = new DesgloseRectificacion();
|
||||
$desgloseRectificacion->setBaseRectificada(150.0);
|
||||
$desgloseRectificacion->setCuotaRectificada(31.5); // Original VAT being corrected
|
||||
$detalle->setDesgloseRectificacion($desgloseRectificacion);
|
||||
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(0.0);
|
||||
$registro->setImporteTotal(150.0);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createR3CorrectiveInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('R3-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('R3');
|
||||
$registro->setDescripcionOperacion('R3 Corrective Invoice - Bad debt for VAT refund (Art. 80.Four LIVA)');
|
||||
|
||||
// R3 requires Spanish NIF for customer identification
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME . ' (CREDITO INCOBRABLE)');
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(300.0); // Base remains
|
||||
$detalle->setCuotaRepercutida(0.0); // VAT refund due to bad debt
|
||||
|
||||
$desgloseRectificacion = new DesgloseRectificacion();
|
||||
$desgloseRectificacion->setBaseRectificada(300.0);
|
||||
$desgloseRectificacion->setCuotaRectificada(63.0); // Original VAT being refunded
|
||||
$detalle->setDesgloseRectificacion($desgloseRectificacion);
|
||||
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(0.0);
|
||||
$registro->setImporteTotal(300.0);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createR4CorrectiveInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('R4-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('R4');
|
||||
$registro->setDescripcionOperacion('R4 General Corrective Invoice - Other corrections not covered by R1-R3');
|
||||
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon(self::TEST_CLIENT_NAME);
|
||||
$destinatario->setNIF(self::TEST_CLIENT_NIF);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(25.0); // Adjustment amount
|
||||
$detalle->setCuotaRepercutida(5.25);
|
||||
|
||||
$desgloseRectificacion = new DesgloseRectificacion();
|
||||
$desgloseRectificacion->setBaseRectificada(200.0); // Original amount
|
||||
$desgloseRectificacion->setCuotaRectificada(42.0);
|
||||
$detalle->setDesgloseRectificacion($desgloseRectificacion);
|
||||
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(5.25);
|
||||
$registro->setImporteTotal(30.25);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function createR5CorrectiveInvoice(): RegistroAlta
|
||||
{
|
||||
$registro = new RegistroAlta();
|
||||
|
||||
$registro->setIDVersion('1.0');
|
||||
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$idFactura->setNumSerieFactura('R5-TEST-001');
|
||||
$idFactura->setFechaExpedicionFactura(date('d-m-Y'));
|
||||
$registro->setIDFactura($idFactura);
|
||||
|
||||
$registro->setNombreRazonEmisor(self::TEST_COMPANY_NAME);
|
||||
$registro->setTipoFactura('R5');
|
||||
$registro->setDescripcionOperacion('R5 Corrective Invoice - Correction of simplified invoices (F2)');
|
||||
|
||||
// R5 corrects F2 invoices, so minimal customer identification like F2
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon('CONSUMIDOR FINAL (CORRECCION)');
|
||||
$idOtro = new IDOtro();
|
||||
$idOtro->setIDType('04');
|
||||
$idOtro->setID('NO-IDENTIFICADO-CORR');
|
||||
$destinatario->setIDOtro($idOtro);
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
$registro->setDestinatarios($destinatarios);
|
||||
|
||||
$desglose = new Desglose();
|
||||
$detalle = new DetalleDesglose();
|
||||
$detalle->setClaveRegimen('01');
|
||||
$detalle->setCalificacionOperacion('S1');
|
||||
$detalle->setTipoImpositivo(21.0);
|
||||
$detalle->setBaseImponibleOimporteNoSujeto(-10.0); // Correction to F2 invoice
|
||||
$detalle->setCuotaRepercutida(-2.1);
|
||||
|
||||
$desgloseRectificacion = new DesgloseRectificacion();
|
||||
$desgloseRectificacion->setBaseRectificada(50.0); // Original F2 amount
|
||||
$desgloseRectificacion->setCuotaRectificada(10.5);
|
||||
$detalle->setDesgloseRectificacion($desgloseRectificacion);
|
||||
|
||||
$desglose->addDetalleDesglose($detalle);
|
||||
$registro->setDesglose($desglose);
|
||||
|
||||
$registro->setCuotaTotal(-2.1);
|
||||
$registro->setImporteTotal(-12.1);
|
||||
|
||||
$this->setSistemaInformatico($registro);
|
||||
$this->setCommonFields($registro);
|
||||
|
||||
return $registro;
|
||||
}
|
||||
|
||||
private function setSistemaInformatico(RegistroAlta $registro): void
|
||||
{
|
||||
$sistemaInformatico = new SistemaInformatico();
|
||||
$sistemaInformatico->setNombreRazon('INVOICE NINJA TEST SYSTEM');
|
||||
$sistemaInformatico->setNIF(self::TEST_COMPANY_NIF);
|
||||
$sistemaInformatico->setNombreSistemaInformatico('InvoiceNinja Verifactu');
|
||||
$sistemaInformatico->setIdSistemaInformatico('INV-NINJA-001');
|
||||
$sistemaInformatico->setVersion('1.0.0');
|
||||
$sistemaInformatico->setNumeroInstalacion('001');
|
||||
$sistemaInformatico->setTipoUsoPosibleSoloVerifactu('S');
|
||||
$sistemaInformatico->setTipoUsoPosibleMultiOT('N');
|
||||
$sistemaInformatico->setIndicadorMultiplesOT('N');
|
||||
|
||||
$registro->setSistemaInformatico($sistemaInformatico);
|
||||
}
|
||||
|
||||
private function setCommonFields(RegistroAlta $registro): void
|
||||
{
|
||||
$registro->setFechaHoraHusoGenRegistro(date('Y-m-d\TH:i:sP'));
|
||||
$registro->setTipoHuella('01');
|
||||
$registro->setHuella('TEST-HASH-' . uniqid());
|
||||
|
||||
// Add encadenamiento (chaining) for blockchain
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$registroAnterior = new IDFacturaAR();
|
||||
$registroAnterior->setIDEmisorFactura(self::TEST_COMPANY_NIF);
|
||||
$registroAnterior->setNumSerieFactura('PREV-001');
|
||||
$registroAnterior->setFechaExpedicionFactura(date('d-m-Y', strtotime('-1 day')));
|
||||
$registroAnterior->setHuella('PREVIOUS-HASH-' . uniqid());
|
||||
$encadenamiento->setRegistroAnterior($registroAnterior);
|
||||
$registro->setEncadenamiento($encadenamiento);
|
||||
}
|
||||
|
||||
public function testAllInvoiceTypesSerializeSuccessfully(): void
|
||||
{
|
||||
$invoiceTypes = [
|
||||
'F1' => 'createF1StandardInvoice',
|
||||
'F2' => 'createF2SimplifiedInvoice',
|
||||
'F3' => 'createF3SubstituteInvoice',
|
||||
'R1' => 'createR1CorrectiveInvoice',
|
||||
'R2' => 'createR2CorrectiveInvoice',
|
||||
'R3' => 'createR3CorrectiveInvoice',
|
||||
'R4' => 'createR4CorrectiveInvoice',
|
||||
'R5' => 'createR5CorrectiveInvoice'
|
||||
];
|
||||
|
||||
foreach ($invoiceTypes as $type => $method) {
|
||||
$registro = $this->$method();
|
||||
|
||||
// Verify the invoice type is set correctly
|
||||
$this->assertEquals($type, $registro->getTipoFactura());
|
||||
|
||||
// Verify the registro can be serialized without errors
|
||||
try {
|
||||
$response = $this->client->sendRegistroAlta($registro);
|
||||
$this->assertNotNull($response, "Failed to serialize invoice type: $type");
|
||||
} catch (\Exception $e) {
|
||||
$this->fail("Exception occurred while serializing invoice type $type: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testInvoiceTypeValidation(): void
|
||||
{
|
||||
$validTypes = ['F1', 'F2', 'F3', 'R1', 'R2', 'R3', 'R4', 'R5'];
|
||||
|
||||
foreach ($validTypes as $type) {
|
||||
$registro = new RegistroAlta();
|
||||
$registro->setTipoFactura($type);
|
||||
$this->assertEquals($type, $registro->getTipoFactura());
|
||||
}
|
||||
}
|
||||
|
||||
public function testCorrectiveInvoicesIncludeRectificationData(): void
|
||||
{
|
||||
$correctiveTypes = ['R1', 'R2', 'R3', 'R4', 'R5'];
|
||||
|
||||
foreach ($correctiveTypes as $type) {
|
||||
$method = 'create' . $type . 'CorrectiveInvoice';
|
||||
$registro = $this->$method();
|
||||
|
||||
$desglose = $registro->getDesglose();
|
||||
$detalles = $desglose->getDetalleDesglose();
|
||||
|
||||
$this->assertNotEmpty($detalles, "Corrective invoice $type should have breakdown details");
|
||||
|
||||
foreach ($detalles as $detalle) {
|
||||
if ($detalle->getDesgloseRectificacion()) {
|
||||
$rectificacion = $detalle->getDesgloseRectificacion();
|
||||
$this->assertNotNull($rectificacion->getBaseRectificada(),
|
||||
"Corrective invoice $type should have rectified base");
|
||||
$this->assertNotNull($rectificacion->getCuotaRectificada(),
|
||||
"Corrective invoice $type should have rectified quota");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Verifactu;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Invoice;
|
||||
use Tests\MockAccountData;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use Database\Factories\InvoiceFactory;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use App\Services\EDocument\Standards\Verifactu;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
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\RegistroAlta;
|
||||
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
|
||||
{
|
||||
|
||||
use MockAccountData;
|
||||
use DatabaseTransactions;
|
||||
|
||||
public $invoice;
|
||||
public $invoice_calc;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->invoice->line_items = $this->buildLineItems();
|
||||
|
||||
$this->invoice->uses_inclusive_taxes = true;
|
||||
|
||||
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||
}
|
||||
|
||||
public function testDeserialize()
|
||||
{
|
||||
|
||||
$document = file_get_contents(__DIR__ . '/invoice.xml');
|
||||
|
||||
$verifactu = new Verifactu($this->invoice);
|
||||
|
||||
$serializer = $verifactu->getSerializer();
|
||||
|
||||
$parent_class = SoapEnvelope::class;
|
||||
|
||||
$invoice = $serializer->deserialize($document, $parent_class, 'xml', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
|
||||
|
||||
$this->assertInstanceOf(SoapEnvelope::class, $invoice);
|
||||
|
||||
}
|
||||
|
||||
public function testSerializeXml()
|
||||
{
|
||||
|
||||
$document = file_get_contents(__DIR__ . '/invoice.xml');
|
||||
|
||||
$verifactu = new Verifactu($this->invoice);
|
||||
|
||||
$serializer = $verifactu->getSerializer();
|
||||
|
||||
$parent_class = SoapEnvelope::class;
|
||||
|
||||
$invoice = $serializer->deserialize($document, $parent_class, 'xml', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
|
||||
|
||||
$xml = $verifactu->serializeXml($invoice);
|
||||
|
||||
// nlog($xml);
|
||||
$this->assertStringContainsString('soapenv:Envelope', $xml);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Verifactu;
|
||||
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Detalle;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DetalleDesglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\DesgloseRectificacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Encadenamiento;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaAR;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Incidencia;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\ObligadoEmision;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\OperacionExenta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridica;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridicaES;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RechazoPrevio;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFactura;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAnulacion;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\SistemaInformatico;
|
||||
use App\Services\EDocument\Standards\Verifactu\Types\Subsanacion;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class VerifactuTest extends TestCase
|
||||
{
|
||||
public function testSubsanacionValidation()
|
||||
{
|
||||
$subsanacion = new Subsanacion();
|
||||
|
||||
$subsanacion->setNumRegistroAcuerdoFacturacion('12345')
|
||||
->setFechaRegistroAcuerdoFacturacion('2024-03-20');
|
||||
|
||||
$this->assertEquals('12345', $subsanacion->getNumRegistroAcuerdoFacturacion());
|
||||
$this->assertEquals('2024-03-20', $subsanacion->getFechaRegistroAcuerdoFacturacion());
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$subsanacion->setNumRegistroAcuerdoFacturacion(str_repeat('1', 16)); // Exceeds 15 chars
|
||||
}
|
||||
|
||||
public function testRechazoPrevioValidation()
|
||||
{
|
||||
$rechazoPrevio = new RechazoPrevio();
|
||||
|
||||
$rechazoPrevio->setNumRegistroAcuerdoFacturacion('12345')
|
||||
->setFechaRegistroAcuerdoFacturacion('2024-03-20')
|
||||
->setMotivoRechazo('Motivo de prueba');
|
||||
|
||||
$this->assertEquals('12345', $rechazoPrevio->getNumRegistroAcuerdoFacturacion());
|
||||
$this->assertEquals('2024-03-20', $rechazoPrevio->getFechaRegistroAcuerdoFacturacion());
|
||||
$this->assertEquals('Motivo de prueba', $rechazoPrevio->getMotivoRechazo());
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$rechazoPrevio->setMotivoRechazo(str_repeat('a', 2001)); // Exceeds 2000 chars
|
||||
}
|
||||
|
||||
public function testDetalleValidation()
|
||||
{
|
||||
$detalle = new Detalle();
|
||||
|
||||
$detalle->setImpuesto('IVA')
|
||||
->setClaveRegimen('01')
|
||||
->setBaseImponibleOimporteNoSujeto(1000.50)
|
||||
->setTipoImpositivo(21.00);
|
||||
|
||||
$this->assertEquals('IVA', $detalle->getImpuesto());
|
||||
$this->assertEquals('01', $detalle->getClaveRegimen());
|
||||
$this->assertEquals(1000.50, $detalle->getBaseImponibleOimporteNoSujeto());
|
||||
$this->assertEquals(21.00, $detalle->getTipoImpositivo());
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$detalle->setTipoImpositivo(101.00); // Exceeds 100%
|
||||
}
|
||||
|
||||
|
||||
public function testOperacionExentaValidation()
|
||||
{
|
||||
$operacionExenta = new OperacionExenta(OperacionExenta::E1);
|
||||
$this->assertEquals(OperacionExenta::E1, $operacionExenta->getValue());
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
new OperacionExenta('INVALID_VALUE');
|
||||
}
|
||||
|
||||
public function testIDFacturaARValidation()
|
||||
{
|
||||
$idFactura = new IDFacturaAR();
|
||||
|
||||
$idFactura->setIDEmisorFactura('B12345678')
|
||||
->setNumSerieFactura('FACT2024-001')
|
||||
->setFechaExpedicionFactura('20-03-2024');
|
||||
|
||||
$this->assertEquals('B12345678', $idFactura->getIDEmisorFactura());
|
||||
$this->assertEquals('FACT2024-001', $idFactura->getNumSerieFactura());
|
||||
$this->assertEquals('20-03-2024', $idFactura->getFechaExpedicionFactura());
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$idFactura->setFechaExpedicionFactura('2024-03-20'); // Wrong format
|
||||
}
|
||||
|
||||
public function testRegistroFacturaValidation()
|
||||
{
|
||||
$registroFactura = new RegistroFactura();
|
||||
$registroAlta = new RegistroAlta();
|
||||
|
||||
$registroFactura->setRegistroAlta($registroAlta);
|
||||
$this->assertInstanceOf(RegistroAlta::class, $registroFactura->getRegistroAlta());
|
||||
|
||||
$registroAnulacion = new RegistroFacturacionAnulacion();
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$registroFactura->setRegistroAnulacion($registroAnulacion); // Cannot set both
|
||||
}
|
||||
|
||||
public function testObligadoEmisionValidation()
|
||||
{
|
||||
$obligado = new ObligadoEmision();
|
||||
$obligado->setNIF('B12345678');
|
||||
$obligado->setNombreRazon('Empresa Test');
|
||||
|
||||
$this->assertEquals('B12345678', $obligado->getNIF());
|
||||
$this->assertEquals('Empresa Test', $obligado->getNombreRazon());
|
||||
}
|
||||
|
||||
public function testObligadoEmisionEmptyNombreRazon()
|
||||
{
|
||||
$obligado = new ObligadoEmision();
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$obligado->setNombreRazon('');
|
||||
}
|
||||
|
||||
public function testObligadoEmisionEmptyNIF()
|
||||
{
|
||||
$obligado = new ObligadoEmision();
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$obligado->setNIF('');
|
||||
}
|
||||
|
||||
public function testObligadoEmisionInvalidNIF()
|
||||
{
|
||||
$obligado = new ObligadoEmision();
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$obligado->setNIF('invalid');
|
||||
}
|
||||
|
||||
public function testCreateCompleteRegistroFactura()
|
||||
{
|
||||
// Create ObligadoEmision
|
||||
$obligadoEmision = new ObligadoEmision();
|
||||
$obligadoEmision->setNombreRazon('XXXXX')
|
||||
->setNIF('A12345678');
|
||||
|
||||
// Create IDFactura
|
||||
$idFactura = new IDFactura();
|
||||
$idFactura->setIDEmisorFactura('B12345678')
|
||||
->setNumSerieFactura('12345')
|
||||
->setFechaExpedicionFactura('13-09-2024');
|
||||
|
||||
// Create Destinatario
|
||||
$destinatario = new IDDestinatario();
|
||||
$destinatario->setNombreRazon('YYYY')
|
||||
->setNIF('C12345678');
|
||||
|
||||
// Create Destinatarios collection
|
||||
$destinatarios = new Destinatarios();
|
||||
$destinatarios->addIDDestinatario($destinatario);
|
||||
|
||||
// Create Detalles for Desglose
|
||||
$detalle1 = new DetalleDesglose();
|
||||
$detalle1->setClaveRegimen('01')
|
||||
->setCalificacionOperacion('S1')
|
||||
->setTipoImpositivo(4.0)
|
||||
->setBaseImponibleOimporteNoSujeto(10.0)
|
||||
->setCuotaRepercutida(0.4);
|
||||
|
||||
$detalle2 = new DetalleDesglose();
|
||||
$detalle2->setClaveRegimen('01')
|
||||
->setCalificacionOperacion('S1')
|
||||
->setTipoImpositivo(21.0)
|
||||
->setBaseImponibleOimporteNoSujeto(100.0)
|
||||
->setCuotaRepercutida(21.0);
|
||||
|
||||
// Create Desglose
|
||||
$desglose = new Desglose();
|
||||
$desglose->addDetalleDesglose($detalle1);
|
||||
$desglose->addDetalleDesglose($detalle2);
|
||||
|
||||
// Create RegistroAnterior for Encadenamiento
|
||||
$registroAnterior = new IDFacturaAR();
|
||||
$registroAnterior->setIDEmisorFactura('E12345678')
|
||||
->setNumSerieFactura('44')
|
||||
->setFechaExpedicionFactura('13-09-2024');
|
||||
|
||||
// Create Encadenamiento
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setRegistroAnterior($registroAnterior)
|
||||
->setHuellaRegistroAnterior('HuellaRegistroAnterior');
|
||||
|
||||
// Create SistemaInformatico
|
||||
$sistemaInformatico = new SistemaInformatico();
|
||||
$sistemaInformatico->setNombreRazon('SSSS')
|
||||
->setNIF('D12345678')
|
||||
->setNombreSistemaInformatico('NombreSistemaInformatico')
|
||||
->setIdSistemaInformatico('77')
|
||||
->setVersion('1.0.03')
|
||||
->setNumeroInstalacion('383')
|
||||
->setTipoUsoPosibleSoloVerifactu('N')
|
||||
->setTipoUsoPosibleMultiOT('S')
|
||||
->setIndicadorMultiplesOT('S');
|
||||
|
||||
// Create RegistroAlta
|
||||
$registroAlta = new RegistroAlta();
|
||||
$registroAlta->setIDVersion('1.0')
|
||||
->setIDFactura($idFactura)
|
||||
->setNombreRazonEmisor('XXXXX')
|
||||
->setTipoFactura('F1')
|
||||
->setDescripcionOperacion('Descripc')
|
||||
->setDestinatarios($destinatarios)
|
||||
->setDesglose($desglose)
|
||||
->setCuotaTotal(21.4)
|
||||
->setImporteTotal(131.4)
|
||||
->setEncadenamiento($encadenamiento)
|
||||
->setSistemaInformatico($sistemaInformatico)
|
||||
->setFechaHoraHusoGenRegistro('2024-09-13T19:20:30+01:00')
|
||||
->setTipoHuella('01')
|
||||
->setHuella('Huella');
|
||||
|
||||
// Create RegistroFactura
|
||||
$registroFactura = new RegistroFactura();
|
||||
$registroFactura->setRegistroAlta($registroAlta);
|
||||
|
||||
// Assertions
|
||||
$this->assertEquals('1.0', $registroAlta->getIDVersion());
|
||||
$this->assertEquals('XXXXX', $registroAlta->getNombreRazonEmisor());
|
||||
$this->assertEquals('F1', $registroAlta->getTipoFactura());
|
||||
$this->assertEquals('Descripc', $registroAlta->getDescripcionOperacion());
|
||||
$this->assertEquals(21.4, $registroAlta->getCuotaTotal());
|
||||
$this->assertEquals(131.4, $registroAlta->getImporteTotal());
|
||||
$this->assertEquals('01', $registroAlta->getTipoHuella());
|
||||
$this->assertEquals('Huella', $registroAlta->getHuella());
|
||||
|
||||
// Test nested objects
|
||||
$this->assertEquals('B12345678', $registroAlta->getIDFactura()->getIDEmisorFactura());
|
||||
$this->assertEquals('12345', $registroAlta->getIDFactura()->getNumSerieFactura());
|
||||
$this->assertEquals('13-09-2024', $registroAlta->getIDFactura()->getFechaExpedicionFactura());
|
||||
|
||||
// Test Destinatarios
|
||||
$destinatarios = $registroAlta->getDestinatarios();
|
||||
$this->assertEquals('YYYY', $destinatarios->getIDDestinatario()[0]->getNombreRazon());
|
||||
$this->assertEquals('C12345678', $destinatarios->getIDDestinatario()[0]->getNIF());
|
||||
|
||||
// Test Desglose
|
||||
$detalles = $registroAlta->getDesglose()->getDetalleDesglose();
|
||||
$this->assertEquals(4.0, $detalles[0]->getTipoImpositivo());
|
||||
$this->assertEquals(10.0, $detalles[0]->getBaseImponibleOimporteNoSujeto());
|
||||
$this->assertEquals(0.4, $detalles[0]->getCuotaRepercutida());
|
||||
$this->assertEquals(21.0, $detalles[1]->getTipoImpositivo());
|
||||
$this->assertEquals(100.0, $detalles[1]->getBaseImponibleOimporteNoSujeto());
|
||||
$this->assertEquals(21.0, $detalles[1]->getCuotaRepercutida());
|
||||
|
||||
// Test SistemaInformatico
|
||||
$this->assertEquals('SSSS', $registroAlta->getSistemaInformatico()->getNombreRazon());
|
||||
$this->assertEquals('D12345678', $registroAlta->getSistemaInformatico()->getNIF());
|
||||
$this->assertEquals('77', $registroAlta->getSistemaInformatico()->getIdSistemaInformatico());
|
||||
}
|
||||
}
|
||||
|
|
@ -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>B12345678</sum1:NombreRazon>
|
||||
<sum1:NIF>B12345678</sum1:NIF>
|
||||
</sum1:ObligadoEmision>
|
||||
</sum:Cabecera>
|
||||
<sum:RegistroFactura>
|
||||
<sum1:RegistroAlta>
|
||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
||||
<sum1:IDFactura>
|
||||
<sum1:IDEmisorFactura>C12345678</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>D12345678</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>D12345678</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>E12345678</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>
|
||||
Loading…
Reference in New Issue