From 10520c3d98e1d5bc39a1675815561d8f7665e0db Mon Sep 17 00:00:00 2001 From: HorstOeko Date: Mon, 23 Sep 2024 05:23:56 +0200 Subject: [PATCH] #119 Update to ZUGFeRD 2.3 (Support for InvoiceReferencedDocument) --- src/ZugferdDocumentBuilder.php | 33 ++++++++-- src/ZugferdDocumentReader.php | 83 ++++++++++++++++++++++++- src/quick/ZugferdQuickDescriptor.php | 2 +- tests/assets/extended_invoice.xml | 14 +++++ tests/testcases/BuilderEn16931Test.php | 20 +++++- tests/testcases/BuilderExtendedTest.php | 23 +++++-- tests/testcases/BuilderMinimumTest.php | 20 +++++- tests/testcases/ReaderExtendedTest.php | 41 ++++++++++++ 8 files changed, 218 insertions(+), 18 deletions(-) diff --git a/src/ZugferdDocumentBuilder.php b/src/ZugferdDocumentBuilder.php index 3b7524e..6c7f5a5 100644 --- a/src/ZugferdDocumentBuilder.php +++ b/src/ZugferdDocumentBuilder.php @@ -2176,22 +2176,43 @@ public function addDocumentAdditionalReferencedDocument(string $issuerassignedid /** * Set a Reference to the previous invoice * - * __Note__: To be used if: + * To be used if: * - a previous invoice is corrected - * - reference is made to previous partial invoices from a final invoice - * - Reference is made to previous invoices for advance payments from a final invoice + * - reference is made from a final invoice to previous partial invoices + * - reference is made from a final invoice to previous invoices for advance payments. * * - * @param string $issuerassignedid __BT-25, From BASIC WL__ Number of the previous invoice + * @param string $issuerassignedid __BT-25, From BASIC WL__ The identification of an invoice previously sent by the seller + * @param string|null $typecode Type of previous invoice (code) * @param DateTime|null $issueddate __BT-26, From BASIC WL__ Date of the previous invoice * @return ZugferdDocumentBuilder */ - public function setDocumentInvoiceReferencedDocument(string $issuerassignedid, ?DateTime $issueddate = null): ZugferdDocumentBuilder + public function setDocumentInvoiceReferencedDocument(string $issuerassignedid, ?string $typecode = null, ?DateTime $issueddate = null): ZugferdDocumentBuilder { - $invoicerefdoc = $this->getObjectHelper()->getReferencedDocumentType($issuerassignedid, null, null, null, null, null, $issueddate, null); + $invoicerefdoc = $this->getObjectHelper()->getReferencedDocumentType($issuerassignedid, null, null, $typecode, null, null, $issueddate, null); $this->getObjectHelper()->tryCallIfMethodExists($this->headerTradeSettlement, "addToInvoiceReferencedDocument", "setInvoiceReferencedDocument", [$invoicerefdoc], $invoicerefdoc); return $this; } + /** + * Add a Reference to the previous invoice + * + * To be used if: + * - a previous invoice is corrected + * - reference is made from a final invoice to previous partial invoices + * - reference is made from a final invoice to previous invoices for advance payments. * + * + * @param string $issuerassignedid __BT-25, From BASIC WL__ The identification of an invoice previously sent by the seller + * @param string|null $typecode Type of previous invoice (code) + * @param DateTime|null $issueddate __BT-26, From BASIC WL__ Date of the previous invoice + * @return ZugferdDocumentBuilder + */ + public function addDocumentInvoiceReferencedDocument(string $issuerassignedid, ?string $typecode = null, ?DateTime $issueddate = null): ZugferdDocumentBuilder + { + $invoicerefdoc = $this->getObjectHelper()->getReferencedDocumentType($issuerassignedid, null, null, $typecode, null, null, $issueddate, null); + $this->getObjectHelper()->tryCall($this->headerTradeSettlement, "addToInvoiceReferencedDocument", $invoicerefdoc); + return $this; + } + /** * Set Details of a project reference * diff --git a/src/ZugferdDocumentReader.php b/src/ZugferdDocumentReader.php index 2b0b5a1..4bbc4e6 100644 --- a/src/ZugferdDocumentReader.php +++ b/src/ZugferdDocumentReader.php @@ -30,7 +30,7 @@ class ZugferdDocumentReader extends ZugferdDocument { /** - * Undocumented variable + * Internal pointer for documents additional documents * * @var integer */ @@ -148,6 +148,13 @@ class ZugferdDocumentReader extends ZugferdDocument */ private $documentPayeeContactPointer = 0; + /** + * Internal pointer for documents invoice reference documents + * + * @var integer + */ + private $documentInvRefDocPointer = 0; + /** * Internal pointer for the position * @@ -2481,6 +2488,80 @@ public function getDocumentAdditionalReferencedDocuments(?array &$refdocs): Zugf return $this; } + /** + * Get first reference to the previous invoice + * Returns true if an additional referenced document is available, otherwise false + * + * @return boolean + */ + public function firstDocumentInvoiceReferencedDocument(): bool + { + $this->documentInvRefDocPointer = 0; + $addRefDoc = $this->getInvoiceValueByPath("getSupplyChainTradeTransaction.getApplicableHeaderTradeSettlement.getInvoiceReferencedDocument", []); + return isset($addRefDoc[$this->documentInvRefDocPointer]); + } + + /** + * Get next reference to the previous invoice + * Returns true when another additional referenced document is available, otherwise false + * + * @return boolean + */ + public function nextDocumentInvoiceReferencedDocument(): bool + { + $this->documentInvRefDocPointer++; + $addRefDoc = $this->getInvoiceValueByPath("getSupplyChainTradeTransaction.getApplicableHeaderTradeSettlement.getInvoiceReferencedDocument", []); + return isset($addRefDoc[$this->documentInvRefDocPointer]); + } + + /** + * Get reference to the previous invoice + * + * @param string|null $issuerassignedid + * __BT-X-331__ Reference to the previous invoice + * @param string|null $typecode + * __BT-X-332__ Type of previous invoice (code) + * @param DateTime|null $issueddate + * __BT-X-333-00__ Document date + */ + public function getDocumentInvoiceReferencedDocument(?string &$issuerassignedid, ?string &$typecode, ?DateTime &$issueddate = null): ZugferdDocumentReader + { + $invoiceRefDoc = $this->getInvoiceValueByPath("getSupplyChainTradeTransaction.getApplicableHeaderTradeSettlement.getInvoiceReferencedDocument", []); + $invoiceRefDoc = $invoiceRefDoc[$this->documentInvRefDocPointer]; + + $issuerassignedid = $this->getInvoiceValueByPathFrom($invoiceRefDoc, "getIssuerAssignedID.value", ""); + $typecode = $this->getInvoiceValueByPathFrom($invoiceRefDoc, "getTypeCode.value", ""); + $issueddate = $this->getObjectHelper()->toDateTime( + $this->getInvoiceValueByPathFrom($invoiceRefDoc, "getFormattedIssueDateTime.getDateTimeString.value", ""), + $this->getInvoiceValueByPathFrom($invoiceRefDoc, "getFormattedIssueDateTime.getDateTimeString.getFormat", "") + ); + + return $this; + } + + /** + * Get all references to the previous invoice + * + * @param array|null $refdocs + * Array contains all invoice referenced documents, but without extracting attached binary objects. If you + * want to access attached binary objects you have to use ZugferdDocumentReader::getDocumentInvoiceReferencedDocument + * @return ZugferdDocumentReader + */ + public function getDocumentInvoiceReferencedDocuments(?array &$invoiceRefDocs): ZugferdDocumentReader + { + $invoiceRefDocs = $this->getInvoiceValueByPath("getSupplyChainTradeTransaction.getApplicableHeaderTradeSettlement.getInvoiceReferencedDocument", []); + $invoiceRefDocs = $this->convertToArray( + $invoiceRefDocs, + [ + "IssuerAssignedID" => ["getIssuerAssignedID.value", ""], + "TypeCode" => ["getTypeCode.value", ""], + "FormattedIssueDateTime" => ["getFormattedIssueDateTime.getDateTimeString.value", ""], + ] + ); + + return $this; + } + /** * Get Details of a project reference * diff --git a/src/quick/ZugferdQuickDescriptor.php b/src/quick/ZugferdQuickDescriptor.php index f76cc4d..fd3f678 100644 --- a/src/quick/ZugferdQuickDescriptor.php +++ b/src/quick/ZugferdQuickDescriptor.php @@ -359,7 +359,7 @@ public function doSetDeliveryNoteReferenceDocument(string $deliveryNoteNo, DateT */ public function doSetInvoiceReferencedDocument(string $id, ?DateTime $issueDateTime = null): ZugferdQuickDescriptor { - $this->setDocumentInvoiceReferencedDocument($id, $issueDateTime); + $this->setDocumentInvoiceReferencedDocument($id, null, $issueDateTime); return $this; } diff --git a/tests/assets/extended_invoice.xml b/tests/assets/extended_invoice.xml index 95a7f86..66b62ee 100644 --- a/tests/assets/extended_invoice.xml +++ b/tests/assets/extended_invoice.xml @@ -351,6 +351,20 @@ 0.00 480.22 + + S-INV1 + 83 + + 2024-09-30 + + + + S-INV2 + 84 + + 2024-09-30 + + diff --git a/tests/testcases/BuilderEn16931Test.php b/tests/testcases/BuilderEn16931Test.php index a0bc1fe..47497af 100644 --- a/tests/testcases/BuilderEn16931Test.php +++ b/tests/testcases/BuilderEn16931Test.php @@ -1306,11 +1306,25 @@ public function testAddDocumentAdditionalReferencedDocumentWithAttachment(): voi public function testSetDocumentInvoiceReferencedDocument(): void { - (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", new DateTime()); + (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", "71", new DateTime()); $this->disableRenderXmlContent(); - $this->assertXPathValue('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', "INV-1"); - $this->assertXPathValueWithAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', (new DateTime())->format("Ymd"), "format", "102"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0, "INV-1"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0, "71"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0, (new DateTime())->format("Ymd"), "format", "102"); + } + + public function testAddDocumentInvoiceReferencedDocument(): void + { + (self::$document)->addDocumentInvoiceReferencedDocument("INV-2", "82", new DateTime()); + + $this->disableRenderXmlContent(); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0, "INV-1"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0, "71"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0, (new DateTime())->format("Ymd"), "format", "102"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 1, "INV-2"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 1, "82"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 1, (new DateTime())->format("Ymd"), "format", "102"); } public function testSetDocumentProcuringProject(): void diff --git a/tests/testcases/BuilderExtendedTest.php b/tests/testcases/BuilderExtendedTest.php index f259ac3..bc0aefb 100644 --- a/tests/testcases/BuilderExtendedTest.php +++ b/tests/testcases/BuilderExtendedTest.php @@ -2,10 +2,11 @@ namespace horstoeko\zugferd\tests\testcases; +use DateTime; use \horstoeko\zugferd\tests\TestCase; -use \horstoeko\zugferd\tests\traits\HandlesXmlTests; use \horstoeko\zugferd\ZugferdProfiles; use \horstoeko\zugferd\ZugferdDocumentBuilder; +use \horstoeko\zugferd\tests\traits\HandlesXmlTests; class BuilderExtendedTest extends TestCase { @@ -1351,11 +1352,25 @@ public function testAddDocumentAdditionalReferencedDocumentWithAttachment(): voi public function testSetDocumentInvoiceReferencedDocument(): void { - (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", new \DateTime()); + (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", "71", new DateTime()); + + $this->disableRenderXmlContent(); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0, "INV-1"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0, "71"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0, (new DateTime())->format("Ymd"), "format", "102"); + } + + public function testAddDocumentInvoiceReferencedDocument(): void + { + (self::$document)->addDocumentInvoiceReferencedDocument("INV-2", "82", new DateTime()); $this->disableRenderXmlContent(); - $this->assertXPathValue('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', "INV-1"); - $this->assertXPathValueWithAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', (new \DateTime())->format("Ymd"), "format", "102"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0, "INV-1"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0, "71"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0, (new DateTime())->format("Ymd"), "format", "102"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 1, "INV-2"); + $this->assertXPathValueWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 1, "82"); + $this->assertXPathValueWithIndexAndAttribute('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 1, (new DateTime())->format("Ymd"), "format", "102"); } public function testSetDocumentProcuringProject(): void diff --git a/tests/testcases/BuilderMinimumTest.php b/tests/testcases/BuilderMinimumTest.php index ca1655a..f7f1f19 100644 --- a/tests/testcases/BuilderMinimumTest.php +++ b/tests/testcases/BuilderMinimumTest.php @@ -1304,11 +1304,25 @@ public function testAddDocumentAdditionalReferencedDocumentWithAttachment(): voi public function testSetDocumentInvoiceReferencedDocument(): void { - (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", new DateTime()); + (self::$document)->setDocumentInvoiceReferencedDocument("INV-1", "71", new DateTime()); $this->disableRenderXmlContent(); - $this->assertXPathNotExists('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID'); - $this->assertXPathNotExists('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString'); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0); + } + + public function testAddDocumentInvoiceReferencedDocument(): void + { + (self::$document)->addDocumentInvoiceReferencedDocument("INV-2", "82", new DateTime()); + + $this->disableRenderXmlContent(); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 0); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 0); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 0); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID', 1); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/a:DateTimeString', 1); + $this->assertXPathNotExistsWithIndex('/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:TypeCode', 1); } public function testSetDocumentProcuringProject(): void diff --git a/tests/testcases/ReaderExtendedTest.php b/tests/testcases/ReaderExtendedTest.php index 46974f0..f247989 100644 --- a/tests/testcases/ReaderExtendedTest.php +++ b/tests/testcases/ReaderExtendedTest.php @@ -772,6 +772,27 @@ public function testDocumentAdditionalReferencedDocuments(): void $this->assertEquals("130", $additionalrefdocs[0]["TypeCode"]); } + public function testDocumentInvoiceReferencedDocuments(): void + { + self::$document->getDocumentInvoiceReferencedDocuments($invoicerefdocs); + $this->assertIsArray($invoicerefdocs); + $this->assertNotEmpty($invoicerefdocs); + $this->assertArrayHasKey(0, $invoicerefdocs); + $this->assertIsArray($invoicerefdocs[0]); + $this->assertArrayHasKey("IssuerAssignedID", $invoicerefdocs[0]); + $this->assertArrayHasKey("TypeCode", $invoicerefdocs[0]); + $this->assertArrayHasKey("FormattedIssueDateTime", $invoicerefdocs[0]); + $this->assertEquals("S-INV1", $invoicerefdocs[0]["IssuerAssignedID"]); + $this->assertEquals("83", $invoicerefdocs[0]["TypeCode"]); + $this->assertArrayHasKey(1, $invoicerefdocs); + $this->assertIsArray($invoicerefdocs[1]); + $this->assertArrayHasKey("IssuerAssignedID", $invoicerefdocs[1]); + $this->assertArrayHasKey("TypeCode", $invoicerefdocs[1]); + $this->assertArrayHasKey("FormattedIssueDateTime", $invoicerefdocs[1]); + $this->assertEquals("S-INV2", $invoicerefdocs[1]["IssuerAssignedID"]); + $this->assertEquals("84", $invoicerefdocs[1]["TypeCode"]); + } + public function testDocumentProcuringProject(): void { self::$document->getDocumentProcuringProject($projectid, $projectname); @@ -872,6 +893,26 @@ public function testGetDocumentAdditionalReferencedDocument(): void $this->assertEquals("", $binarydatafilename); } + public function testDocumentInvoiceReferencedDocumentLoop(): void + { + $this->assertTrue(self::$document->firstDocumentInvoiceReferencedDocument()); + $this->assertTrue(self::$document->nextDocumentInvoiceReferencedDocument()); + $this->assertFalse(self::$document->nextDocumentInvoiceReferencedDocument()); + } + + public function testGetDocumentInvoiceReferencedDocument(): void + { + $this->assertTrue(self::$document->firstDocumentInvoiceReferencedDocument()); + self::$document->getDocumentInvoiceReferencedDocument($issuerassignedid, $typecode, $issueddate); + $this->assertEquals("S-INV1", $issuerassignedid); + $this->assertEquals("83", $typecode); + $this->assertTrue(self::$document->nextDocumentInvoiceReferencedDocument()); + self::$document->getDocumentInvoiceReferencedDocument($issuerassignedid, $typecode, $issueddate); + $this->assertEquals("S-INV2", $issuerassignedid); + $this->assertEquals("84", $typecode); + $this->assertFalse(self::$document->nextDocumentInvoiceReferencedDocument()); + } + public function testDocumentUltimateCustomerOrderReferencedDocumentLoop(): void { $this->assertFalse(self::$document->firstDocumentUltimateCustomerOrderReferencedDocument());