Browse Source

Add ZUGFeRD conformance testing

Marcin Ziąbek 1 month ago
parent
commit
e1eaaf373d

+ 191 - 0
Source/QuestPDF.ConformanceTests/Resources/zugferd-factur-x.xml

@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
+    <rsm:ExchangedDocumentContext>
+        <ram:GuidelineSpecifiedDocumentContextParameter>
+            <ram:ID>urn:cen.eu:en16931:2017</ram:ID>
+        </ram:GuidelineSpecifiedDocumentContextParameter>
+    </rsm:ExchangedDocumentContext>
+    <rsm:ExchangedDocument>
+        <ram:ID>RE-20201121/508</ram:ID>
+        <ram:TypeCode>380</ram:TypeCode>
+        <ram:IssueDateTime>
+            <udt:DateTimeString format="102">20201121</udt:DateTimeString>f
+        </ram:IssueDateTime>
+    </rsm:ExchangedDocument>
+    <rsm:SupplyChainTradeTransaction>
+        <ram:IncludedSupplyChainTradeLineItem>
+            <ram:AssociatedDocumentLineDocument>
+                <ram:LineID>1</ram:LineID>
+            </ram:AssociatedDocumentLineDocument>
+            <ram:SpecifiedTradeProduct>
+                <ram:Name>Design (hours)</ram:Name>
+                <ram:Description>Of a sample invoice</ram:Description>
+            </ram:SpecifiedTradeProduct>
+            <ram:SpecifiedLineTradeAgreement>
+                <ram:GrossPriceProductTradePrice>
+                    <ram:ChargeAmount>160.0000</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity>
+                </ram:GrossPriceProductTradePrice>
+                <ram:NetPriceProductTradePrice>
+                    <ram:ChargeAmount>160.0000</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity>
+                </ram:NetPriceProductTradePrice>
+            </ram:SpecifiedLineTradeAgreement>
+            <ram:SpecifiedLineTradeDelivery>
+                <ram:BilledQuantity unitCode="HUR">1.0000</ram:BilledQuantity>
+            </ram:SpecifiedLineTradeDelivery>
+            <ram:SpecifiedLineTradeSettlement>
+                <ram:ApplicableTradeTax>
+                    <ram:TypeCode>VAT</ram:TypeCode>
+                    <ram:CategoryCode>S</ram:CategoryCode>
+                    <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
+                </ram:ApplicableTradeTax>
+                <ram:SpecifiedTradeSettlementLineMonetarySummation>
+                    <ram:LineTotalAmount>160.00</ram:LineTotalAmount>
+                </ram:SpecifiedTradeSettlementLineMonetarySummation>
+            </ram:SpecifiedLineTradeSettlement>
+        </ram:IncludedSupplyChainTradeLineItem>
+        <ram:IncludedSupplyChainTradeLineItem>
+            <ram:AssociatedDocumentLineDocument>
+                <ram:LineID>2</ram:LineID>
+            </ram:AssociatedDocumentLineDocument>
+            <ram:SpecifiedTradeProduct>
+                <ram:Name>Ballons</ram:Name>
+                <ram:Description>various colors, ~2000ml</ram:Description>
+            </ram:SpecifiedTradeProduct>
+            <ram:SpecifiedLineTradeAgreement>
+                <ram:GrossPriceProductTradePrice>
+                    <ram:ChargeAmount>0.7900</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
+                </ram:GrossPriceProductTradePrice>
+                <ram:NetPriceProductTradePrice>
+                    <ram:ChargeAmount>0.7900</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
+                </ram:NetPriceProductTradePrice>
+            </ram:SpecifiedLineTradeAgreement>
+            <ram:SpecifiedLineTradeDelivery>
+                <ram:BilledQuantity unitCode="H87">400.0000</ram:BilledQuantity>
+            </ram:SpecifiedLineTradeDelivery>
+            <ram:SpecifiedLineTradeSettlement>
+                <ram:ApplicableTradeTax>
+                    <ram:TypeCode>VAT</ram:TypeCode>
+                    <ram:CategoryCode>S</ram:CategoryCode>
+                    <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
+                </ram:ApplicableTradeTax>
+                <ram:SpecifiedTradeSettlementLineMonetarySummation>
+                    <ram:LineTotalAmount>316.00</ram:LineTotalAmount>
+                </ram:SpecifiedTradeSettlementLineMonetarySummation>
+            </ram:SpecifiedLineTradeSettlement>
+        </ram:IncludedSupplyChainTradeLineItem>
+        <ram:IncludedSupplyChainTradeLineItem>
+            <ram:AssociatedDocumentLineDocument>
+                <ram:LineID>3</ram:LineID>
+            </ram:AssociatedDocumentLineDocument>
+            <ram:SpecifiedTradeProduct>
+                <ram:Name>Hot air „heiße Luft“ (litres)</ram:Name>
+                <ram:Description/>
+            </ram:SpecifiedTradeProduct>
+            <ram:SpecifiedLineTradeAgreement>
+                <ram:GrossPriceProductTradePrice>
+                    <ram:ChargeAmount>0.0250</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity>
+                </ram:GrossPriceProductTradePrice>
+                <ram:NetPriceProductTradePrice>
+                    <ram:ChargeAmount>0.0250</ram:ChargeAmount>
+                    <ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity>
+                </ram:NetPriceProductTradePrice>
+            </ram:SpecifiedLineTradeAgreement>
+            <ram:SpecifiedLineTradeDelivery>
+                <ram:BilledQuantity unitCode="LTR">800.0000</ram:BilledQuantity>
+            </ram:SpecifiedLineTradeDelivery>
+            <ram:SpecifiedLineTradeSettlement>
+                <ram:ApplicableTradeTax>
+                    <ram:TypeCode>VAT</ram:TypeCode>
+                    <ram:CategoryCode>S</ram:CategoryCode>
+                    <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
+                </ram:ApplicableTradeTax>
+                <ram:SpecifiedTradeSettlementLineMonetarySummation>
+                    <ram:LineTotalAmount>20.00</ram:LineTotalAmount>
+                </ram:SpecifiedTradeSettlementLineMonetarySummation>
+            </ram:SpecifiedLineTradeSettlement>
+        </ram:IncludedSupplyChainTradeLineItem>
+        <ram:ApplicableHeaderTradeAgreement>
+            <ram:BuyerReference>AB321</ram:BuyerReference>
+            <ram:SellerTradeParty>
+                <ram:Name>Bei Spiel GmbH</ram:Name>
+                <ram:PostalTradeAddress>
+                    <ram:PostcodeCode>12345</ram:PostcodeCode>
+                    <ram:LineOne>Ecke 12</ram:LineOne>
+                    <ram:CityName>Stadthausen</ram:CityName>
+                    <ram:CountryID>DE</ram:CountryID>
+                </ram:PostalTradeAddress>
+                <ram:SpecifiedTaxRegistration>
+                    <ram:ID schemeID="VA">DE136695976</ram:ID>
+                </ram:SpecifiedTaxRegistration>
+            </ram:SellerTradeParty>
+            <ram:BuyerTradeParty>
+                <ram:ID>2</ram:ID>
+                <ram:Name>Theodor Est</ram:Name>
+                <ram:PostalTradeAddress>
+                    <ram:PostcodeCode>88802</ram:PostcodeCode>
+                    <ram:LineOne>Bahnstr. 42</ram:LineOne>
+                    <ram:CityName>Spielkreis</ram:CityName>
+                    <ram:CountryID>DE</ram:CountryID>
+                </ram:PostalTradeAddress>
+            </ram:BuyerTradeParty>
+        </ram:ApplicableHeaderTradeAgreement>
+        <ram:ApplicableHeaderTradeDelivery>
+            <ram:ActualDeliverySupplyChainEvent>
+                <ram:OccurrenceDateTime>
+                    <udt:DateTimeString format="102">20201110</udt:DateTimeString>
+                </ram:OccurrenceDateTime>
+            </ram:ActualDeliverySupplyChainEvent>
+        </ram:ApplicableHeaderTradeDelivery>
+        <ram:ApplicableHeaderTradeSettlement>
+            <ram:PaymentReference>RE-20201121/508</ram:PaymentReference>
+            <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
+            <ram:SpecifiedTradeSettlementPaymentMeans>
+                <ram:TypeCode>42</ram:TypeCode>
+                <ram:Information>Bank transfer</ram:Information>
+                <ram:PayeePartyCreditorFinancialAccount>
+                    <ram:IBANID>DE88200800000970375700</ram:IBANID>
+                    <ram:AccountName>Max Mustermann</ram:AccountName>
+                </ram:PayeePartyCreditorFinancialAccount>
+                <ram:PayeeSpecifiedCreditorFinancialInstitution>
+                    <ram:BICID>COBADEFFXXX</ram:BICID>
+                </ram:PayeeSpecifiedCreditorFinancialInstitution>
+            </ram:SpecifiedTradeSettlementPaymentMeans>
+            <ram:ApplicableTradeTax>
+                <ram:CalculatedAmount>11.20</ram:CalculatedAmount>
+                <ram:TypeCode>VAT</ram:TypeCode>
+                <ram:BasisAmount>160.00</ram:BasisAmount>
+                <ram:CategoryCode>S</ram:CategoryCode>
+                <ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
+            </ram:ApplicableTradeTax>
+            <ram:ApplicableTradeTax>
+                <ram:CalculatedAmount>63.84</ram:CalculatedAmount>
+                <ram:TypeCode>VAT</ram:TypeCode>
+                <ram:BasisAmount>336.00</ram:BasisAmount>
+                <ram:CategoryCode>S</ram:CategoryCode>
+                <ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
+            </ram:ApplicableTradeTax>
+            <ram:SpecifiedTradePaymentTerms>
+                <ram:Description>Zahlbar ohne Abzug bis 12.12.2020</ram:Description>
+                <ram:DueDateDateTime>
+                    <udt:DateTimeString format="102">20201212</udt:DateTimeString>
+                </ram:DueDateDateTime>
+            </ram:SpecifiedTradePaymentTerms>
+            <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
+                <ram:LineTotalAmount>496.00</ram:LineTotalAmount>
+                <ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
+                <ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
+                <ram:TaxBasisTotalAmount>496.00</ram:TaxBasisTotalAmount>
+                <ram:TaxTotalAmount currencyID="EUR">75.04</ram:TaxTotalAmount>
+                <ram:GrandTotalAmount>571.04</ram:GrandTotalAmount>
+                <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
+                <ram:DuePayableAmount>571.04</ram:DuePayableAmount>
+            </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
+        </ram:ApplicableHeaderTradeSettlement>
+    </rsm:SupplyChainTradeTransaction>
+</rsm:CrossIndustryInvoice>

