diff --git a/app/Services/EDocument/Standards/Verifactu/Types/Cabecera.php b/app/Services/EDocument/Standards/Verifactu/Types/Cabecera.php new file mode 100644 index 0000000000..b92648663f --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/Cabecera.php @@ -0,0 +1,74 @@ +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, incidencia?: IncidenciaType}|null + */ + public function getRemisionVoluntaria(): ?array + { + return $this->remisionVoluntaria; + } + + /** + * @param array{fechaFinVeriFactu?: string, incidencia?: IncidenciaType}|null $remisionVoluntaria + */ + public function setRemisionVoluntaria(?array $remisionVoluntaria): self + { + $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 + { + $this->remisionRequerimiento = $remisionRequerimiento; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/Common/TextTypes.php b/app/Services/EDocument/Standards/Verifactu/Types/Common/TextTypes.php new file mode 100644 index 0000000000..a62b9e3aa0 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/Common/TextTypes.php @@ -0,0 +1,64 @@ + $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"); + } + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/Desglose.php b/app/Services/EDocument/Standards/Verifactu/Types/Desglose.php new file mode 100644 index 0000000000..fbd263b5bc --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/Desglose.php @@ -0,0 +1,38 @@ +detalle; + } + + public function addDetalle(Detalle $detalle): self + { + if (count($this->detalle) >= 1000) { + throw new \RuntimeException('Maximum number of Detalle (1000) exceeded'); + } + $this->detalle[] = $detalle; + return $this; + } + + /** + * @param Detalle[] $detalle + */ + public function setDetalle(array $detalle): self + { + if (count($detalle) > 1000) { + throw new \RuntimeException('Maximum number of Detalle (1000) exceeded'); + } + $this->detalle = $detalle; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/DesgloseRectificacion.php b/app/Services/EDocument/Standards/Verifactu/Types/DesgloseRectificacion.php new file mode 100644 index 0000000000..f7d47f6206 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/DesgloseRectificacion.php @@ -0,0 +1,74 @@ +baseRectificada; + } + + public function setBaseRectificada(float $baseRectificada): self + { + // Validate format: max 12 digits before decimal point, 2 after + $strValue = (string)$baseRectificada; + if (strlen(substr(strrchr($strValue, "."), 1)) > 2) { + throw new \InvalidArgumentException('BaseRectificada must have at most 2 decimal places'); + } + if (strlen(explode('.', $strValue)[0]) > 12) { + throw new \InvalidArgumentException('BaseRectificada must have at most 12 digits before decimal point'); + } + $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 + $strValue = (string)$cuotaRectificada; + if (strlen(substr(strrchr($strValue, "."), 1)) > 2) { + throw new \InvalidArgumentException('CuotaRectificada must have at most 2 decimal places'); + } + if (strlen(explode('.', $strValue)[0]) > 12) { + throw new \InvalidArgumentException('CuotaRectificada must have at most 12 digits before decimal point'); + } + $this->cuotaRectificada = $cuotaRectificada; + return $this; + } + + public function getCuotaRecargoRectificada(): ?float + { + return $this->cuotaRecargoRectificada; + } + + public function setCuotaRecargoRectificada(?float $cuotaRecargoRectificada): self + { + if ($cuotaRecargoRectificada !== null) { + // Validate format: max 12 digits before decimal point, 2 after + $strValue = (string)$cuotaRecargoRectificada; + if (strlen(substr(strrchr($strValue, "."), 1)) > 2) { + throw new \InvalidArgumentException('CuotaRecargoRectificada must have at most 2 decimal places'); + } + if (strlen(explode('.', $strValue)[0]) > 12) { + throw new \InvalidArgumentException('CuotaRecargoRectificada must have at most 12 digits before decimal point'); + } + } + $this->cuotaRecargoRectificada = $cuotaRecargoRectificada; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/Detalle.php b/app/Services/EDocument/Standards/Verifactu/Types/Detalle.php new file mode 100644 index 0000000000..8238324d51 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/Detalle.php @@ -0,0 +1,198 @@ +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 + { + $this->claveRegimen = $claveRegimen; + return $this; + } + + public function getCalificacionOperacion(): ?string + { + return $this->calificacionOperacion; + } + + public function setCalificacionOperacion(?string $calificacionOperacion): self + { + if ($calificacionOperacion !== null && $this->operacionExenta !== null) { + throw new \InvalidArgumentException('Cannot set both CalificacionOperacion and OperacionExenta'); + } + $this->calificacionOperacion = $calificacionOperacion; + return $this; + } + + public function getOperacionExenta(): ?string + { + return $this->operacionExenta; + } + + public function setOperacionExenta(?string $operacionExenta): self + { + if ($operacionExenta !== null && $this->calificacionOperacion !== null) { + throw new \InvalidArgumentException('Cannot set both CalificacionOperacion and OperacionExenta'); + } + $this->operacionExenta = $operacionExenta; + return $this; + } + + public function getTipoImpositivo(): ?float + { + return $this->tipoImpositivo; + } + + public function setTipoImpositivo(?float $tipoImpositivo): self + { + if ($tipoImpositivo !== null) { + // Validate format: max 2 decimal places + if (strlen(substr(strrchr((string)$tipoImpositivo, "."), 1)) > 2) { + throw new \InvalidArgumentException('TipoImpositivo must have at most 2 decimal places'); + } + } + $this->tipoImpositivo = $tipoImpositivo; + return $this; + } + + public function getBaseImponibleOimporteNoSujeto(): float + { + return $this->baseImponibleOimporteNoSujeto; + } + + public function setBaseImponibleOimporteNoSujeto(float $baseImponibleOimporteNoSujeto): self + { + // Validate format: max 12 digits before decimal point, 2 after + if (strlen(substr(strrchr((string)$baseImponibleOimporteNoSujeto, "."), 1)) > 2) { + throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 2 decimal places'); + } + if (strlen(explode('.', (string)$baseImponibleOimporteNoSujeto)[0]) > 12) { + throw new \InvalidArgumentException('BaseImponibleOimporteNoSujeto must have at most 12 digits before decimal point'); + } + $this->baseImponibleOimporteNoSujeto = $baseImponibleOimporteNoSujeto; + return $this; + } + + public function getBaseImponibleACoste(): ?float + { + return $this->baseImponibleACoste; + } + + public function setBaseImponibleACoste(?float $baseImponibleACoste): self + { + if ($baseImponibleACoste !== null) { + // Validate format: max 12 digits before decimal point, 2 after + if (strlen(substr(strrchr((string)$baseImponibleACoste, "."), 1)) > 2) { + throw new \InvalidArgumentException('BaseImponibleACoste must have at most 2 decimal places'); + } + if (strlen(explode('.', (string)$baseImponibleACoste)[0]) > 12) { + throw new \InvalidArgumentException('BaseImponibleACoste must have at most 12 digits before decimal point'); + } + } + $this->baseImponibleACoste = $baseImponibleACoste; + return $this; + } + + public function getCuotaRepercutida(): ?float + { + return $this->cuotaRepercutida; + } + + public function setCuotaRepercutida(?float $cuotaRepercutida): self + { + if ($cuotaRepercutida !== null) { + // Validate format: max 12 digits before decimal point, 2 after + if (strlen(substr(strrchr((string)$cuotaRepercutida, "."), 1)) > 2) { + throw new \InvalidArgumentException('CuotaRepercutida must have at most 2 decimal places'); + } + if (strlen(explode('.', (string)$cuotaRepercutida)[0]) > 12) { + throw new \InvalidArgumentException('CuotaRepercutida must have at most 12 digits before decimal point'); + } + } + $this->cuotaRepercutida = $cuotaRepercutida; + return $this; + } + + public function getTipoRecargoEquivalencia(): ?float + { + return $this->tipoRecargoEquivalencia; + } + + public function setTipoRecargoEquivalencia(?float $tipoRecargoEquivalencia): self + { + if ($tipoRecargoEquivalencia !== null) { + // Validate format: max 2 decimal places + if (strlen(substr(strrchr((string)$tipoRecargoEquivalencia, "."), 1)) > 2) { + throw new \InvalidArgumentException('TipoRecargoEquivalencia must have at most 2 decimal places'); + } + } + $this->tipoRecargoEquivalencia = $tipoRecargoEquivalencia; + return $this; + } + + public function getCuotaRecargoEquivalencia(): ?float + { + return $this->cuotaRecargoEquivalencia; + } + + public function setCuotaRecargoEquivalencia(?float $cuotaRecargoEquivalencia): self + { + if ($cuotaRecargoEquivalencia !== null) { + // Validate format: max 12 digits before decimal point, 2 after + if (strlen(substr(strrchr((string)$cuotaRecargoEquivalencia, "."), 1)) > 2) { + throw new \InvalidArgumentException('CuotaRecargoEquivalencia must have at most 2 decimal places'); + } + if (strlen(explode('.', (string)$cuotaRecargoEquivalencia)[0]) > 12) { + throw new \InvalidArgumentException('CuotaRecargoEquivalencia must have at most 12 digits before decimal point'); + } + } + $this->cuotaRecargoEquivalencia = $cuotaRecargoEquivalencia; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/IDDestinatario.php b/app/Services/EDocument/Standards/Verifactu/Types/IDDestinatario.php new file mode 100644 index 0000000000..caf680ec82 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/IDDestinatario.php @@ -0,0 +1,37 @@ +codigoPais; + } + + public function setCodigoPais(?string $codigoPais): self + { + if ($codigoPais !== null && strlen($codigoPais) !== 2) { + throw new \InvalidArgumentException('CodigoPais must be a 2-character ISO country code'); + } + $this->codigoPais = $codigoPais; + return $this; + } + + public function getIdOtro(): ?IDOtro + { + return $this->idOtro; + } + + public function setIdOtro(?IDOtro $idOtro): self + { + $this->idOtro = $idOtro; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/IDFactura.php b/app/Services/EDocument/Standards/Verifactu/Types/IDFactura.php new file mode 100644 index 0000000000..ced541ea85 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/IDFactura.php @@ -0,0 +1,63 @@ +idEmisorFactura; + } + + public function setIdEmisorFactura(string $idEmisorFactura): self + { + // TODO: Add NIF validation + $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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaAR.php b/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaAR.php new file mode 100644 index 0000000000..ea812effa9 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaAR.php @@ -0,0 +1,66 @@ +idEmisorFactura; + } + + public function setIdEmisorFactura(string $idEmisorFactura): self + { + // TODO: Add NIF validation + $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 + { + if (!\DateTime::createFromFormat('Y-m-d', $fechaExpedicionFactura)) { + throw new \InvalidArgumentException('FechaExpedicionFactura must be in YYYY-MM-DD format'); + } + $this->fechaExpedicionFactura = $fechaExpedicionFactura; + return $this; + } + + public function getNumRegistroAcuerdoFacturacion(): ?string + { + return $this->numRegistroAcuerdoFacturacion; + } + + public function setNumRegistroAcuerdoFacturacion(?string $numRegistroAcuerdoFacturacion): self + { + $this->numRegistroAcuerdoFacturacion = $numRegistroAcuerdoFacturacion; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaExpedida.php b/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaExpedida.php new file mode 100644 index 0000000000..edd990747d --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/IDFacturaExpedida.php @@ -0,0 +1,53 @@ +idEmisorFactura; + } + + public function setIdEmisorFactura(string $idEmisorFactura): self + { + // TODO: Add NIF validation + $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 + { + // Validate date format + if (!\DateTime::createFromFormat('Y-m-d', $fechaExpedicionFactura)) { + throw new \InvalidArgumentException('FechaExpedicionFactura must be in YYYY-MM-DD format'); + } + $this->fechaExpedicionFactura = $fechaExpedicionFactura; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/IDOtro.php b/app/Services/EDocument/Standards/Verifactu/Types/IDOtro.php new file mode 100644 index 0000000000..39c0283abe --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/IDOtro.php @@ -0,0 +1,64 @@ +codigoPais; + } + + public function setCodigoPais(?string $codigoPais): self + { + if ($codigoPais !== null) { + if (strlen($codigoPais) !== 2) { + throw new \InvalidArgumentException('CodigoPais must be a 2-letter ISO country code'); + } + // Prevent using ES with IDType 01 + if ($codigoPais === 'ES' && $this->idType === '01') { + throw new \InvalidArgumentException('Cannot use CodigoPais=ES with IDType=01, use NIF instead'); + } + } + $this->codigoPais = $codigoPais; + return $this; + } + + public function getIdType(): string + { + return $this->idType; + } + + public function setIdType(string $idType): self + { + // Prevent using ES with IDType 01 + if ($this->codigoPais === 'ES' && $idType === '01') { + throw new \InvalidArgumentException('Cannot use CodigoPais=ES with IDType=01, use NIF instead'); + } + $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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/ImporteSgn14_2.php b/app/Services/EDocument/Standards/Verifactu/Types/ImporteSgn14_2.php new file mode 100644 index 0000000000..040e006174 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/ImporteSgn14_2.php @@ -0,0 +1,35 @@ +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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/Incidencia.php b/app/Services/EDocument/Standards/Verifactu/Types/Incidencia.php new file mode 100644 index 0000000000..f8830e0461 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/Incidencia.php @@ -0,0 +1,74 @@ +descripcion; + } + + public function setDescripcion(string $descripcion): self + { + if (strlen($descripcion) > 2000) { + throw new \InvalidArgumentException('Descripcion must not exceed 2000 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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/ObligadoEmision.php b/app/Services/EDocument/Standards/Verifactu/Types/ObligadoEmision.php new file mode 100644 index 0000000000..2d7b980812 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/ObligadoEmision.php @@ -0,0 +1,198 @@ +tipoPersona; + } + + public function setTipoPersona(?string $tipoPersona): self + { + if ($tipoPersona !== null && !in_array($tipoPersona, ['F', 'J'])) { + throw new \InvalidArgumentException('TipoPersona must be either "F" (Física) or "J" (Jurídica)'); + } + $this->tipoPersona = $tipoPersona; + return $this; + } + + public function getRazonSocialCompleta(): ?string + { + return $this->razonSocialCompleta; + } + + public function setRazonSocialCompleta(?string $razonSocialCompleta): self + { + if ($razonSocialCompleta !== null && strlen($razonSocialCompleta) > 120) { + throw new \InvalidArgumentException('RazonSocialCompleta must not exceed 120 characters'); + } + $this->razonSocialCompleta = $razonSocialCompleta; + return $this; + } + + public function getNombreComercial(): ?string + { + return $this->nombreComercial; + } + + public function setNombreComercial(?string $nombreComercial): self + { + if ($nombreComercial !== null && strlen($nombreComercial) > 120) { + throw new \InvalidArgumentException('NombreComercial must not exceed 120 characters'); + } + $this->nombreComercial = $nombreComercial; + return $this; + } + + public function getCodigoPostal(): ?string + { + return $this->codigoPostal; + } + + public function setCodigoPostal(?string $codigoPostal): self + { + if ($codigoPostal !== null && strlen($codigoPostal) > 10) { + throw new \InvalidArgumentException('CodigoPostal must not exceed 10 characters'); + } + $this->codigoPostal = $codigoPostal; + return $this; + } + + public function getDireccion(): ?string + { + return $this->direccion; + } + + public function setDireccion(?string $direccion): self + { + if ($direccion !== null && strlen($direccion) > 250) { + throw new \InvalidArgumentException('Direccion must not exceed 250 characters'); + } + $this->direccion = $direccion; + return $this; + } + + public function getPoblacion(): ?string + { + return $this->poblacion; + } + + public function setPoblacion(?string $poblacion): self + { + if ($poblacion !== null && strlen($poblacion) > 50) { + throw new \InvalidArgumentException('Poblacion must not exceed 50 characters'); + } + $this->poblacion = $poblacion; + return $this; + } + + public function getProvincia(): ?string + { + return $this->provincia; + } + + public function setProvincia(?string $provincia): self + { + if ($provincia !== null && strlen($provincia) > 20) { + throw new \InvalidArgumentException('Provincia must not exceed 20 characters'); + } + $this->provincia = $provincia; + return $this; + } + + public function getPais(): ?string + { + return $this->pais; + } + + public function setPais(?string $pais): self + { + if ($pais !== null && strlen($pais) > 20) { + throw new \InvalidArgumentException('Pais must not exceed 20 characters'); + } + $this->pais = $pais; + return $this; + } + + public function getTelefono(): ?string + { + return $this->telefono; + } + + public function setTelefono(?string $telefono): self + { + if ($telefono !== null && strlen($telefono) > 20) { + throw new \InvalidArgumentException('Telefono must not exceed 20 characters'); + } + $this->telefono = $telefono; + return $this; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(?string $email): self + { + if ($email !== null) { + if (strlen($email) > 120) { + throw new \InvalidArgumentException('Email must not exceed 120 characters'); + } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new \InvalidArgumentException('Invalid email format'); + } + } + $this->email = $email; + return $this; + } + + public function getWeb(): ?string + { + return $this->web; + } + + public function setWeb(?string $web): self + { + if ($web !== null && strlen($web) > 250) { + throw new \InvalidArgumentException('Web must not exceed 250 characters'); + } + $this->web = $web; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/OperacionExenta.php b/app/Services/EDocument/Standards/Verifactu/Types/OperacionExenta.php new file mode 100644 index 0000000000..81b8b1e823 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/OperacionExenta.php @@ -0,0 +1,53 @@ +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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridica.php b/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridica.php new file mode 100644 index 0000000000..5b24741495 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridica.php @@ -0,0 +1,58 @@ +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 + if ($nif !== null && $this->idOtro !== null) { + throw new \InvalidArgumentException('Cannot set both NIF and IDOtro'); + } + $this->nif = $nif; + return $this; + } + + public function getIdOtro(): ?IDOtro + { + return $this->idOtro; + } + + public function setIdOtro(?IDOtro $idOtro): self + { + if ($idOtro !== null && $this->nif !== null) { + throw new \InvalidArgumentException('Cannot set both NIF and IDOtro'); + } + $this->idOtro = $idOtro; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridicaES.php b/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridicaES.php new file mode 100644 index 0000000000..be371636eb --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/PersonaFisicaJuridicaES.php @@ -0,0 +1,38 @@ +nif; + } + + public function setNif(string $nif): self + { + // TODO: Add NIF validation + $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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/RegFactuSistemaFacturacion.php b/app/Services/EDocument/Standards/Verifactu/Types/RegFactuSistemaFacturacion.php new file mode 100644 index 0000000000..0e471c4bd2 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/RegFactuSistemaFacturacion.php @@ -0,0 +1,34 @@ +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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/RegistroAlta.php b/app/Services/EDocument/Standards/Verifactu/Types/RegistroAlta.php new file mode 100644 index 0000000000..701a247bb6 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/RegistroAlta.php @@ -0,0 +1,227 @@ + */ + protected $destinatarios = []; + + /** @var array */ + protected $desglose = []; + + /** @var float */ + protected $cuotaTotal; + + /** @var float */ + protected $importeTotal; + + /** @var RegistroAnterior|null */ + protected $encadenamiento; + + /** @var SistemaInformatico */ + protected $sistemaInformatico; + + /** @var string */ + protected $fechaHoraHusoGenRegistro; + + /** @var string */ + protected $tipoHuella; + + /** @var string */ + 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 (!in_array($tipoFactura, ['F1', 'F2', 'F3', 'F4', 'R1', 'R2', 'R3', 'R4'])) { + throw new \InvalidArgumentException('Invalid TipoFactura value'); + } + $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; + } + + /** + * @return array + */ + public function getDestinatarios(): array + { + return $this->destinatarios; + } + + public function addDestinatario(IDDestinatario $destinatario): self + { + $this->destinatarios[] = $destinatario; + return $this; + } + + /** + * @return array + */ + public function getDesglose(): array + { + return $this->desglose; + } + + public function addDesglose(DetalleDesglose $detalle): self + { + $this->desglose[] = $detalle; + 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(): ?RegistroAnterior + { + return $this->encadenamiento; + } + + public function setEncadenamiento(?RegistroAnterior $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 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 getTipoHuella(): string + { + return $this->tipoHuella; + } + + public function setTipoHuella(string $tipoHuella): self + { + if (!in_array($tipoHuella, ['01', '02', '03', '04'])) { + throw new \InvalidArgumentException('Invalid TipoHuella value'); + } + $this->tipoHuella = $tipoHuella; + 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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/RegistroFactura.php b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFactura.php new file mode 100644 index 0000000000..527fce59a7 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFactura.php @@ -0,0 +1,40 @@ +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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAlta.php b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAlta.php new file mode 100644 index 0000000000..19b9974064 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAlta.php @@ -0,0 +1,184 @@ +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; + } + + // Add remaining getters and setters with appropriate validation... + + /** + * @return PersonaFisicaJuridica[] + */ + public function getDestinatarios(): array + { + return $this->destinatarios; + } + + public function addDestinatario(PersonaFisicaJuridica $destinatario): self + { + if (count($this->destinatarios) >= 1000) { + throw new \RuntimeException('Maximum number of Destinatarios (1000) exceeded'); + } + $this->destinatarios[] = $destinatario; + 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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAnulacion.php b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAnulacion.php new file mode 100644 index 0000000000..8d6e4ede98 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/RegistroFacturacionAnulacion.php @@ -0,0 +1,192 @@ +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 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 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 + { + $this->tipoHuella = $tipoHuella; + 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; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/Types/SistemaInformatico.php b/app/Services/EDocument/Standards/Verifactu/Types/SistemaInformatico.php new file mode 100644 index 0000000000..b1cd8b73d3 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/Types/SistemaInformatico.php @@ -0,0 +1,171 @@ +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 + { + // TODO: Add NIF validation + $this->nif = $nif; + return $this; + } + + public function getIdOtro(): ?array + { + return $this->idOtro; + } + + public function setIdOtro(?array $idOtro): self + { + $this->idOtro = $idOtro; + return $this; + } + + public function getNombreSistemaInformatico(): string + { + return $this->nombreSistemaInformatico; + } + + public function setNombreSistemaInformatico(string $nombreSistemaInformatico): self + { + if (strlen($nombreSistemaInformatico) > 30) { + throw new \InvalidArgumentException('NombreSistemaInformatico must not exceed 30 characters'); + } + $this->nombreSistemaInformatico = $nombreSistemaInformatico; + return $this; + } + + public function getIdSistemaInformatico(): string + { + return $this->idSistemaInformatico; + } + + public function setIdSistemaInformatico(string $idSistemaInformatico): self + { + if (strlen($idSistemaInformatico) > 2) { + throw new \InvalidArgumentException('IdSistemaInformatico must not exceed 2 characters'); + } + $this->idSistemaInformatico = $idSistemaInformatico; + return $this; + } + + public function getVersion(): string + { + return $this->version; + } + + public function setVersion(string $version): self + { + if (strlen($version) > 50) { + throw new \InvalidArgumentException('Version must not exceed 50 characters'); + } + $this->version = $version; + return $this; + } + + public function getNumeroInstalacion(): string + { + return $this->numeroInstalacion; + } + + public function setNumeroInstalacion(string $numeroInstalacion): self + { + if (strlen($numeroInstalacion) > 100) { + throw new \InvalidArgumentException('NumeroInstalacion must not exceed 100 characters'); + } + $this->numeroInstalacion = $numeroInstalacion; + return $this; + } + + public function getTipoUsoPosibleSoloVerifactu(): string + { + return $this->tipoUsoPosibleSoloVerifactu; + } + + public function setTipoUsoPosibleSoloVerifactu(string $value): self + { + if (!in_array($value, ['S', 'N'])) { + throw new \InvalidArgumentException('TipoUsoPosibleSoloVerifactu must be either "S" or "N"'); + } + $this->tipoUsoPosibleSoloVerifactu = $value; + return $this; + } + + public function getTipoUsoPosibleMultiOT(): string + { + return $this->tipoUsoPosibleMultiOT; + } + + public function setTipoUsoPosibleMultiOT(string $value): self + { + if (!in_array($value, ['S', 'N'])) { + throw new \InvalidArgumentException('TipoUsoPosibleMultiOT must be either "S" or "N"'); + } + $this->tipoUsoPosibleMultiOT = $value; + return $this; + } + + public function getIndicadorMultiplesOT(): string + { + return $this->indicadorMultiplesOT; + } + + public function setIndicadorMultiplesOT(string $value): self + { + if (!in_array($value, ['S', 'N'])) { + throw new \InvalidArgumentException('IndicadorMultiplesOT must be either "S" or "N"'); + } + $this->indicadorMultiplesOT = $value; + return $this; + } +} \ No newline at end of file diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/ConsultaLR.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/ConsultaLR.xsd new file mode 100644 index 0000000000..0e6327fb98 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/ConsultaLR.xsd @@ -0,0 +1,38 @@ + + + + + + + + + Servicio de consulta Registros Facturacion + + + + + + + + + + + + + + Nº Serie+Nº Factura de la Factura del Emisor. + + + + + 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. + + + + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/EventosSIF.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/EventosSIF.xsd new file mode 100644 index 0000000000..21cb370535 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/EventosSIF.xsd @@ -0,0 +1,823 @@ + + + + + + + + + + + + + + + + + + + Obligado a expedir la factura. + + + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + + + + + + + + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + + + + + + + + + + + + + + + + + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + + + + + + + Formato: YYYY-MM-DDThh:mm:ssTZD (ej: 2024-01-01T19:20:30+01:00) (ISO 8601) + + + + + + + + + + + + + + + Datos de una persona física o jurídica Española con un NIF asociado + + + + + + + + + NIF + + + + + + + + + Datos de una persona física o jurídica Española o Extranjera + + + + + + + + + + + + + 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. + + + + + + + + + + + + + + Destinatario + + + + + Tercero + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NIF-IVA + + + + + Pasaporte + + + + + IDEnPaisResidencia + + + + + Certificado Residencia + + + + + Otro documento Probatorio + + + + + No Censado + + + + + + + + + + SHA-256 + + + + + + + + + Inicio del funcionamiento del sistema informático como «NO VERI*FACTU». + + + + + Fin del funcionamiento del sistema informático como «NO VERI*FACTU». + + + + + Lanzamiento del proceso de detección de anomalías en los registros de facturación. + + + + + Detección de anomalías en la integridad, inalterabilidad y trazabilidad de registros de facturación. + + + + + Lanzamiento del proceso de detección de anomalías en los registros de evento. + + + + + Detección de anomalías en la integridad, inalterabilidad y trazabilidad de registros de evento. + + + + + Restauración de copia de seguridad, cuando ésta se gestione desde el propio sistema informático de facturación. + + + + + Exportación de registros de facturación generados en un periodo. + + + + + Exportación de registros de evento generados en un periodo. + + + + + Registro resumen de eventos + + + + + Otros tipos de eventos a registrar voluntariamente por la persona o entidad productora del sistema informático. + + + + + + + + + + Integridad-huella + + + + + Integridad-firma + + + + + Integridad - Otros + + + + + Trazabilidad-cadena-registro - Reg. no primero pero con reg. anterior no anotado o inexistente + + + + + Trazabilidad-cadena-registro - Reg. no último pero con reg. posterior no anotado o inexistente + + + + + Trazabilidad-cadena-registro - Otros + + + + + Trazabilidad-cadena-huella - Huella del reg. no se corresponde con la 'huella del reg. anterior' almacenada en el registro posterior + + + + + Trazabilidad-cadena-huella - Campo 'huella del reg. anterior' no se corresponde con la huella del reg. anterior + + + + + Trazabilidad-cadena-huella - Otros + + + + + Trazabilidad-cadena - Otros + + + + + Trazabilidad-fechas - Fecha-hora anterior a la fecha del reg. anterior + + + + + Trazabilidad-fechas - Fecha-hora posterior a la fecha del reg. posterior + + + + + Trazabilidad-fechas - Reg. con fecha-hora de generación posterior a la fecha-hora actual del sistema + + + + + Trazabilidad-fechas - Otros + + + + + Trazabilidad - Otros + + + + + Otros + + + + + + + Datos de identificación de factura expedida para operaciones de consulta + + + + + + + + + + Datos de encadenamiento + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaConsultaLR.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaConsultaLR.xsd new file mode 100644 index 0000000000..fd3ab14fd2 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaConsultaLR.xsd @@ -0,0 +1,195 @@ + + + + + + + + + Servicio de consulta de regIstros de facturacion + + + + + + + + + + + + + + + + + + + Estado del registro almacenado en el sistema. Los estados posibles son: Correcta, AceptadaConErrores y Anulada + + + + + + + Código del error de registro, en su caso. + + + + + + + Descripción detallada del error de registro, en su caso. + + + + + + + + + + + + + + + + + + + + Período al que corresponden los apuntes. todos los apuntes deben corresponder al mismo período impositivo + + + + + + + + + + + + + + + Apunte correspondiente al libro de facturas expedidas. + + + + + + + + + + + Clave del tipo de factura + + + + + Identifica si el tipo de factura rectificativa es por sustitución o por diferencia + + + + + + El ID de las facturas rectificadas, únicamente se rellena en el caso de rectificación de facturas + + + + + + + + + + El ID de las facturas sustituidas, únicamente se rellena en el caso de facturas sustituidas + + + + + + + + + + + + + + + + Tercero que expida la factura y/o genera el registro de alta. + + + + + + Contraparte de la operación. Cliente + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + El registro se almacenado sin errores + + + + + El registro se almacenado tiene algunos errores. Ver detalle del error + + + + + El registro almacenado ha sido anulado + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaSuministro.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaSuministro.xsd new file mode 100644 index 0000000000..d4902b9334 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/RespuestaSuministro.xsd @@ -0,0 +1,139 @@ + + + + + + + + + + + + CSV asociado al envío generado por AEAT. Solo se genera si no hay rechazo del envio + + + + + Se devuelven datos de la presentacion realizada. Solo se genera si no hay rechazo del envio + + + + + Se devuelve la cabecera que se incluyó en el envío. + + + + + + + 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. + + + + + + + + Respuesta a un envío de registro de facturacion + + + + + + + + Estado detallado de cada línea del suministro. + + + + + + + + + + Respuesta a un envío + + + + + ID Factura Expedida + + + + + + + + Estado del registro. Correcto o Incorrecto + + + + + + + Código del error de registro, en su caso. + + + + + + + Descripción detallada del error de registro, en su caso. + + + + + + + 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 + + + + + + + + + + Correcto + + + + + Parcialmente correcto. Ver detalle de errores + + + + + Incorrecto + + + + + + + + + Correcto + + + + + Aceptado con Errores. Ver detalle del error + + + + + Incorrecto + + + + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/SistemaFacturacion.wsdl b/app/Services/EDocument/Standards/Verifactu/xsd/SistemaFacturacion.wsdl new file mode 100644 index 0000000000..3d40d898b9 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/SistemaFacturacion.wsdl @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/SuministroInformacion.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/SuministroInformacion.xsd new file mode 100644 index 0000000000..b6d14b5820 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/SuministroInformacion.xsd @@ -0,0 +1,1399 @@ + + + + + + Datos de cabecera + + + + + Obligado a expedir la factura. + + + + + Representante del obligado tributario. A rellenar solo en caso de que los registros de facturación remitdos hayan sido generados por un representante/asesor del obligado tributario. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Información básica que contienen los registros del sistema de facturacion + + + + + + Período de la fecha de la operación + + + + + + + + + + + + Datos de identificación de factura expedida para operaciones de consulta + + + + + + Nº Serie+Nº Factura de la Factura del Emisor. + + + + + Fecha de emisión de la factura + + + + + + + Datos de identificación de factura que se anula para operaciones de baja + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la Factura que se anula. + + + + + Fecha de emisión de la factura que se anula + + + + + + + Datos correspondientes al registro de facturacion de alta + + + + + + + + + + + Clave del tipo de factura + + + + + Identifica si el tipo de factura rectificativa es por sustitución o por diferencia + + + + + + El ID de las facturas rectificadas, únicamente se rellena en el caso de rectificación de facturas + + + + + + + + + + El ID de las facturas sustituidas, únicamente se rellena en el caso de facturas sustituidas + + + + + + + + + + + + + + + + Tercero que expida la factura y/o genera el registro de alta. + + + + + + Contraparte de la operación. Cliente + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos correspondientes al registro de facturacion de anulacion + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos de encadenamiento + + + + + NIF del obligado a expedir la factura a que se refiere el registro de facturación anterior + + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos de identificación de factura + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la Factura del Emisor + + + + + Fecha de emisión de la factura + + + + + + + + Datos de identificación de factura sustituida o rectificada. El NIF se cogerá del NIF indicado en el bloque IDFactura + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la factura + + + + + Fecha de emisión de la factura sustituida o rectificada + + + + + + + + + + + + + + + + + + + + + + + Desglose de Base y Cuota sustituida en las Facturas Rectificativas sustitutivas + + + + + + + + + + + Datos de una persona física o jurídica Española con un NIF asociado + + + + + + + + + + Datos de una persona física o jurídica Española o Extranjera + + + + + + + + + + + + + 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. + + + + + + + + + + + Rango de fechas de expedicion + + + + + + + + + + + + + + + + + + IdPeticion asociado a la factura registrada previamente en el sistema. Solo se suministra si la factura enviada es rechazada por estar duplicada + + + + + + + Estado del registro duplicado almacenado en el sistema. Los estados posibles son: Correcta, AceptadaConErrores y Anulada. Solo se suministra si la factura enviada es rechazada por estar duplicada + + + + + + + Código del error de registro duplicado almacenado en el sistema, en su caso. + + + + + + + Descripción detallada del error de registro duplicado almacenado en el sistema, en su caso. + + + + + + + + + + + + + + + Año en formato YYYY + + + + + + + + + Período de la factura + + + + + Enero + + + + + Febrero + + + + + Marzo + + + + + Abril + + + + + Mayo + + + + + Junio + + + + + Julio + + + + + Agosto + + + + + Septiembre + + + + + Octubre + + + + + Noviembre + + + + + Diciembre + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NIF + + + + + + + + + + + + + + + + + + + + + + EXENTA por Art. 20 + + + + + EXENTA por Art. 21 + + + + + EXENTA por Art. 22 + + + + + EXENTA por Art. 24 + + + + + EXENTA por Art. 25 + + + + + EXENTA otros + + + + + + + + + + FACTURA (ART. 6, 7.2 Y 7.3 DEL RD 1619/2012) + + + + + FACTURA SIMPLIFICADA Y FACTURAS SIN IDENTIFICACIÓN DEL DESTINATARIO ART. 6.1.D) RD 1619/2012 + + + + + FACTURA RECTIFICATIVA (Art 80.1 y 80.2 y error fundado en derecho) + + + + + FACTURA RECTIFICATIVA (Art. 80.3) + + + + + FACTURA RECTIFICATIVA (Art. 80.4) + + + + + FACTURA RECTIFICATIVA (Resto) + + + + + FACTURA RECTIFICATIVA EN FACTURAS SIMPLIFICADAS + + + + + FACTURA EMITIDA EN SUSTITUCIÓN DE FACTURAS SIMPLIFICADAS FACTURADAS Y DECLARADAS + + + + + + + + + No ha habido rechazo previo por la AEAT. + + + + + Ha habido rechazo previo por la AEAT. + + + + + Independientemente de si ha habido o no algún rechazo previo por la AEAT, el registro de facturación no existe en la AEAT (registro existente en ese SIF o en algún SIF del obligado tributario y que no se remitió a la AEAT, por ejemplo, al acogerse a Veri*factu desde no Veri*factu). No deberían existir operaciones de alta (N,X), por lo que no se admiten. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SUSTITUTIVA + + + + + INCREMENTAL + + + + + + + + + + Destinatario + + + + + Tercero + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Expedidor (obligado a Expedir la factura anulada). + + + + + Destinatario + + + + + Tercero + + + + + + + + + + NIF-IVA + + + + + Pasaporte + + + + + IDEnPaisResidencia + + + + + Certificado Residencia + + + + + Otro documento Probatorio + + + + + No Censado + + + + + + + + + + SHA-256 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + El registro se ha almacenado sin errores + + + + + El registro que se ha almacenado tiene algunos errores. Ver detalle del error + + + + + El registro almacenado ha sido anulado + + + + + + + + + + + + OPERACIÓN SUJETA Y NO EXENTA - SIN INVERSIÓN DEL SUJETO PASIVO. + + + + + OPERACIÓN SUJETA Y NO EXENTA - CON INVERSIÓN DEL SUJETO PASIVO + + + + + OPERACIÓN NO SUJETA ARTÍCULO 7, 14, OTROS. + + + + + OPERACIÓN NO SUJETA POR REGLAS DE LOCALIZACIÓN + + + + + + + + + + + + + + + + + + + Datos de una persona física o jurídica Española o Extranjera + + + + + + + + + + + + Compuesto por datos + de contexto y una secuencia de 1 o más registros. + + + + + + + + Cabecera de la Cobnsulta + + + + + + + Obligado a la emision de los registros de facturacion + + + + + Destinatario (a veces también denominado contraparte, es decir, el cliente) de la operación + + + + + + Flag opcional que tendrá valor S si la consulta la está realizando el representante/asesor del obligado tributario. A rellenar solo en caso de que los registros de facturación remitidos hayan sido generados por un representante/asesor del obligado tributario. Este flag solo se puede cumplimentar cuando esté informado el obligado tributario en la consulta + + + + + + + + Datos de una persona física o jurídica Española con un NIF asociado + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impuesto sobre el Valor Añadido (IVA) + + + + + Impuesto sobre la Producción, los Servicios y la Importación (IPSI) de Ceuta y Melilla + + + + + Impuesto General Indirecto Canario (IGIC) + + + + + Otros + + + + + + + + + + + + + + + + + + La operación realizada ha sido un alta + + + + + La operación realizada ha sido una anulación + + + + + diff --git a/app/Services/EDocument/Standards/Verifactu/xsd/SuministroLR.xsd b/app/Services/EDocument/Standards/Verifactu/xsd/SuministroLR.xsd new file mode 100644 index 0000000000..bde17b7728 --- /dev/null +++ b/app/Services/EDocument/Standards/Verifactu/xsd/SuministroLR.xsd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + Datos correspondientes a los registros de facturacion + + + + + + + + + \ No newline at end of file