SkillAgentSearch skills...

ZUV

ZUgferd validator using Verapdf, discontinued because integrated into Mustangproject 2.0

Install / Use

/learn @ZUGFeRD/ZUV
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Merge into Mustang

If you want to help or get the latest version: ZUV has been completely merged into Mustang 2.0.

ZUV

ZUV (ZUgferd+VeraPDF) was the project name of an open-source e-invoice validator for the ZUGFeRD/Factur-X standard.

It checks both PDF/A-3 compliance (based on VeraPDF) and ZUGFeRD version 1 respectively 2/2.1 XMLs for correctness. The XML check is done by validating against the official ZUGFeRD schematron file for v1 and v2.1 and additionally the EN16931 UN/CEFACT SCRDM v16B uncoupled schematron from the CEN.

Build

In the pom.xml directory compile the jar with ./mvnw clean package

To prepare the schematron files they have to be converted to XSLT files, which is done with a XSLT transformation itself. Get Saxon and the XSLT to convert schematron to XSLT and run java -jar ./saxon9he.jar -o:minimum.xsl -s:zugferd2p0_basicwl_minimum.sch ./schematron_to_xslt/iso-schematron-xslt2/iso_svrl_for_xslt1.xsl to do so.

To create a new en16931 run git clone https://github.com/ConnectingEurope/eInvoicing-EN16931.git mvn -f pom-xslt.xml package

Run

To check a file for ZUGFeRD conformance use

java -jar ZUV-0.9.0.jar --action validate -f <filename of ZUGFeRD PDF.pdf>

You can provide either the complete PDF which will be checked for XML and PDF correctness, or just a XML file, which of course will only be checked for XML correctness.

Output

A valid file looks like this

<?xml version="1.0" encoding="UTF-8"?>

<validation filename="Facture_UE_MINIMUM.pdf" datetime="2020-03-23 12:34:54">
  <pdf> 
    <report> 
      <buildInformation> 
        <releaseDetails id="core" version="1.12.1" buildDate="2018-05-08T18:57:00+02:00"/>  
        <releaseDetails id="validation-model" version="1.12.1" buildDate="2018-05-08T20:39:00+02:00"/> 
      </buildInformation>  
      <jobs> 
        <job> 
          <item size="101181"> 
            <name>/Users/jstaerk/workspace/zugferd/helper_files/../foreign_samples/fx/Facture_UE_MINIMUM.pdf</name> 
          </item>  
          <validationReport profileName="PDF/A-3B validation profile" statement="PDF file is compliant with Validation Profile requirements." isCompliant="true"> 
            <details passedRules="123" failedRules="0" passedChecks="11082" failedChecks="0"/> 
          </validationReport>  
          <duration start="1584963295227" finish="1584963296646">00:00:01.419</duration> 
        </job> 
      </jobs>  
      <batchSummary totalJobs="1" failedToParse="0" encrypted="0"> 
        <validationReports compliant="1" nonCompliant="0" failedJobs="0">1</validationReports>  
        <featureReports failedJobs="0">0</featureReports>  
        <repairReports failedJobs="0">0</repairReports>  
        <duration start="1584963294997" finish="1584963296679">00:00:01.682</duration> 
      </batchSummary> 
    </report>  
    <info>
      <signature>Factur/X Python</signature>
      <duration unit="ms">2361</duration>
    </info>
    <summary status="valid"/>
  </pdf>  
  <xml>
    <info>
      <version>2</version>
      <profile>urn:factur-x.eu:1p0:minimum</profile>
      <validator version="0.8.4-RESTRICTED"/>
      <rules>
        <fired>26</fired>
        <failed>0</failed>
      </rules>
      <duration unit="ms">2772</duration>
    </info>
    <summary status="valid"/>
  </xml>
  <summary status="valid"/>
</validation>

Invalid files with errors e.g. in the XML part can look like this

<?xml version="1.0" encoding="UTF-8"?>

