Skip to content

Commit

Permalink
Merge pull request #7 from josemmo/develop
Browse files Browse the repository at this point in the history
v0.1.3
  • Loading branch information
josemmo committed Sep 12, 2021
2 parents cc96960 + bc7d777 commit c9ce3bc
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 1 deletion.
148 changes: 148 additions & 0 deletions src/Attachment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
namespace Einvoicing;

class Attachment {
protected $id = null;
protected $description = null;
protected $externalUrl = null;
protected $filename = null;
protected $mimeCode = null;
protected $contents = null;

/**
* Get attachment ID
* @return Identifier|null Attachment ID
*/
public function getId(): ?Identifier {
return $this->id;
}


/**
* Set attachment ID
* @param Identifier|null $id Attachment ID
* @return self Attachment instance
*/
public function setId(?Identifier $id): self {
$this->id = $id;
return $this;
}


/**
* Get description
* @return string|null Attachment description
*/
public function getDescription(): ?string {
return $this->description;
}


/**
* Set description
* @param string|null $description Attachment description
* @return self Attachment instance
*/
public function setDescription(?string $description): self {
$this->description = $description;
return $this;
}


/**
* Has external URL
* @return boolean Whether this attachment has an external URL or not
*/
public function hasExternalUrl(): bool {
return ($this->externalUrl !== null);
}


/**
* Get external URL
* @return string|null Attachment external URL
*/
public function getExternalUrl(): ?string {
return $this->externalUrl;
}


/**
* Set external URL
* @param string|null $externalUrl Attachment external URL
* @return self Attachment instance
*/
public function setExternalUrl(?string $externalUrl): self {
$this->externalUrl = $externalUrl;
return $this;
}


/**
* Get filename
* @return string|null Attachment filename
*/
public function getFilename(): ?string {
return $this->filename;
}


/**
* Set filename
* @param string|null $filename Attachment filename
* @return self Attachment instance
*/
public function setFilename(?string $filename): self {
$this->filename = $filename;
return $this;
}


/**
* Get mime code
* @return string|null Attachment mime code
*/
public function getMimeCode(): ?string {
return $this->mimeCode;
}


/**
* Set mime code
* @param string|null $mimeCode Attachment mime code
* @return self Attachment instance
*/
public function setMimeCode(?string $mimeCode): self {
$this->mimeCode = $mimeCode;
return $this;
}


/**
* Has embedded contents
* @return boolean Whether this attachment has embedded contents or not
*/
public function hasContents(): bool {
return ($this->contents !== null);
}


/**
* Get embedded contents
* @return string|null Attachment contents
*/
public function getContents(): ?string {
return $this->contents;
}


/**
* Set embedded contents
* @param string|null $content Attachment contents
* @return self Attachment instance
*/
public function setContents(?string $contents): self {
$this->contents = $contents;
return $this;
}
}
2 changes: 2 additions & 0 deletions src/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Einvoicing\Payments\Payment;
use Einvoicing\Presets\AbstractPreset;
use Einvoicing\Traits\AllowanceOrChargeTrait;
use Einvoicing\Traits\AttachmentsTrait;
use Einvoicing\Traits\BuyerAccountingReferenceTrait;
use Einvoicing\Traits\InvoiceValidationTrait;
use Einvoicing\Traits\PeriodTrait;
Expand Down Expand Up @@ -43,6 +44,7 @@ class Invoice {
protected $lines = [];

use AllowanceOrChargeTrait;
use AttachmentsTrait;
use BuyerAccountingReferenceTrait;
use PeriodTrait;
use InvoiceValidationTrait;
Expand Down
51 changes: 51 additions & 0 deletions src/Readers/UblReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use DateTime;
use Einvoicing\AllowanceOrCharge;
use Einvoicing\Attachment;
use Einvoicing\Attribute;
use Einvoicing\Delivery;
use Einvoicing\Identifier;
Expand Down Expand Up @@ -141,6 +142,11 @@ public function import(string $document): Invoice {
$invoice->setSalesOrderReference($salesOrderReferenceNode->asText());
}

// BG-24: Attachment nodes
foreach ($xml->getAll("{{$cac}}AdditionalDocumentReference") as $node) {
$invoice->addAttachment($this->parseAttachmentNode($node));
}

// Seller node
$sellerNode = $xml->get("{{$cac}}AccountingSupplierParty/{{$cac}}Party");
if ($sellerNode !== null) {
Expand Down Expand Up @@ -760,4 +766,49 @@ private function parseInvoiceLine(UXML $xml, array &$taxExemptions): InvoiceLine

return $line;
}

/**
* Parse attachment node
* @param UXML $xml XML node
* @return Attachment Attachment instance
*/
private function parseAttachmentNode(UXML $xml): Attachment {
$attachment = new Attachment();
$cac = UblWriter::NS_CAC;
$cbc = UblWriter::NS_CBC;

// BT-122: Supporting document reference
$identifierNode = $xml->get("{{$cbc}}ID");
if ($identifierNode !== null) {
$attachment->setId($this->parseIdentifierNode($identifierNode));
}

// BT-123: Supporting document description
$descriptionNode = $xml->get("{{$cbc}}DocumentDescription");
if ($descriptionNode !== null) {
$attachment->setDescription($descriptionNode->asText());
}

// BT-125: Attached document
$embeddedDocumentNode = $xml->get("{{$cac}}Attachment/{{$cbc}}EmbeddedDocumentBinaryObject");
if ($embeddedDocumentNode !== null) {
$embeddedDocumentElement = $embeddedDocumentNode->element();
// @phan-suppress-next-line PhanPossiblyFalseTypeArgument
$attachment->setContents(base64_decode($embeddedDocumentNode->asText()));
if ($embeddedDocumentElement->hasAttribute('mimeCode')) {
$attachment->setMimeCode($embeddedDocumentElement->getAttribute('mimeCode'));
}
if ($embeddedDocumentElement->hasAttribute('filename')) {
$attachment->setFilename($embeddedDocumentElement->getAttribute('filename'));
}
}

// BT-124: External document location
$externalDocumentNode = $xml->get("{{$cac}}Attachment/{{$cac}}ExternalReference/{{$cbc}}URI");
if ($externalDocumentNode !== null) {
$attachment->setExternalUrl($externalDocumentNode->asText());
}

return $attachment;
}
}
53 changes: 53 additions & 0 deletions src/Traits/AttachmentsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
namespace Einvoicing\Traits;