+ 48 - 0
Source/QuestPDF.ConformanceTests/Resources/zugferd-xmp-metadata.xml

@@ -0,0 +1,48 @@
+<rdf:Description xmlns:fx="urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#" rdf:about="">
+    <fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>
+    <fx:DocumentType>INVOICE</fx:DocumentType>
+    <fx:DocumentFileName>factur-x.xml</fx:DocumentFileName>
+    <fx:Version>1.0</fx:Version>
+</rdf:Description>
+<rdf:Description xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
+                 xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#"
+                 xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
+                 rdf:about="">
+<pdfaExtension:schemas>
+    <rdf:Bag>
+        <rdf:li rdf:parseType="Resource">
+            <pdfaSchema:schema>ZUGFeRD PDFA Extension Schema</pdfaSchema:schema>
+            <pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
+            <pdfaSchema:prefix>fx</pdfaSchema:prefix>
+            <pdfaSchema:property>
+                <rdf:Seq>
+                    <rdf:li rdf:parseType="Resource">
+                        <pdfaProperty:name>DocumentFileName</pdfaProperty:name>
+                        <pdfaProperty:valueType>Text</pdfaProperty:valueType>
+                        <pdfaProperty:category>external</pdfaProperty:category>
+                        <pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
+                    </rdf:li>
+                    <rdf:li rdf:parseType="Resource">
+                        <pdfaProperty:name>DocumentType</pdfaProperty:name>
+                        <pdfaProperty:valueType>Text</pdfaProperty:valueType>
+                        <pdfaProperty:category>external</pdfaProperty:category>
+                        <pdfaProperty:description>INVOICE</pdfaProperty:description>
+                    </rdf:li>
+                    <rdf:li rdf:parseType="Resource">
+                        <pdfaProperty:name>Version</pdfaProperty:name>
+                        <pdfaProperty:valueType>Text</pdfaProperty:valueType>
+                        <pdfaProperty:category>external</pdfaProperty:category>
+                        <pdfaProperty:description>The actual version of the ZUGFeRD XML schema</pdfaProperty:description>
+                    </rdf:li>
+                    <rdf:li rdf:parseType="Resource">
+                        <pdfaProperty:name>ConformanceLevel</pdfaProperty:name>
+                        <pdfaProperty:valueType>Text</pdfaProperty:valueType>
+                        <pdfaProperty:category>external</pdfaProperty:category>
+                        <pdfaProperty:description>The selected ZUGFeRD profile completeness</pdfaProperty:description>
+                    </rdf:li>
+                </rdf:Seq>
+            </pdfaSchema:property>
+        </rdf:li>
+    </rdf:Bag>
+</pdfaExtension:schemas>
+</rdf:Description>