<validation filename="Facture_UE_EXTENDED.pdf" datetime="2020-03-23 12:34:43">
  <pdf> 
    <report> 
      <buildInformation> 
        <releaseDetails id="core" version="1.12.1" buildDate="2018-05-08T18:57:00+02:00"/>  
        <releaseDetails id="validation-model" version="1.12.1" buildDate="2018-05-08T20:39:00+02:00"/> 
      </buildInformation>  
      <jobs> 
        <job> 
          <item size="109172"> 
            <name>/Users/jstaerk/workspace/zugferd/helper_files/../foreign_samples/fx/Facture_UE_EXTENDED.pdf</name> 
          </item>  
          <validationReport profileName="PDF/A-3B validation profile" statement="PDF file is compliant with Validation Profile requirements." isCompliant="true"> 
            <details passedRules="123" failedRules="0" passedChecks="11082" failedChecks="0"/> 
          </validationReport>  
          <duration start="1584963283448" finish="1584963284790">00:00:01.342</duration> 
        </job> 
      </jobs>  
      <batchSummary totalJobs="1" failedToParse="0" encrypted="0"> 
        <validationReports compliant="1" nonCompliant="0" failedJobs="0">1</validationReports>  
        <featureReports failedJobs="0">0</featureReports>  
        <repairReports failedJobs="0">0</repairReports>  
        <duration start="1584963283223" finish="1584963284825">00:00:01.602</duration> 
      </batchSummary> 
    </report>  
    <info>
      <signature>Factur/X Python</signature>
      <duration unit="ms">2296</duration>
    </info>
    <summary status="valid"/>
  </pdf>  
  <xml>
    <info>
      <version>2</version>
      <profile>urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended</profile>
      <validator version="0.8.4-RESTRICTED"/>
      <rules>
        <fired>97</fired>
        <failed>1</failed>
      </rules>
      <duration unit="ms">8546</duration>
    </info>
    <messages>
      <error type="4" location="/*[local-name()='CrossIndustryInvoice' and namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100']/*[local-name()='SupplyChainTradeTransaction' and namespace-uri()='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100']/*[local-name()='ApplicableHeaderTradeSettlement' and namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100']/*[local-name()='ApplicableTradeTax' and namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100']/*[local-name()='CategoryCode' and namespace-uri()='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100']" criterion="(/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime/udt:DateTimeString) or (../../ram:BillingSpecifiedPeriod/ram:StartDateTime) or (../../ram:BillingSpecifiedPeriod/ram:EndDateTime)">In an Invoice with a VAT breakdown (BG-23) where the VAT category code (BT-118) is "Intra-community supply" the Actual delivery date (BT-72) or the Invoicing period (BG-14) shall not be blank.</error> 
    </messages>
    <summary status="invalid"/>
  </xml>
  <messages></messages>
  <summary status="invalid"/>
</validation>

Embed

Feel free to embed this into your java software, send me a PR to use it as a library, or exec it and parse it's output to put on the web.

For exec, you might try something like

exec('java -Dfile.encoding=UTF-8 -jar /path/to/ZUV-0.9.0.jar --action validate -f '.escapeshellarg($uploadfile).' 2>/dev/null', $output);
  • Redirecting stderr away (some logging messages might otherwise disturb XML well formedness)
  • Escaping any file names in case you use original file names at all (apart from security concerns please take into account that they might contain spaces)
  • Signal java to use UTF-8 even when otherwise it would not: You might run into trouble with XML files starting with a BOM otherwise and when you exec, keep in mind that you lose all env vars.

License

Permissive Open Source APL2, see LICENSE

Codes

| section | meaning | |---|---| | 1 | file not found | | 2 | additional data schema validation fails | | 3 | xml data not found | | 4 | schematron rule failed | | 5 | file too small | | 6 | VeraPDFException | | 7 | IOException PDF | | 8 | File does not look like PDF nor XML (contains neither %PDF nor <?xml) | | 9 | IOException XML | | 11 | XMP Metadata: ConformanceLevel not found | | 12 | XMP Metadata: ConformanceLevel contains invalid value | | 13 | XMP Metadata: DocumentType not found | | 14 | XMP Metadata: DocumentType invalid | | 15 | XMP Metadata: Version not found | | 16 | XMP Metadata: Version contains invalid value | | 18 | schema validation failed | | 19 | XMP Metadata: DocumentFileName contains invalid value | | 20 | not a pdf | | 21 | XMP Metadata: DocumentFileName not found") | | 22 | generic XML validation exception | | 23 | Not a PDF/A-3 | | 24 | Issues in CEN EN16931 Schematron Check | | 25 | Unsupported profile type | | 26 | No rules matched, XML to minimal? | | 27 | XRechnung schematron validation |

History

see the history file

Architecture

Diagram of the architecture

Monitoring

This is a sample of a logstash config file for the ZUV log format

input {
    # stdin {}
    file {
	path => "/var/log/*.log"
	start_position => "beginning"
	sincedb_path => "/dev/null"
    }
}

filter {
    grok {
	match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} \[main\] %{WORD:loglevel}  ZUV.ZUGFeRDValidator - Parsed PDF:%{WORD:pdfvalidity} XML:%{WORD:xmlvalidity} Signature:%{WORD:signature} Checksum:%{WORD:checksum} Profile:%{DATA:profile} Version:%{WORD:version} Took:%{NUMBER:parseTime}" }
    }

    date {
	match => [ "timestamp" , "yyyy-MM-dd HH:mm:ss.SSS" ]
    }

}

output {
    # stdout {}
    elasticsearch {
	hosts => ["138.201.160.93:9200"]
    }
}

Authors

Jochen Staerk "Mustangproject Chief ZUGFeRD amatuer" jochen@zugferd.org

View on GitHub
GitHub Stars30
CategoryDevelopment
Updated4mo ago
Forks7

Languages

XSLT

Security Score

92/100

Audited on Nov 26, 2025

No findings