use Einvoicing\Attachment;
use OutOfBoundsException;

trait AttachmentsTrait {
protected $attachments = [];

/**
* Get attachments
* @return Attachment[] Array of attachments
*/
public function getAttachments(): array {
return $this->attachments;
}


/**
* Add attachment
* @param Attachment $attachment Attachment
* @return self This instance
*/
public function addAttachment(Attachment $attachment): self {
$this->attachments[] = $attachment;
return $this;
}


/**
* Remove attachment
* @param int $index Attachment index
* @return self This instance
* @throws OutOfBoundsException if attachment index is out of bounds
*/
public function removeAttachment(int $index): self {
if ($index < 0 || $index >= count($this->attachments)) {
throw new OutOfBoundsException('Could not find attachment by index');
}
array_splice($this->attachments, $index, 1);
return $this;
}


/**
* Clear all attachments
* @return self This instance
*/
public function clearAttachments(): self {
$this->attachments = [];
return $this;
}
}
7 changes: 7 additions & 0 deletions src/Traits/InvoiceValidationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ private function getDefaultRules(): array {
"shall be present if Payment card information (BG-18) is provided in the Invoice";
}
};
$res['BR-52'] = static function(Invoice $inv) {
foreach ($inv->getAttachments() as $attachment) {
if ($attachment->getId() === null) {
return "Each Additional supporting document shall contain a Supporting document reference (BT-122)";
}
}
};
$res['BR-61'] = static function(Invoice $inv) {
if ($inv->getPayment() === null) return;
if (!in_array($inv->getPayment()->getMeansCode(), ['30', '58'])) return;
Expand Down
61 changes: 61 additions & 0 deletions src/Writers/UblWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Einvoicing\Writers;

use Einvoicing\AllowanceOrCharge;
use Einvoicing\Attachment;
use Einvoicing\Delivery;
use Einvoicing\Identifier;
use Einvoicing\Invoice;
Expand Down Expand Up @@ -95,6 +96,11 @@ public function export(Invoice $invoice): string {
// Order reference node
$this->addOrderReferenceNode($xml, $invoice);

// BG-24: Attachments node
foreach ($invoice->getAttachments() as $attachment) {
$this->addAttachmentNode($xml, $attachment);
}

// Seller node
$seller = $invoice->getSeller();
if ($seller !== null) {
Expand Down Expand Up @@ -827,4 +833,59 @@ private function addLineNode(UXML $parent, InvoiceLine $line, int $index, Invoic

return $xml;
}

/**
* Add attachment node
* @param UXML $parent Parent element
* @param Attachment $attachment Attachment instance
*/
private function addAttachmentNode(UXML $parent, Attachment $attachment) {
$xml = $parent->add('cac:AdditionalDocumentReference');
$isInvoiceObjectReference = (!$attachment->hasExternalUrl() && !$attachment->hasContents());

// BT-122: Supporting document reference
$identifier = $attachment->getId();
if ($identifier !== null) {
$this->addIdentifierNode($xml, 'cbc:ID', $identifier);
}

// BT-18: Document type code
if ($isInvoiceObjectReference) {
// Code "130" MUST be used to indicate an invoice object reference
// Not used for other additional documents
$xml->add('cbc:DocumentTypeCode', '130');
}

// BT-123: Supporting document description
$description = $attachment->getDescription();
if ($description !== null) {
$xml->add('cbc:DocumentDescription', $description);
}

// Attachment inner node
if ($isInvoiceObjectReference) {
return; // Skip inner node in this case
}
$attXml = $xml->add('cac:Attachment');

// BT-125: Attached document
if ($attachment->hasContents()) {
$attrs = [];
$mimeCode = $attachment->getMimeCode();
$filename = $attachment->getFilename();
if ($mimeCode !== null) {
$attrs['mimeCode'] = $mimeCode;
}
if ($filename !== null) {
$attrs['filename'] = $filename;
}
$attXml->add('cbc:EmbeddedDocumentBinaryObject', base64_encode($attachment->getContents()), $attrs);
}

// BT-124: External document location
$externalUrl = $attachment->getExternalUrl();
if ($externalUrl !== null) {
$attXml->add('cac:ExternalReference')->add('cbc:URI', $externalUrl);
}
}
}
Loading

0 comments on commit c9ce3bc

Please sign in to comment.