Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ composer require robrichards/xmlseclibs
### 1. **Configuration**

Before using any service, you must configure the library with your certificate, password, certificate type, and
environment.
environment.
Choose between certificate type (`certificate` or `seal`) and environment (`production` or `sandbox`) according to
whether you'll be working in production or testing.

Expand Down Expand Up @@ -145,14 +145,17 @@ $invoice->operationDescription = 'Venta de productos';
$invoice->taxAmount = 21.00; // Total tax amount
$invoice->totalAmount = 121.00; // Total invoice amount
$invoice->simplifiedInvoice = YesNoType::NO;
$invoice->invoiceWithoutRecipient = YesNoType::NO;
$invoice->invoiceWithoutRecipient = YesNoType::NO;

// If providing a subsanation after receiving an "Accepted with error" message
// $invoice->isCorrection = true;

// Add tax breakdown (using object-oriented approach)
$breakdown = new Breakdown();
$detail = new BreakdownDetail();
$detail->taxType = TaxType::IVA;
$detail->taxRate = 21.00;
$detail->taxableBase = 100.00;
$detail->taxableBase = 100.00;
$detail->taxAmount = 21.00;
$detail->operationQualification = OperationQualificationType::SUBJECT_NO_EXEMPT_NO_REVERSE;
$breakdown->addDetail($detail);
Expand Down
16 changes: 15 additions & 1 deletion src/models/InvoiceSubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ class InvoiceSubmission extends InvoiceRecord
*/
private $rectificationData = [];

/**
* Identifies if a submission is to subsanate a previous one accepted with errors
*
* @var YesNoType|null
*/
public $isCorrection;

/**
* Invoice type (TipoFactura).
* @var InvoiceType
Expand Down Expand Up @@ -442,6 +449,13 @@ public function rules(): array

return ($value instanceof YesNoType) ? true : 'Must be an instance of YesNoType.';
}],
['isCorrection', function ($value): bool|string {
if ($value === null) {
return true;
}

return ($value instanceof YesNoType) ? true : 'Must be an instance of YesNoType.';
}],
['invoiceWithoutRecipient', function ($value): bool|string {
if ($value === null) {
return true;
Expand Down Expand Up @@ -557,7 +571,7 @@ public function rules(): array

/**
* Deprecated: Use InvoiceSerializer::toInvoiceXml() instead.
*
*
* @deprecated This method has been replaced by InvoiceSerializer::toInvoiceXml()
* @return \DOMDocument
* @throws \Exception
Expand Down
6 changes: 5 additions & 1 deletion src/services/InvoiceSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use eseperio\verifactu\models\Breakdown;
use eseperio\verifactu\models\BreakdownDetail;
use eseperio\verifactu\models\ComputerSystem;
use eseperio\verifactu\models\enums\YesNoType;
use eseperio\verifactu\models\InvoiceCancellation;
use eseperio\verifactu\models\InvoiceId;
use eseperio\verifactu\models\InvoiceQuery;
Expand Down Expand Up @@ -73,7 +74,10 @@ public static function toInvoiceXml(InvoiceSubmission $invoice, bool $validate =
// NombreRazonEmisor (required)
$root->appendChild($doc->createElementNS(self::SF_NAMESPACE, 'sf:NombreRazonEmisor', (string) $invoice->issuerName));


// Subsanacion (optional)
if($invoice->isCorrection) {
$root->appendChild($doc->createElementNS(self::SF_NAMESPACE, 'sf:Subsanacion', (string) $invoice->isCorrection->value));
}

// TipoFactura (required)
if ($invoice->invoiceType) {
Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Models/InvoiceSubmissionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public function testToXmlMethodExists(): void
$submission->invoiceType = 'F1';
$submission->taxAmount = 21.00;
$submission->totalAmount = 121.00;
$submission->isCorrection = YesNoType::NO;

// Set InvoiceId
$invoiceId = new InvoiceId();
Expand Down
25 changes: 25 additions & 0 deletions tests/Unit/Services/InvoiceSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public function testToInvoiceXml(): void
$this->assertEquals(1, $nombreRazon->length);
$this->assertEquals('Test Company', $nombreRazon->item(0)->textContent);

// Verify subsanation is not present
$correction = $dom->getElementsByTagNameNS(InvoiceSerializer::SF_NAMESPACE, 'Subsanacion');
$this->assertEquals(YesNoType::NO->value, $correction->item(0)->textContent);

// Verify invoice type
$tipoFactura = $dom->getElementsByTagNameNS(InvoiceSerializer::SF_NAMESPACE, 'TipoFactura');
$this->assertEquals(1, $tipoFactura->length);
Expand Down Expand Up @@ -96,6 +100,23 @@ public function testToInvoiceXml(): void
$this->assertEquals(str_repeat('a', 64), $huella->item(0)->textContent);
}

/**
* Test that the InvoiceSerializer can generate a XML for InvoiceSubmission with the subsanation info
*/
public function testToInvoiceXmlWithIsSubsanation()
{
// Create a basic InvoiceSubmission object
$invoice = $this->createBasicInvoiceSubmission();
$invoice->isCorrection = YesNoType::YES;

// Generate XML using the serializer
$dom = InvoiceSerializer::toInvoiceXml($invoice, false); // Skip validation

// Verify subsanation is present
$subsanation = $dom->getElementsByTagNameNS(InvoiceSerializer::SF_NAMESPACE, 'Subsanacion');
$this->assertEquals(YesNoType::YES->value, $subsanation->item(0)->textContent);
}

/**
* Test that the InvoiceSerializer can generate XML for an InvoiceCancellation.
*/
Expand Down Expand Up @@ -309,6 +330,7 @@ private function createBasicInvoiceSubmission(): InvoiceSubmission
$invoice->operationDescription = 'Test operation';
$invoice->taxAmount = 21.00;
$invoice->totalAmount = 121.00;
$invoice->isCorrection = YesNoType::NO;

// Add a recipient
$recipient = new LegalPerson();
Expand Down Expand Up @@ -376,6 +398,9 @@ private function createBasicInvoiceQuery(): InvoiceQuery
// Set counterparty
$query->setCounterparty('87654321X', 'Test Counterparty');

// Set issuerparty
$query->setIssuerparty('98765432M', 'Test Issuer');

// Set system info
$query->setSystemInfo('Test System', '1.0');

Expand Down