Skip to content

Commit

Permalink
Merge pull request #15 from josemmo/develop
Browse files Browse the repository at this point in the history
v0.1.6
  • Loading branch information
josemmo authored Nov 22, 2021
2 parents 0853202 + 79baa88 commit 9704bb1
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 102 deletions.
184 changes: 96 additions & 88 deletions composer.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions src/InvoiceLine.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use function round;

class InvoiceLine {
protected $id = null;
protected $orderLineReference = null;
protected $name = null;
protected $description = null;
Expand All @@ -30,6 +31,26 @@ class InvoiceLine {
use PeriodTrait;
use VatTrait;

/**
* Get invoice line identifier
* @return string|null
*/
public function getId(): ?string {
return $this->id;
}


/**
* Set invoice line identifier
* @param string $id Invoice line identifier
* @return self Invoice line instance
*/
public function setId(string $id): self {
$this->id = $id;
return $this;
}


/**
* Get order line reference
* @return string|null Order line reference
Expand Down
2 changes: 1 addition & 1 deletion src/Presets/Peppol.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use Einvoicing\Invoice;

// @phan-file-suppress PhanPossiblyNonClassMethodCall
// @phan-file-suppress PhanPluginInconsistentReturnFunction, PhanPossiblyNonClassMethodCall

/**
* PEPPOL BIS Billing 3.0
Expand Down
6 changes: 6 additions & 0 deletions src/Readers/UblReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,12 @@ private function parseInvoiceLine(UXML $xml, array &$taxExemptions): InvoiceLine
$cac = UblWriter::NS_CAC;
$cbc = UblWriter::NS_CBC;

// BT-126: Invoice line identifier
$lineId = $xml->get("{{$cbc}}ID");
if ($lineId !== null) {
$line->setId($lineId->asText());
}

// BT-127: Invoice line note
$noteNode = $xml->get("{{$cbc}}Note");
if ($noteNode !== null) {
Expand Down
6 changes: 3 additions & 3 deletions src/Traits/InvoiceValidationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use function array_merge;
use function in_array;

// @phan-file-suppress PhanPossiblyNonClassMethodCall
// @phan-file-suppress PhanPluginInconsistentReturnFunction, PhanPossiblyNonClassMethodCall

trait InvoiceValidationTrait {
/**
Expand All @@ -26,7 +26,7 @@ public function validate(): void {

/**
* Get effective validation rules
* @return array Map of <string,callable> rules
* @return array<string,callable> Map of rules
* @suppress PhanUndeclaredProperty
*/
private function getRules(): array {
Expand All @@ -40,7 +40,7 @@ private function getRules(): array {

/**
* Get EN16931 validation rules
* @return array Map of <string,callable> rules
* @return array<string,callable> Map of rules
*/
private function getDefaultRules(): array {
$res = [];
Expand Down
34 changes: 25 additions & 9 deletions src/Writers/UblWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Einvoicing\Payments\Payment;
use Einvoicing\Payments\Transfer;
use UXML\UXML;
use function in_array;

class UblWriter extends AbstractWriter {
const NS_INVOICE = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2";
Expand Down Expand Up @@ -168,8 +169,16 @@ public function export(Invoice $invoice): string {

// Invoice lines
$lines = $invoice->getLines();
foreach ($lines as $i=>$line) {
$this->addLineNode($xml, $line, $i+1, $invoice); // @phan-suppress-current-line PhanPartialTypeMismatchArgument
$lastGenId = 0;
$usedIds = [];
foreach ($lines as $line) {
$lineId = $line->getId();
if ($lineId !== null) {
$usedIds[] = $lineId;
}
}
foreach ($lines as $line) {
$this->addLineNode($xml, $line, $invoice, $lastGenId, $usedIds);
}

return $xml->asXML();
Expand Down Expand Up @@ -745,16 +754,23 @@ private function addDocumentTotalsNode(UXML $parent, InvoiceTotals $totals) {

/**
* Add invoice line
* @param UXML $parent Parent XML element
* @param InvoiceLine $line Invoice line
* @param int $index Invoice line index
* @param Invoice $invoice Invoice instance
* @param UXML $parent Parent XML element
* @param InvoiceLine $line Invoice line
* @param Invoice $invoice Invoice instance
* @param int &$lastGenId Last used auto-generated ID
* @param string[] &$usedIds Used invoice line IDs
*/
private function addLineNode(UXML $parent, InvoiceLine $line, int $index, Invoice $invoice) {
private function addLineNode(UXML $parent, InvoiceLine $line, Invoice $invoice, int &$lastGenId, array &$usedIds) {
$xml = $parent->add('cac:InvoiceLine');

// BT-126: Line ID
$xml->add('cbc:ID', (string) $index);
// BT-126: Invoice line identifier
$lineId = $line->getId();
if ($lineId === null) {
do {
$lineId = (string) ++$lastGenId;
} while (in_array($lineId, $usedIds));
}
$xml->add('cbc:ID', $lineId);

// BT-127: Invoice line note
$note = $line->getNote();
Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/peppol-allowance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:ID>a-custom-identifier</cbc:ID>
<cbc:Note>Testing note on line level</cbc:Note>
<cbc:InvoicedQuantity unitCode="C62">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">909</cbc:LineExtensionAmount>
Expand Down
5 changes: 5 additions & 0 deletions tests/Readers/UblReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ protected function setUp(): void {
public function testCanReadInvoice(): void {
$invoice = $this->reader->import(file_get_contents(self::DOCUMENT_PATH));
$invoice->validate();

$lines = $invoice->getLines();
$this->assertEquals('1', $lines[0]->getId());
$this->assertEquals('2', $lines[1]->getId());

$totals = $invoice->getTotals();
$this->assertEquals(1300, $totals->netAmount);
$this->assertEquals(1325, $totals->taxExclusiveAmount);
Expand Down
26 changes: 26 additions & 0 deletions tests/Writers/UblWriterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
use Einvoicing\Presets\Peppol;
use Einvoicing\Writers\UblWriter;
use PHPUnit\Framework\TestCase;
use UXML\UXML;
use const CURLOPT_HTTPHEADER;
use const CURLOPT_POSTFIELDS;
use const CURLOPT_RETURNTRANSFER;
use const CURLOPT_URL;
use function array_map;
use function curl_close;
use function curl_exec;
use function curl_init;
Expand Down Expand Up @@ -79,6 +81,7 @@ private function getSampleInvoice(): Invoice {
->addLine($complexLine)
->addLine((new InvoiceLine)->setName('Line #2')->setPrice(40, 2)->setVatRate(21)->setQuantity(4))
->addLine((new InvoiceLine)->setName('Line #3')->setPrice(0.56)->setVatRate(10)->setQuantity(2))
->addLine((new InvoiceLine)->setName('Line #4')->setPrice(0.56)->setVatRate(10)->setQuantity(2))
->addAllowance((new AllowanceOrCharge)->setReason('5% discount')->setAmount(5)->markAsPercentage()->setVatRate(21))
->addAttachment((new Attachment)->setId(new Identifier('INV-123', 'ABT')))
->addAttachment($externalAttachment)
Expand Down Expand Up @@ -124,4 +127,27 @@ public function testCanGenerateValidInvoice(): void {
$contents = $this->writer->export($invoice);
$this->assertTrue($this->validateInvoice($contents, 'ubl'));
}

public function testCanHaveLinesWithForcedDuplicateIdentifiers(): void {
$invoice = $this->getSampleInvoice();
$invoice->getLines()[1]->setId('DuplicateId');
$invoice->getLines()[2]->setId('DuplicateId');
$invoice->getLines()[3]->setId('DuplicateId');
$xml = UXML::fromString($this->writer->export($invoice));
$actualLineIds = array_map(function(UXML $item) {
return $item->asText();
}, $xml->getAll('cac:InvoiceLine/cbc:ID'));
$this->assertEquals(['1', 'DuplicateId', 'DuplicateId', 'DuplicateId'], $actualLineIds);
}

public function testCanAutogenerateInvoiceLineIdentifiers(): void {
$invoice = $this->getSampleInvoice();
$invoice->getLines()[1]->setId('1');
$invoice->getLines()[2]->setId('AnotherCustomId');
$xml = UXML::fromString($this->writer->export($invoice));
$actualLineIds = array_map(function(UXML $item) {
return $item->asText();
}, $xml->getAll('cac:InvoiceLine/cbc:ID'));
$this->assertEquals(['2', '1', 'AnotherCustomId', '3'], $actualLineIds);
}
}

0 comments on commit 9704bb1

Please sign in to comment.