+ 69 - 0
Source/QuestPDF.ConformanceTests/ZugferdTests.cs

@@ -0,0 +1,69 @@
+using QuestPDF.ConformanceTests.TestEngine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.ConformanceTests;
+
+internal class ZugferdTests
+{
+    [Test]
+    public void ZugferdValidation_WithMustang()
+    {
+        var guid = Guid.NewGuid();
+        var invoicePath = Path.Combine(Path.GetTempPath(), $"{guid}.pdf");
+
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.Margin(60);
+
+                    page.Content()
+                        .Text("Conformance Test: ZUGFeRD")
+                        .FontSize(24)
+                        .FontColor(Colors.Blue.Darken2)
+                        .Bold();
+                });
+            })
+            .WithMetadata(new DocumentMetadata
+            {
+                Title = "Conformance Test: ZUGFeRD",
+                Author = "SampleCompany",
+                Subject = "ZUGFeRD Test Document",
+                Language = "en-US"
+            })
+            .WithSettings(new DocumentSettings
+            {
+                PDFA_Conformance = PDFA_Conformance.PDFA_3B
+            })
+            .GeneratePdf(invoicePath);
+        
+        VeraPdfConformanceTestRunner.TestConformance(invoicePath);
+        
+        var zugferdInvoicePath = Path.Combine(Path.GetTempPath(), $"zugferd-{guid}.pdf");
+
+        var facturPath = Path.Combine("Resources", "zugferd-factur-x.xml");
+        var metadataPath = Path.Combine("Resources", "zugferd-xmp-metadata.xml");
+
+        DocumentOperation
+            .LoadFile(invoicePath)
+            .AddAttachment(new DocumentOperation.DocumentAttachment
+            {
+                Key = "factur-zugferd",
+                FilePath = facturPath,
+                AttachmentName = "factur-x.xml",
+                MimeType = "text/xml",
+                Description = "Factur-X Invoice",
+                Relationship = DocumentOperation.DocumentAttachmentRelationship.Source,
+                CreationDate = DateTime.UtcNow,
+                ModificationDate = DateTime.UtcNow
+            })
+            .ExtendMetadata(File.ReadAllText(metadataPath))
+            .Save(zugferdInvoicePath);
+        
+        VeraPdfConformanceTestRunner.TestConformance(zugferdInvoicePath);
+        MustangConformanceTestRunner.TestConformance(zugferdInvoicePath); 
+    }
+}