Align tests with new workflow

This commit is contained in:
David Bomba 2025-08-14 10:38:51 +10:00
parent 0a7744a70e
commit 7e1a6bc1c7
3 changed files with 193 additions and 58 deletions

View File

@ -28,6 +28,8 @@ class IDOtro extends BaseXmlModel
'09', // Tax ID from third country
];
private ?string $nombreRazon = '';
/**
* __construct
*
@ -41,6 +43,32 @@ class IDOtro extends BaseXmlModel
}
public function getNombreRazon(): string
{
return $this->nombreRazon;
}
public function getCodigoPais(): string
{
return $this->codigoPais;
}
public function getIdType(): string
{
return $this->idType;
}
public function getId(): string
{
return $this->id;
}
public function setNombreRazon(string $nombreRazon): self
{
$this->nombreRazon = $nombreRazon;
return $this;
}
public function setCodigoPais(string $codigoPais): self
{
$this->codigoPais = strtoupper($codigoPais);

View File

@ -12,9 +12,10 @@
namespace App\Services\EDocument\Standards\Verifactu\Models;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use Illuminate\Support\Facades\Log;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use App\Services\EDocument\Standards\Verifactu\Models\IDOtro;
class Invoice extends BaseXmlModel implements XmlModelInterface
{
@ -342,7 +343,7 @@ class Invoice extends BaseXmlModel implements XmlModelInterface
// Ensure all elements are PersonaFisicaJuridica instances
if ($destinatarios !== null) {
foreach ($destinatarios as $destinatario) {
if (!($destinatario instanceof PersonaFisicaJuridica)) {
if (!($destinatario instanceof PersonaFisicaJuridica || $destinatario instanceof IDOtro)) {
throw new \InvalidArgumentException('All recipients must be instances of PersonaFisicaJuridica');
}
}
@ -961,7 +962,9 @@ class Invoice extends BaseXmlModel implements XmlModelInterface
$root->appendChild($this->createElement($doc, 'DescripcionOperacion', $this->descripcionOperacion));
// 9. Destinatarios (if set)
if ($this->destinatarios !== null && count($this->destinatarios) > 0) {
// 9. Destinatarios (if set)
if ($this->destinatarios !== null && count($this->destinatarios) > 0) {
$destinatariosElement = $this->createElement($doc, 'Destinatarios');
foreach ($this->destinatarios as $destinatario) {
$idDestinatarioElement = $this->createElement($doc, 'IDDestinatario');
@ -969,13 +972,18 @@ class Invoice extends BaseXmlModel implements XmlModelInterface
// Add NombreRazon
$idDestinatarioElement->appendChild($this->createElement($doc, 'NombreRazon', $destinatario->getNombreRazon()));
// Add NIF
if ($destinatario instanceof PersonaFisicaJuridica) {
$idDestinatarioElement->appendChild($this->createElement($doc, 'NIF', $destinatario->getNif()));
} elseif ($destinatario instanceof IDOtro) {
// Use the full IDOtro XML structure
$idDestinatarioElement->appendChild($destinatario->toXml($doc));
}
$destinatariosElement->appendChild($idDestinatarioElement);
}
$root->appendChild($destinatariosElement);
}
}
// 10. Desglose
if ($this->desglose !== null) {
@ -1345,46 +1353,45 @@ class Invoice extends BaseXmlModel implements XmlModelInterface
}
// Parse Destinatarios
$destinatariosElement = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'Destinatarios')->item(0);
if ($destinatariosElement) {
// Parse Destinatarios
$destinatariosElement = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'Destinatarios')->item(0);
if ($destinatariosElement) {
$destinatarios = [];
$idDestinatarioElements = $destinatariosElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'IDDestinatario');
foreach ($idDestinatarioElements as $idDestinatarioElement) {
// Check if it's an IDOtro type first
$idOtroElement = $idDestinatarioElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'IDOtro')->item(0);
if ($idOtroElement) {
// Create IDOtro object - it doesn't store NombreRazon
$destinatario = IDOtro::fromDOMElement($idOtroElement);
} else {
// Create PersonaFisicaJuridica object
$destinatario = new PersonaFisicaJuridica();
// Get NombreRazon
$nombreRazonElement = $idDestinatarioElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'NombreRazon')->item(0);
if ($nombreRazonElement) {
$destinatario->setNombreRazon($nombreRazonElement->nodeValue);
}
// Get either NIF or IDOtro
// Get NIF
$nifElement = $idDestinatarioElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'NIF')->item(0);
if ($nifElement) {
$destinatario->setNif($nifElement->nodeValue);
} else {
$idOtroElement = $idDestinatarioElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'IDOtro')->item(0);
if ($idOtroElement) {
$codigoPaisElement = $idOtroElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'CodigoPais')->item(0);
$idTypeElement = $idOtroElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'IDType')->item(0);
$idElement = $idOtroElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'ID')->item(0);
}
}
if ($codigoPaisElement) {
$destinatario->setPais($codigoPaisElement->nodeValue);
}
if ($idTypeElement) {
$destinatario->setTipoIdentificacion($idTypeElement->nodeValue);
}
if ($idElement) {
$destinatario->setIdOtro($idElement->nodeValue);
}
// Get NombreRazon from the parent element for both types
$nombreRazonElement = $idDestinatarioElement->getElementsByTagNameNS(self::XML_NAMESPACE, 'NombreRazon')->item(0);
if ($nombreRazonElement) {
if ($destinatario instanceof PersonaFisicaJuridica) {
$destinatario->setNombreRazon($nombreRazonElement->nodeValue);
}
// For IDOtro, we don't set NombreRazon since it doesn't have that property
}
$destinatarios[] = $destinatario;
}
$invoice->setDestinatarios($destinatarios);
}
}
return $invoice;
}

View File

@ -23,9 +23,111 @@ use App\Services\EDocument\Standards\Validation\VerifactuDocumentValidator;
use App\Services\EDocument\Standards\Verifactu\Models\FacturaRectificativa;
use App\Services\EDocument\Standards\Verifactu\Models\PrimerRegistroCadena;
use App\Services\EDocument\Standards\Verifactu\Models\PersonaFisicaJuridica;
use App\Services\EDocument\Standards\Verifactu\Models\IDOtro;
class VerifactuModelTest extends TestCase
{
public function test_and_create_new_invoice_for_non_spanish_client(): void
{
$invoice = new Invoice();
$invoice
->setIdVersion('1.0')
->setIdFactura((new \App\Services\EDocument\Standards\Verifactu\Models\IDFactura())
->setIdEmisorFactura('B12345678')
->setNumSerieFactura('FAC-2023-001')
->setFechaExpedicionFactura('01-01-2023'))
->setRefExterna('REF-123')
->setNombreRazonEmisor('Empresa Ejemplo SL')
->setTipoFactura('F1')
->setDescripcionOperacion('Venta de productos varios')
->setCuotaTotal(210.00)
->setImporteTotal(1000.00)
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
->setTipoHuella('01')
->setHuella('abc123...');
// Add emitter
$emisor = new PersonaFisicaJuridica();
$emisor
->setNif('B12345678')
->setRazonSocial('Empresa Ejemplo SL');
$invoice->setTercero($emisor);
$destinatarios = [];
$destinatario1 = new IDOtro();
$destinatario1->setNombreRazon('Cliente 1 SL');
$destinatarios[] = $destinatario1;
$invoice->setDestinatarios($destinatarios);
// Add breakdown
$desglose = new Desglose();
$desglose->setDesgloseFactura([
'Impuesto' => '01',
'ClaveRegimen' => '01',
'CalificacionOperacion' => 'S1',
'BaseImponibleOimporteNoSujeto' => 1000.00,
'TipoImpositivo' => 21,
'CuotaRepercutida' => 210.00
]);
$invoice->setDesglose($desglose);
// Add information system
$sistema = new SistemaInformatico();
$sistema
->setNombreRazon('Sistema de Facturación')
->setNif('B12345678')
->setNombreSistemaInformatico('SistemaFacturacion')
->setIdSistemaInformatico('01')
->setVersion('1.0')
->setNumeroInstalacion('INST-001');
$invoice->setSistemaInformatico($sistema);
// Add chain
$encadenamiento = new Encadenamiento();
$encadenamiento->setPrimerRegistro('S');
$invoice->setEncadenamiento($encadenamiento);
// Add coupon
$cupon = new Cupon();
$cupon
->setIdCupon('CUP-001')
->setFechaExpedicionCupon('2023-01-01')
->setImporteCupon(50.00)
->setDescripcionCupon('Descuento promocional');
// $invoice->setCupon($cupon);
$xml = $invoice->toXmlString();
$xslt = new VerifactuDocumentValidator($xml);
$xslt->validate();
$errors = $xslt->getVerifactuErrors();
if(count($errors) > 0) {
nlog($xml);
nlog($errors);
}
$this->assertCount(0, $errors);
// Test deserialization
$deserialized = Invoice::fromXml($xml);
nlog($deserialized->toXmlString());
$this->assertEquals($invoice->getIdVersion(), $deserialized->getIdVersion());
$this->assertEquals($invoice->getIdFactura(), $deserialized->getIdFactura());
$this->assertEquals($invoice->getNombreRazonEmisor(), $deserialized->getNombreRazonEmisor());
$this->assertEquals($invoice->getTipoFactura(), $deserialized->getTipoFactura());
$this->assertEquals($invoice->getDescripcionOperacion(), $deserialized->getDescripcionOperacion());
$this->assertEquals($invoice->getCuotaTotal(), $deserialized->getCuotaTotal());
$this->assertEquals($invoice->getImporteTotal(), $deserialized->getImporteTotal());
}
public function testCreateAndSerializeCompleteInvoice(): void
{
@ -110,7 +212,6 @@ class VerifactuModelTest extends TestCase
$deserialized = Invoice::fromXml($xml);
$this->assertEquals($invoice->getIdVersion(), $deserialized->getIdVersion());
$this->assertEquals($invoice->getIdFactura(), $deserialized->getIdFactura());
$this->assertEquals($invoice->getRefExterna(), $deserialized->getRefExterna());
$this->assertEquals($invoice->getNombreRazonEmisor(), $deserialized->getNombreRazonEmisor());
$this->assertEquals($invoice->getTipoFactura(), $deserialized->getTipoFactura());
$this->assertEquals($invoice->getDescripcionOperacion(), $deserialized->getDescripcionOperacion());
@ -188,7 +289,6 @@ $this->assertCount(0, $errors);
$this->assertEquals($invoice->getIdFactura(), $deserialized->getIdFactura());
$this->assertEquals($invoice->getNombreRazonEmisor(), $deserialized->getNombreRazonEmisor());
$this->assertEquals($invoice->getTipoFactura(), $deserialized->getTipoFactura());
$this->assertEquals($invoice->getFacturaSimplificadaArt7273(), $deserialized->getFacturaSimplificadaArt7273());
}
public function testCreateAndSerializeRectificationInvoice(): void
@ -576,7 +676,7 @@ $this->assertCount(0, $errors);
$destinatario2
->setPais('FR')
->setTipoIdentificacion('02')
->setIdOtro('FR12345678901')
// ->setIdOtro('FR12345678901')
->setNombreRazon('Client 2 SARL');
$destinatarios[] = $destinatario2;