Merge branch 'temp_for_upstream_merge_of_PR4_08_2019'

This is necessary because the automatic rebase of pull request
https://github.com/seeburger-ag/PDFA3/pull/4
did not work. Merge is done via command line as suggested by GitHub.
This commit is contained in:
Ivan Vaklinov
2019-08-06 14:00:56 +03:00
28 changed files with 3927 additions and 272 deletions

View File

@@ -1,10 +1,27 @@
1.7.3
=====
2019-08-01
- #105 does not build in windows
- #99 BOMs confuse parser
- updated javadoc
- fixed #104 nullpointerex when specifying no parameter
- PR #112 complete profile options for v1
- access to TotalPrepaidAmount in ZF v1 #118
- have unit tests for nDigitFormat #23
1.7.2
=====
2019-07-08
Support BuyerReference (r+w), as well as SpecifiedLegalOrganization (w) and DefinedTradeContact (w)
use dom4j to format output xml document
corrected some exception logging glitches
upgrade PDFBox to 2.0.15+
extraction to use proper filename instead of alias #98
NullPointerException in ZUGFeRDImporter.extractLowLevel #96
Removed Bankleitzahl from ZF2
1.7.1

27
pom.xml
View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject.ZUGFeRD</groupId>
<artifactId>mustang</artifactId>
<version>1.7.2-SNAPSHOT</version>
<version>1.7.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Mustang</name>
<description>The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs
@@ -14,7 +13,7 @@
<connection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</connection>
<developerConnection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</developerConnection>
<url>https://github.com/ZUGFeRD/mustangproject</url>
<tag>mustang-1.5.2</tag>
<tag>mustang-1.7.3</tag>
</scm>
<repositories>
<repository><!-- for jargs -->
@@ -66,15 +65,26 @@
<artifactId>jaxb-runtime</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>preflight</artifactId>
<version>2.0.12</version>
<version>[2.0.15,)</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.12</version>
<version>[2.0.15,)</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
@@ -203,8 +213,7 @@
<version>2.4.3</version>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.mustangproject.toecount.Toecount</mainClass>
</transformer>
</transformers>
@@ -279,7 +288,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.3.1</version>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>

View File

@@ -18,7 +18,7 @@
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
public class CustomXMLProvider implements IXMLProvider {
public class CustomXMLProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
@@ -48,4 +48,14 @@ public class CustomXMLProvider implements IXMLProvider {
}
@Override
public String getProfile() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setProfile(ZUGFeRDConformanceLevel level) {
// TODO Auto-generated method stub
}
}

View File

@@ -22,6 +22,13 @@ import java.io.IOException;
import java.io.InputStream;
public interface IExporterFactory {
/**
* factory: loads a PDF file and returns an appropriate exporter
*
* @param pdfFilename binary of a PDF/A1 compliant document
* @return the generated exporter
* @throws IOException if anything is wrong with filename
*/
public ZUGFeRDExporter load(String pdfFilename) throws IOException;
/**
@@ -29,6 +36,8 @@ public interface IExporterFactory {
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfBinary binary of a PDF/A1 compliant document
* @return the generated exporter
* @throws IOException (should not happen at all)
*/
public ZUGFeRDExporter load(byte[] pdfBinary) throws IOException;
@@ -37,7 +46,8 @@ public interface IExporterFactory {
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfSource source to read a PDF/A1 compliant document from
* @throws IOException
* @throws IOException if anything is wrong with inputstream
* @return the generated ZUGFeRDExporter
*/
public ZUGFeRDExporter load(InputStream pdfSource) throws IOException;

View File

@@ -22,4 +22,6 @@ public interface IProfileProvider {
public String getProfile();
public void setProfile(ZUGFeRDConformanceLevel level);
}

View File

@@ -19,11 +19,11 @@
package org.mustangproject.ZUGFeRD;
/**
* Mustangproject's ZUGFeRD implementation Neccessary interface for ZUGFeRD exporter Licensed under the APLv2
* Mustangproject's ZUGFeRD implementation neccessary interface for ZUGFeRD exporter Licensed under the APLv2
*
* @author jstaerk
* @version 1.2.0
* @date 2014-05-10
* dated 2014-05-10
*/
@@ -38,6 +38,24 @@ public interface IZUGFeRDExportableContact {
return null;
}
/**
* customer global identification assigned by the seller
*
* @return customer identification
*/
default String getGlobalID() {
return null;
}
/**
* customer global identification scheme
*
* @return customer identification
*/
default String getGlobalIDScheme() {
return null;
}
/**
* First and last name of the recipient

View File

@@ -27,7 +27,7 @@ import java.math.BigDecimal;
*
* @author jstaerk
* @version 1.2.0
* @date 2014-05-10
* dated 2014-05-10
*/
public interface IZUGFeRDExportableProduct {

View File

@@ -29,10 +29,19 @@ package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import java.math.BigDecimal;
import java.util.Date;
public interface IZUGFeRDExportableTransaction {
/**
* appears in /rsm:CrossIndustryDocument/rsm:HeaderExchangedDocument/ram:Name
* @return Name of document
*/
default String getDocumentName() {
return "RECHNUNG";
}
/**
*
*
@@ -64,7 +73,7 @@ public interface IZUGFeRDExportableTransaction {
/**
* this should be the full sender institution name, details, manager and tax registration. It is one of the few functions which may return null. e.g.
* <p/>
* <p>
* Lieferant GmbH Lieferantenstraße 20 80333 München Deutschland Geschäftsführer: Hans Muster Handelsregisternummer: H A 123
*
* @return null or full sender institution name, details, manager and tax registration
@@ -115,6 +124,8 @@ public interface IZUGFeRDExportableTransaction {
/**
* the creditors payment informations
*
* @return an array of IZUGFeRDTradeSettlementPayment
*/
IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment();
@@ -236,4 +247,23 @@ public interface IZUGFeRDExportableTransaction {
return null;
}
/**
* get the ID of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
* @return the ID of the document
*/
default String getBuyerOrderReferencedDocumentID() { return null; }
/**
* get the issue timestamp of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
* @return the IssueDateTime in format CCYY-MM-DDTHH:MM:SS
*/
default String getBuyerOrderReferencedDocumentIssueDateTime() { return null; }
/**
* get the TotalPrepaidAmount located in SpecifiedTradeSettlementMonetarySummation (v1) or
* SpecifiedTradeSettlementHeaderMonetarySummation (v2)
* @return the total sum (incl. VAT) of prepayments, i.e. the difference between GrandTotalAmount
* and DuePayableAmount
*/
default BigDecimal getTotalPrepaidAmount() { return BigDecimal.ZERO; }
}

View File

@@ -27,20 +27,20 @@ import java.math.BigDecimal;
*
* @author jstaerk
* @version 1.2.0
* @date 2015-10-29
* dated 2015-10-29
*/
public class VATAmount {
public VATAmount(BigDecimal basis, BigDecimal calculated, String documentCode) {
public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) {
super();
this.basis = basis;
this.calculated = calculated;
this.documentCode = documentCode;
this.categoryCode = categoryCode;
}
BigDecimal basis, calculated;
String documentCode;
String categoryCode;
public BigDecimal getBasis() {
return basis;
@@ -58,20 +58,39 @@ public class VATAmount {
this.calculated = calculated;
}
/**
*
* @deprecated Use {@link #getCategoryCode() instead}
* @return String with category code
*/
@Deprecated
public String getDocumentCode() {
return documentCode;
return categoryCode;
}
/**
* @param documentCode as String
* @deprecated Use {@link #setCategoryCode(String)} instead
*/
@Deprecated
public void setDocumentCode(String documentCode) {
this.documentCode = documentCode;
this.categoryCode = documentCode;
}
public String getCategoryCode() {
return categoryCode;
}
public void setCategoryCode(String categoryCode) {
this.categoryCode = categoryCode;
}
public VATAmount add(VATAmount v) {
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.documentCode);
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode);
}
public VATAmount subtract(VATAmount v) {
return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.documentCode);
return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.categoryCode);
}
}

View File

@@ -31,8 +31,13 @@ import org.apache.xmpbox.schema.XMPSchema;
public class XMPSchemaZugferd extends XMPSchema {
/**
/***
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file
* @param metadata the xmp to be added to
* @param conformanceLevel e.g. conformanceLevel.EN16931
* @param URN the xml URI for the XMP
* @param prefix the xml namespace prefix for the XMP, zf for ZUGFeRD, fx for Factur-X
* @param filename the filename of the invoice
*/
public XMPSchemaZugferd(XMPMetadata metadata, ZUGFeRDConformanceLevel conformanceLevel, String URN, String prefix, String filename) {
super(metadata, URN, prefix, "ZUGFeRD Schema");

View File

@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import org.mustangproject.ZUGFeRD.model.ZFNamespacePrefixMapper;
import javax.xml.bind.JAXBContext;
@@ -27,13 +28,27 @@ import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.ByteArrayOutputStream;
public class ZUGFeRD1PullProvider implements IXMLProvider {
public class ZUGFeRD1PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
private Marshaller marshaller;
private boolean isTest;
private ZUGFeRDConformanceLevel level;
public void setProfile(ZUGFeRDConformanceLevel level) {
this.level = level;
}
public String getProfile() {
switch (level) {
case BASIC: return DocumentContextParameterTypeConstants.BASIC;
case COMFORT: return DocumentContextParameterTypeConstants.COMFORT;
default: return DocumentContextParameterTypeConstants.EXTENDED;
}
}
/**
@@ -59,7 +74,7 @@ public class ZUGFeRD1PullProvider implements IXMLProvider {
private String createZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) {
JAXBElement<CrossIndustryDocumentType> jaxElement =
new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).convertToModel();
new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).withProfile(getProfile()).convertToModel();
try {
return marshalJaxToXMLString(jaxElement);

View File

@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
@@ -70,6 +71,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
private IZUGFeRDExportableTransaction trans;
private ZUGFeRDConformanceLevel level;
public void setProfile(ZUGFeRDConformanceLevel level) {
this.level = level;
}
/**
* enables the flag to indicate a test invoice in the XML structure
@@ -77,7 +83,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
public void setTest() {
}
private String nDigitFormat(BigDecimal value, int scale) {
public static String nDigitFormat(BigDecimal value, int scale) {
/*
* I needed 123,45, locale independent.I tried
* NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale
@@ -92,44 +98,25 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
* http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html in the
* end I decided to calculate myself and take eur+sparator+cents
*
* This function will cut off, i.e. floor() subcent values Tests:
* System.err.println(utils.currencyFormat(new BigDecimal(0),
* ".")+"\n"+utils.currencyFormat(new BigDecimal("-1.10"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.1"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.01"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3489"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3419"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("12"), ","));
*
* results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00
*/
value = value.setScale(scale, BigDecimal.ROUND_HALF_UP); // first, round so that e.g.
// 1.189999999999999946709294817992486059665679931640625
// becomes 1.19
char[] repeat = new char[scale];
Arrays.fill(repeat, '0');
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
otherSymbols.setDecimalSeparator('.');
DecimalFormat dec = new DecimalFormat("0." + new String(repeat), otherSymbols);
return dec.format(value);
return value.setScale(scale, RoundingMode.HALF_UP).toPlainString();
}
private String vatFormat(BigDecimal value) {
return nDigitFormat(value, 2);
return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String currencyFormat(BigDecimal value) {
return nDigitFormat(value, 2);
return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String priceFormat(BigDecimal value) {
return nDigitFormat(value, 4);
return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
private String quantityFormat(BigDecimal value) {
return nDigitFormat(value, 4);
return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
@Override
@@ -158,6 +145,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
}
private BigDecimal getTotalPrepaid() {
return trans.getTotalPrepaidAmount();
}
private BigDecimal getTotalGross() {
BigDecimal res = getTotal();
@@ -214,9 +205,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
String senderReg = "";
if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
senderReg = "" + "<ram:IncludedCINote>\n" + " <ram:Content>\n"
senderReg = "" + "<ram:IncludedNote>\n" + " <ram:Content>\n"
+ trans.getOwnOrganisationFullPlaintextInfo() + " </ram:Content>\n"
+ "<ram:SubjectCode>REG</ram:SubjectCode>\n" + "</ram:IncludedCINote>\n";
+ "<ram:SubjectCode>REG</ram:SubjectCode>\n" + "</ram:IncludedNote>\n";
}
@@ -378,11 +369,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " <ram:LineOne>" + trans.getRecipient().getStreet() + "</ram:LineOne>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:CityName>" + trans.getRecipient().getLocation() + "</ram:CityName>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:CountryID>" + trans.getRecipient().getCountry() + "</ram:CountryID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:PostalTradeAddress>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " </ram:PostalTradeAddress>\n"; //$NON-NLS-1$
if (trans.getRecipient().getVATID() != null) {
xml += " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"VA\">" + trans.getRecipient().getVATID() + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " </ram:BuyerTradeParty>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTaxRegistration>\n"; //$NON-NLS-1$
}
xml += " </ram:BuyerTradeParty>\n" //$NON-NLS-1$
// + " <BuyerOrderReferencedDocument>\n"
// + " <IssueDateTime format=\"102\">20130301</IssueDateTime>\n"
// + " <ID>2013-471331</ID>\n"
@@ -412,8 +405,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " <ram:BICID>" + payment.getOwnBIC() + "</ram:BICID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:GermanBankleitzahlID>" + payment.getOwnBLZ() //$NON-NLS-1$
+ "</ram:GermanBankleitzahlID>\n" //$NON-NLS-1$
// + " <ram:Name>"+trans.getOwnBankName()+"</ram:Name>\n" //$NON-NLS-1$
// //$NON-NLS-2$
+ " </ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
@@ -490,8 +481,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " <ram:GrandTotalAmount>" + currencyFormat(getTotalGross()) + "</ram:GrandTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// //
// currencyID=\"EUR\"
// + " <TotalPrepaidAmount currencyID=\"EUR\">0.00</TotalPrepaidAmount>\n"
+ " <ram:DuePayableAmount>" + currencyFormat(getTotalGross()) + "</ram:DuePayableAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:TotalPrepaidAmount currencyID=\"EUR\">" + currencyFormat(getTotalPrepaid()) + "</ram:TotalPrepaidAmount>\n"
+ " <ram:DuePayableAmount>" + currencyFormat(getTotalGross().subtract(getTotalPrepaid())) + "</ram:DuePayableAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// //
// currencyID=\"EUR\"
+ " </ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$

View File

@@ -195,15 +195,16 @@ public class ZUGFeRDExporter implements Closeable {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
* <p/>
*
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
* <p/>
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p/>
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
* @param newLevel "A", "U" or "B"
*
* @deprecated Use {@link ZUGFeRDExporterFromA1Factory} instead
*/
@@ -217,16 +218,17 @@ public class ZUGFeRDExporter implements Closeable {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
* <p/>
* <p>
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
* <p/>
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p/>
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
*
* @param newLevel "A", "U" or "B"
* @deprecated Use
* {@link #setConformanceLevel(org.mustangproject.ZUGFeRD.PDFAConformanceLevel)}
* instead
@@ -251,7 +253,10 @@ public class ZUGFeRDExporter implements Closeable {
ignoreA1Errors = true;
}
/**
/***
* load from a pdf which is already A/3
* @param filename the PDF
* @throws IOException if anything is wrong with filename
* @deprecated Use the factory method {@link #createFromPDFA3(String)} instead
*/
@Deprecated
@@ -259,19 +264,34 @@ public class ZUGFeRDExporter implements Closeable {
doc = PDDocument.load(new File(filename));
}
/***
* factory create a ZUGFeRD exporter for a PDF/A-3 pdf file
* @param filename of the pdf
* @return the created ZUGFeRDExporter
* @throws IOException if something is wrong with filename
*/
public static ZUGFeRDExporter createFromPDFA3(String filename) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(new File(filename)));
}
/**
* @deprecated Use the factory method {@link #createFromPDFA3(InputStream)}
* instead
* load from a pdf which is already A/3 from filestream
* @param file InputStream to load pdf from
* @deprecated Use the factory method {@link #createFromPDFA3(InputStream)} instead
* @throws IOException in case anything is wrong with file
*
*/
@Deprecated
public void loadPDFA3(InputStream file) throws IOException {
doc = PDDocument.load(file);
}
/***
* factory create a ZUGFeRD exporter for a PDF/A-3 inputstream
* @param pdfSource the InputStream of the pdf
* @return the created ZUGFeRDExporter
* @throws IOException (should not happen)
*/
public static ZUGFeRDExporter createFromPDFA3(InputStream pdfSource) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(pdfSource));
}
@@ -279,7 +299,13 @@ public class ZUGFeRDExporter implements Closeable {
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param filename the pdf
* @param producer the name of the xmp producer (app)
* @param creator the name of the xmp creator (user)
* @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
* @return the created PDFbox PDDocumentCatalog
* @throws IOException if anything is wrong with file
* @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
@@ -291,7 +317,18 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog();
}
/**
/***
*
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
* @param file the InputStream of the pdf
* @param producer the name of the xmp producer (app)
* @param creator the name of the xmp creator (user)
* @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
* @return the created PDFbox PDDocumentCatalog
* @throws IOException if anything is wrong with file
* @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
@@ -303,6 +340,7 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog();
}
private IExporterFactory createPDFA1Factory() {
ZUGFeRDExporterFromA1Factory factory = new ZUGFeRDExporterFromA1Factory();
if (ignoreA1Errors) {
@@ -325,10 +363,11 @@ public class ZUGFeRDExporter implements Closeable {
* populate the XML. This parameter may be null, if so the XML data
* should hav ebeen set via
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF
*/
public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) throws IOException {
prepareDocument();
((IProfileProvider) xmlProvider).setProfile(profile);
xmlProvider.generateXML(trans);
String filename = getFilenameForVersion(ZFVersion);
PDFAttachGenericFile(doc, filename, "Alternative",
@@ -339,6 +378,11 @@ public class ZUGFeRDExporter implements Closeable {
}
}
/***
* Perform the final export to a now ZUGFeRD-enriched PDF file
* @param ZUGFeRDfilename the pdf file name
* @throws IOException if anything is wrong in the target location
*/
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) {
prepareDocument();
@@ -353,6 +397,12 @@ public class ZUGFeRDExporter implements Closeable {
}
}
/***
* Perform the final export to a now ZUGFeRD-enriched PDF file as OutputStream
* @param output the OutputStream
* @throws IOException if anything is wrong in the OutputStream
*/
public void export(OutputStream output) throws IOException {
if (!documentPrepared) {
prepareDocument();
@@ -376,7 +426,7 @@ public class ZUGFeRDExporter implements Closeable {
* @param description Human-readable description of the file content
* @param subType type of the data e.g. could be "text/xml" - mime like
* @param data the binary data of the file/attachment
* @throws java.io.IOException
* @throws java.io.IOException if anything is wrong with filename
*/
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException {
@@ -446,6 +496,7 @@ public class ZUGFeRDExporter implements Closeable {
* API or component.
*
* @param zugferdData XML data to be set as a byte array (XML file in raw form).
* @throws IOException (should not happen)
*/
public void setZUGFeRDXMLData(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider();
@@ -495,7 +546,7 @@ public class ZUGFeRDExporter implements Closeable {
* neccessary PDF/A schema extension description to be able to add this
* information to RDF
*
* @param metadata
* @param metadata the PDFbox XMPMetadata object
*/
protected void addXMP(XMPMetadata metadata) {
@@ -607,7 +658,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the human author (use factory method instead)
*
* @param creator
* @param creator the (human) name who created the PDF
*/
@Deprecated
public void setCreator(String creator) {
@@ -617,7 +668,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the CreatorTool attribute for the PDF
*
* @param creator
* @param creatorTool the application which created the PDF
*/
protected void setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool;
@@ -626,7 +677,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the authoring software (use factory method instead)
*
* @param producer
* @param producer the authoring software
*/
@Deprecated
public void setProducer(String producer) {

View File

@@ -173,14 +173,14 @@ public class ZUGFeRDExporterFromA3Factory implements IExporterFactory {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
* <p/>
* <p>
* PDF/A-3 has three coformance levels, called "A", "U" and "B".
* <p/>
* <p>
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
* <p/>
* <p>
* Feel free to pass "A" as new level if you know what you are doing :-)
*/
public IExporterFactory setConformanceLevel(PDFAConformanceLevel newLevel) {

View File

@@ -46,6 +46,9 @@ import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -65,12 +68,12 @@ public class ZUGFeRDImporter {
*/
private byte[] rawXML = null;
private String xmpString = null; // XMP metadata
private Document document;
public ZUGFeRDImporter(String pdfFilename) {
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pdfFilename));
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
extractLowLevel(bis);
bis.close();
} catch (IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
@@ -125,7 +128,10 @@ public class ZUGFeRDImporter {
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException {
for (String filename : names.keySet()) {
for (String alias : names.keySet()) {
PDComplexFileSpecification fileSpec = names.get(alias);
String filename=fileSpec.getFilename();
/**
* currently (in the release candidate of version 1) only one attached file with
* the name ZUGFeRD-invoice.xml is allowed
@@ -133,7 +139,6 @@ public class ZUGFeRDImporter {
if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml"))) { //$NON-NLS-1$
containsMeta = true;
PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
// String embeddedFilename = filePath + filename;
// File file = new File(filePath + filename);
@@ -142,15 +147,13 @@ public class ZUGFeRDImporter {
// ByteArrayOutputStream();
// FileOutputStream fos = new FileOutputStream(file);
rawXML = embeddedFile.toByteArray();
setMeta(new String(rawXML));
setRawXML(embeddedFile.toByteArray());
// fos.write(embeddedFile.getByteArray());
// fos.close();
}
if (filename.startsWith("additional_data")) {
PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
additionalXMLs.put(filename, embeddedFile.toByteArray());
@@ -173,13 +176,52 @@ public class ZUGFeRDImporter {
System.err.println(output);
}
private Document getDocument() throws ParserConfigurationException, IOException, SAXException, TransformerException {
private Document getDocument() { return document; }
public void setRawXML(byte[] rawXML) throws IOException {
this.rawXML = rawXML;
try {
setDocument();
} catch (ParserConfigurationException | SAXException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
private void setDocument() throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
xmlFact.setNamespaceAware(false);
DocumentBuilder builder = xmlFact.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(rawXML));
//prettyPrint(doc);
return doc;
ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
is.skip(guessBOMSize(is));
document = builder.parse(is);
}
/**
* Skips over a BOM at the beginning of the given ByteArrayInputStream, if one exists.
* @param is the ByteArrayInputStream used
* @throws IOException
* @see <a href="https://www.w3.org/TR/xml/#sec-guessing">Autodetection of Character Encodings</a>
*/
private int guessBOMSize(ByteArrayInputStream is) throws IOException {
byte[] pad = new byte[4];
is.read(pad);
is.reset();
int test2 = ((pad[0] & 0xFF) << 8) | (pad[1] & 0xFF);
int test3 = ((test2 & 0xFFFF) << 8) | (pad[2] & 0xFF);
int test4 = ((test3 & 0xFFFFFF) << 8) | (pad[3] & 0xFF);
//
if (test4 == 0x0000FEFF || test4 == 0xFFFE0000 || test4 == 0x0000FFFE || test4 == 0xFEFF0000) {
// UCS-4: BOM takes 4 bytes
return 4;
} else if (test3 == 0xEFBBFF) {
// UTF-8: BOM takes 3 bytes
return 3;
} else if (test2 == 0xFEFF || test2 == 0xFFFE) {
// UTF-16: BOM takes 2 bytes
return 2;
}
return 0;
}
private String extractString(String xpathStr) {
@@ -192,10 +234,7 @@ public class ZUGFeRDImporter {
XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath();
result = xpath.evaluate(xpathStr, document);
} catch (ParserConfigurationException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
} catch (IOException | SAXException | TransformerException | XPathExpressionException e) {
} catch (XPathExpressionException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
@@ -300,8 +339,8 @@ public class ZUGFeRDImporter {
/**
* @param meta raw XML to be set
*/
public void setMeta(String meta) {
this.rawXML = meta.getBytes();
public void setMeta(String meta) throws IOException {
setRawXML(meta.getBytes());
}
/**
@@ -346,6 +385,7 @@ public class ZUGFeRDImporter {
/**
* Returns the raw XML data as extracted from the ZUGFeRD PDF file.
* @return the raw ZUGFeRD XML data
*/
public byte[] getRawXML() {
return rawXML;
@@ -369,7 +409,7 @@ public class ZUGFeRDImporter {
static String convertStreamToString(java.io.InputStream is) {
// source https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java referring to
// https://community.oracle.com/blogs/pat/2004/10/23/stupid-scanner-tricks
Scanner s = new Scanner(is).useDelimiter("\\A");
Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}

View File

@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
@@ -30,51 +31,7 @@ import java.util.List;
import javax.xml.bind.JAXBElement;
import org.mustangproject.ZUGFeRD.model.AmountType;
import org.mustangproject.ZUGFeRD.model.CodeType;
import org.mustangproject.ZUGFeRD.model.CountryIDType;
import org.mustangproject.ZUGFeRD.model.CreditorFinancialAccountType;
import org.mustangproject.ZUGFeRD.model.CreditorFinancialInstitutionType;
import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
import org.mustangproject.ZUGFeRD.model.DateTimeType;
import org.mustangproject.ZUGFeRD.model.DateTimeTypeConstants;
import org.mustangproject.ZUGFeRD.model.DocumentCodeType;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterType;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import org.mustangproject.ZUGFeRD.model.DocumentLineDocumentType;
import org.mustangproject.ZUGFeRD.model.ExchangedDocumentContextType;
import org.mustangproject.ZUGFeRD.model.ExchangedDocumentType;
import org.mustangproject.ZUGFeRD.model.IDType;
import org.mustangproject.ZUGFeRD.model.IndicatorType;
import org.mustangproject.ZUGFeRD.model.LogisticsServiceChargeType;
import org.mustangproject.ZUGFeRD.model.NoteType;
import org.mustangproject.ZUGFeRD.model.NoteTypeConstants;
import org.mustangproject.ZUGFeRD.model.ObjectFactory;
import org.mustangproject.ZUGFeRD.model.PaymentMeansCodeType;
import org.mustangproject.ZUGFeRD.model.PaymentMeansCodeTypeConstants;
import org.mustangproject.ZUGFeRD.model.PercentType;
import org.mustangproject.ZUGFeRD.model.QuantityType;
import org.mustangproject.ZUGFeRD.model.SupplyChainEventType;
import org.mustangproject.ZUGFeRD.model.SupplyChainTradeAgreementType;
import org.mustangproject.ZUGFeRD.model.SupplyChainTradeDeliveryType;
import org.mustangproject.ZUGFeRD.model.SupplyChainTradeLineItemType;
import org.mustangproject.ZUGFeRD.model.SupplyChainTradeSettlementType;
import org.mustangproject.ZUGFeRD.model.SupplyChainTradeTransactionType;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeType;
import org.mustangproject.ZUGFeRD.model.TaxRegistrationType;
import org.mustangproject.ZUGFeRD.model.TaxRegistrationTypeConstants;
import org.mustangproject.ZUGFeRD.model.TaxTypeCodeType;
import org.mustangproject.ZUGFeRD.model.TaxTypeCodeTypeConstants;
import org.mustangproject.ZUGFeRD.model.TextType;
import org.mustangproject.ZUGFeRD.model.TradeAddressType;
import org.mustangproject.ZUGFeRD.model.TradeAllowanceChargeType;
import org.mustangproject.ZUGFeRD.model.TradePartyType;
import org.mustangproject.ZUGFeRD.model.TradePaymentTermsType;
import org.mustangproject.ZUGFeRD.model.TradePriceType;
import org.mustangproject.ZUGFeRD.model.TradeProductType;
import org.mustangproject.ZUGFeRD.model.TradeSettlementMonetarySummationType;
import org.mustangproject.ZUGFeRD.model.TradeSettlementPaymentMeansType;
import org.mustangproject.ZUGFeRD.model.TradeTaxType;
import org.mustangproject.ZUGFeRD.model.*;
class ZUGFeRDTransactionModelConverter {
private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd");
@@ -84,6 +41,7 @@ class ZUGFeRDTransactionModelConverter {
private final Totals totals;
private boolean isTest;
private String currency = "EUR";
private String profile;
ZUGFeRDTransactionModelConverter(IZUGFeRDExportableTransaction trans) {
@@ -113,7 +71,7 @@ class ZUGFeRDTransactionModelConverter {
DocumentContextParameterType contextParameter = xmlFactory
.createDocumentContextParameterType();
IDType idType = xmlFactory.createIDType();
idType.setValue(DocumentContextParameterTypeConstants.EXTENDED);
idType.setValue(profile);
contextParameter.setID(idType);
context.getGuidelineSpecifiedDocumentContextParameter().add(
contextParameter);
@@ -149,7 +107,7 @@ class ZUGFeRDTransactionModelConverter {
document.setTypeCode(documentCodeType);
TextType name = xmlFactory.createTextType();
name.setValue("RECHNUNG");
name.setValue(trans.getDocumentName());
document.getName().add(name);
if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
@@ -167,7 +125,7 @@ class ZUGFeRDTransactionModelConverter {
if (trans.getReferenceNumber() != null && !"".equals(trans.getReferenceNumber())) {
NoteType referenceInfo = xmlFactory.createNoteType();
TextType referenceInfoContent = xmlFactory.createTextType();
referenceInfoContent.setValue("Ursprungsbeleg: " + trans.getReferenceNumber());
referenceInfoContent.setValue(trans.getReferenceNumber());
referenceInfo.getContent().add(referenceInfoContent);
document.getIncludedNote().add(referenceInfo);
}
@@ -199,6 +157,15 @@ class ZUGFeRDTransactionModelConverter {
tradeAgreement.setBuyerTradeParty(getBuyer());
tradeAgreement.setSellerTradeParty(getSeller());
if (trans.getBuyerOrderReferencedDocumentID() != null) {
ReferencedDocumentType refdoc = xmlFactory.createReferencedDocumentType();
IDType id = xmlFactory.createIDType();
id.setValue(trans.getBuyerOrderReferencedDocumentID());
refdoc.getID().add(id);
refdoc.setIssueDateTime(trans.getBuyerOrderReferencedDocumentIssueDateTime());
tradeAgreement.getBuyerOrderReferencedDocument().add(refdoc);
}
return tradeAgreement;
}
@@ -212,6 +179,12 @@ class ZUGFeRDTransactionModelConverter {
buyerID.setValue(trans.getRecipient().getID());
buyerTradeParty.getID().add(buyerID);
}
if (trans.getRecipient().getGlobalID() != null) {
IDType globalID = xmlFactory.createIDType();
globalID.setValue(trans.getRecipient().getGlobalID());
globalID.setSchemeID(trans.getRecipient().getGlobalIDScheme());
buyerTradeParty.getGlobalID().add(globalID);
}
TextType buyerName = xmlFactory.createTextType();
buyerName.setValue(trans.getRecipient().getName());
@@ -422,7 +395,7 @@ class ZUGFeRDTransactionModelConverter {
TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType();
VATAmount vatAmount = VATPercentAmountMap.get(currentTaxPercent);
taxCategoryCode.setValue(vatAmount.getDocumentCode());
taxCategoryCode.setValue(vatAmount.getCategoryCode());
tradeTax.setCategoryCode(taxCategoryCode);
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
@@ -686,9 +659,14 @@ class ZUGFeRDTransactionModelConverter {
grandTotalAmount.setValue(currencyFormat(totals.getTotalGross()));
monetarySummation.getGrandTotalAmount().add(grandTotalAmount);
AmountType totalPrepaidAmount = xmlFactory.createAmountType();
totalPrepaidAmount.setCurrencyID(currency);
totalPrepaidAmount.setValue(currencyFormat(trans.getTotalPrepaidAmount()));
monetarySummation.getTotalPrepaidAmount().add(totalPrepaidAmount);
AmountType duePayableAmount = xmlFactory.createAmountType();
duePayableAmount.setCurrencyID(currency);
duePayableAmount.setValue(currencyFormat(totals.getTotalGross()));
duePayableAmount.setValue(currencyFormat(totals.getTotalGross().subtract(trans.getTotalPrepaidAmount())));
monetarySummation.getDuePayableAmount().add(duePayableAmount);
return monetarySummation;
@@ -843,69 +821,25 @@ class ZUGFeRDTransactionModelConverter {
private BigDecimal vatFormat(BigDecimal value) {
return nDigitFormat(value, 2);
return value.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal currencyFormat(BigDecimal value) {
return nDigitFormat(value, 2);
return value.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal priceFormat(BigDecimal value) {
return nDigitFormat(value, 4);
return value.setScale(4, RoundingMode.HALF_UP);
}
private BigDecimal quantityFormat(BigDecimal value) {
return nDigitFormat(value, 4);
return value.setScale(4, RoundingMode.HALF_UP);
}
private BigDecimal nDigitFormat(BigDecimal value, int scale) {
/*
* I needed 123,45, locale independent.I tried
* NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is
* locale specific.I also tried DecimalFormat df = new DecimalFormat(
* "0,00" ); df.setDecimalSeparatorAlwaysShown(true);
* df.setGroupingUsed(false); DecimalFormatSymbols symbols = new
* DecimalFormatSymbols(); symbols.setDecimalSeparator(',');
* symbols.setGroupingSeparator(' ');
* df.setDecimalFormatSymbols(symbols);
*
* but that would not switch off grouping. Although I liked very much
* the (incomplete) "BNF diagram" in
* http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
* in the end I decided to calculate myself and take eur+sparator+cents
*
* This function will cut off, i.e. floor() subcent values Tests:
* System.err.println(utils.currencyFormat(new BigDecimal(0),
* ".")+"\n"+utils.currencyFormat(new BigDecimal("-1.10"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.1"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.01"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3489"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3419"),
* ",")+"\n"+utils.currencyFormat(new BigDecimal("12"), ","));
*
* results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00
*/
value = value.setScale(scale, BigDecimal.ROUND_HALF_UP); // first, round
// so that
// e.g.
// 1.189999999999999946709294817992486059665679931640625
// becomes
// 1.19
char[] repeat = new char[scale];
Arrays.fill(repeat, '0');
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
otherSymbols.setDecimalSeparator('.');
DecimalFormat dec = new DecimalFormat("0." + new String(repeat),
otherSymbols);
return new BigDecimal(dec.format(value));
}
/**
* which taxes have been used with which amounts in this transaction, empty for no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable to 19% VAT
@@ -924,7 +858,7 @@ class ZUGFeRDTransactionModelConverter {
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalc lc = new LineCalc(currentItem);
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), trans.getDocumentCode());
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), lc.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -958,7 +892,7 @@ class ZUGFeRDTransactionModelConverter {
VATAmount itemVATAmount = new VATAmount(
logisticsServiceCharge.getTotalAmount(),
logisticsServiceCharge.getTotalAmount()
.multiply(percent).divide(new BigDecimal(100)), trans.getDocumentCode());
.multiply(percent).divide(new BigDecimal(100)), logisticsServiceCharge.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -973,7 +907,7 @@ class ZUGFeRDTransactionModelConverter {
BigDecimal percent = charge.getTaxPercent();
VATAmount itemVATAmount = new VATAmount(
charge.getTotalAmount(), charge.getTotalAmount()
.multiply(percent).divide(new BigDecimal(100)), trans.getDocumentCode());
.multiply(percent).divide(new BigDecimal(100)), charge.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -992,12 +926,18 @@ class ZUGFeRDTransactionModelConverter {
return this;
}
public ZUGFeRDTransactionModelConverter withProfile(String profile) {
this.profile = profile;
return this;
}
private class LineCalc {
private BigDecimal totalGross;
private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount;
private BigDecimal itemNetAmount;
private String categoryCode;
public LineCalc(IZUGFeRDExportableItem currentItem) {
@@ -1040,6 +980,7 @@ class ZUGFeRDTransactionModelConverter {
.add(totalCharge)
.divide(currentItem.getQuantity(), 4,
BigDecimal.ROUND_HALF_UP);
categoryCode = currentItem.getCategoryCode();
}
@@ -1057,6 +998,8 @@ class ZUGFeRDTransactionModelConverter {
return itemNetAmount;
}
public String getCategoryCode() { return categoryCode; }
}
private class Totals {

View File

@@ -0,0 +1,165 @@
/** **********************************************************************
*
* Copyright 2018 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ZUGFeRDVisualizer {
static final ClassLoader CLASS_LOADER = ZUGFeRDVisualizer.class.getClassLoader();
private static final String RESOURCE_PATH = ""; //$NON-NLS-1$
private static final Logger LOG = Logger.getLogger(ZUGFeRDVisualizer.class.getName());
// private static File createTempFileResult(final Transformer transformer, final
// StreamSource toTransform,
// final String suffix) throws TransformerException, IOException {
// File result = File.createTempFile("ZUV_", suffix); //$NON-NLS-1$
// result.deleteOnExit();
//
// try (FileOutputStream fos = new FileOutputStream(result)) {
// transformer.transform(toTransform, new StreamResult(fos));
// }
// return result;
// }
private TransformerFactory mFactory = null;
private Templates mXsltXRTemplate = null;
private Templates mXsltHTMLTemplate = null;
private Templates mXsltZF1HTMLTemplate = null;
public ZUGFeRDVisualizer() {
mFactory = new net.sf.saxon.TransformerFactoryImpl();
// fact = TransformerFactory.newInstance();
mFactory.setURIResolver(new ClasspathResourceURIResolver());
try {
mXsltXRTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html.xsl")));
mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt")));
} catch (TransformerConfigurationException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
public String visualize(String xmlFilename)
throws FileNotFoundException, TransformerException, UnsupportedEncodingException {
/**
* *
* http://www.unece.org/fileadmin/DAM/cefact/xml/XML-Naming-And-Design-Rules-V2_1.pdf
* http://www.ferd-net.de/upload/Dokumente/FACTUR-X_ZUGFeRD_2p0_Teil1_Profil_EN16931_1p03.pdf
* http://countwordsfree.com/xmlviewer
*/
FileInputStream fis=new FileInputStream(xmlFilename);
String fileContent="";
try {
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)));
} catch (IOException e2) {
LOG.log(Level.SEVERE, null, e2);
}
ByteArrayOutputStream iaos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String zf1Signature = "rsm:CrossIndustryDocument";
if (fileContent.contains(zf1Signature)) {
applyZF1SchematronXsl(fis, baos);
} else {
//zf2 or fx
applySchematronXsl(fis, iaos);
// take the copy of the stream and re-write it to an InputStream
PipedInputStream in = new PipedInputStream();
PipedOutputStream out;
try {
out = new PipedOutputStream(in);
new Thread(new Runnable() {
public void run() {
try {
// write the original OutputStream to the PipedOutputStream
// note that in order for the below method to work, you need
// to ensure that the data has finished writing to the
// ByteArrayOutputStream
iaos.writeTo(out);
} catch (IOException e) {
LOG.log(Level.SEVERE, null, e);
} finally {
// close the PipedOutputStream here because we're done writing data
// once this thread has completed its run
if (out != null) {
// close the PipedOutputStream cleanly
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE, null, e);
}
}
}
}
}).start();
applySchematronXsl2(in, baos);
} catch (IOException e1) {
LOG.log(Level.SEVERE, null, e1);
}
}
return baos.toString("UTF-8");
}
public void applySchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltXRTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
public void applyZF1SchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltZF1HTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
public void applySchematronXsl2(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException {
Transformer transformer = mXsltHTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
}
private static class ClasspathResourceURIResolver implements URIResolver {
ClasspathResourceURIResolver() {
// Do nothing, just prevents synthetic access warning.
}
@Override
public Source resolve(String href, String base) throws TransformerException {
return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + href));
}
}
}

View File

@@ -19,7 +19,7 @@
package org.mustangproject.ZUGFeRD.model;
public class DocumentContextParameterTypeConstants {
public static final String BASIC = "urn:ferd:CrossIndustryDocument:invoice:1p0:basic";
public static final String COMFORT = "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort";
public static final String EXTENDED = "urn:ferd:CrossIndustryDocument:invoice:1p0:extended";
// this does not look complete :-/
}

View File

@@ -30,8 +30,14 @@ import org.mustangproject.ZUGFeRD.*;
import javax.xml.transform.TransformerException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -104,8 +110,7 @@ public class Toecount {
+ "\t\t[--source-xml <filename>]: set input XML file\r\n"
+ "\t\t[--out <filename>]: set output PDF file\r\n"
+ "\t\t[--format <fx|zf>]: enable factur-x or ZUGFeRD\r\n"
+ "\t\t[--version <1|2>]: set ZUGFeRD version\r\n"
+ "\t\t[--profile <...>]: set ZUGFeRD profile\r\n"
+ "\t\t[--version <1|2>]: set ZUGFeRD version\r\n" + "\t\t[--profile <...>]: set ZUGFeRD profile\r\n"
+ "\t\t\tFor ZUGFeRD v1: <B>ASIC, <C>OMFORT or <E>XTENDED\r\n"
+ "\t\t\tFor ZUGFeRD v2: <M>INIMUM, BASIC <W>L, <B>ASIC, <C>IUS, <E>N16931, E<X>TENDED\r\n");
}
@@ -114,11 +119,15 @@ public class Toecount {
* Asks the user (repeatedly, if neccessary) on the command line for a String
* (offering a defaultValue) conforming to a Regex pattern
*
* @param prompt the question to be asked to the user
* @param defaultValue the default return value if user hits enter
* @param pattern a regex of acceptable values
* @param prompt
* the question to be asked to the user
* @param defaultValue
* the default return value if user hits enter
* @param pattern
* a regex of acceptable values
* @return the user answer conforming to pattern
* @throws Exception if pattern not compielable or IOexception on input
* @throws Exception
* if pattern not compielable or IOexception on input
*/
protected static String getStringFromUser(String prompt, String defaultValue, String pattern) throws Exception {
String input = "";
@@ -157,8 +166,8 @@ public class Toecount {
/**
* Prompts the user for a input or output filename
*
* @param the text the user is asked
* @param a default Filename
* @param prompt the text the user is asked
* @param defaultFilename a default Filename
* @param expectedExtension will warn if filename does not match expected file extension
* @param ensureFileExists will warn if file does NOT exist (for input files)
* @param ensureFileNotExists will warn if file DOES exist (for output files)
@@ -235,6 +244,8 @@ public class Toecount {
// Option: Help
Option<Boolean> helpOption = parser.addBooleanOption('h', "help");
// Option: Action
Option<String> actionOption = parser.addStringOption('a', "action");
// Generic options available for multiple command
// --source: input file
@@ -292,14 +303,15 @@ public class Toecount {
}
// Retrieve all options
String action = parser.getOptionValue(actionOption);
String directoryName = parser.getOptionValue(dirnameOption);
Boolean filesFromStdIn = parser.getOptionValue(filesFromStdInOption, Boolean.FALSE);
Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE);
Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE);
Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE);
Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE);
Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE) || ((action!=null)&&(action.equals("combine")));
Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE) || ((action!=null)&&(action.equals("extract")));
Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE) || ((action!=null)&&(action.equals("help")));
Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE) || ((action!=null)&&(action.equals("upgrade")));
Boolean ignoreFileExt = parser.getOptionValue(ignoreFileExtOption, Boolean.FALSE);
Boolean a3only = parser.getOptionValue(a3onlyOption, Boolean.FALSE);
Boolean a3only = parser.getOptionValue(a3onlyOption, Boolean.FALSE) || ((action!=null)&&(action.equals("a3")));
String sourceName = parser.getOptionValue(sourceOption);
String sourceXMLName = parser.getOptionValue(sourceXmlOption);
String outName = parser.getOptionValue(outOption);
@@ -341,7 +353,7 @@ public class Toecount {
System.out.println("ZUGFeRD 1.0 XML source set to " + xmlName);
}
if (outName == null) {
outName = getFilenameFromUser("ZUGFeRD 2.0 XML target", "factur-x.xml", "xml", false, true);
outName = getFilenameFromUser("ZUGFeRD 2.0 XML target", "zugferd-invoice.xml", "xml", false, true);
} else {
System.out.println("ZUGFeRD 1.0 XML source set to " + outName);
}
@@ -459,7 +471,6 @@ public class Toecount {
System.out.println("Format set to " + format);
}
if (zfVersion == null) {
try {
zfVersion = getStringFromUser("Version (1 or 2)", "1", "1|2");
@@ -473,13 +484,12 @@ public class Toecount {
if (zfProfile == null) {
try {
if (format.equals("zf")&&(zfIntVersion == 1)) {
zfProfile = getStringFromUser("Profile b)asic, c)omfort or e)xtended", "e",
"B|b|C|c|E|e");
if (format.equals("zf") && (zfIntVersion == 1)) {
zfProfile = getStringFromUser("Profile b)asic, c)omfort or e)xtended", "e", "B|b|C|c|E|e");
} else {
zfProfile = getStringFromUser(
"Profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED",
"E", "M|m|W|w|B|b|C|c|E|e|X|x|");
"Profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED", "E",
"M|m|W|w|B|b|C|c|E|e|X|x|");
}
} catch (Exception e) {
Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
@@ -495,11 +505,11 @@ public class Toecount {
ensureFileExists(xmlName);
ensureFileNotExists(outName);
if ((format.equals("fx"))&&(zfIntVersion>1)) {
if ((format.equals("fx")) && (zfIntVersion > 1)) {
throw new Exception("Factur-X is only available in version 1 (roughly corresponding to ZF2)");
}
if ((format.equals("zf"))&&(zfIntVersion == 1)) {
if ((format.equals("zf")) && (zfIntVersion == 1)) {
if (zfProfile.equals("b")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.BASIC;
} else if (zfProfile.equals("c")) {
@@ -509,7 +519,7 @@ public class Toecount {
} else {
throw new Exception(String.format("Unknown ZUGFeRD profile '%s'", zfProfile));
}
} else if (((format.equals("zf"))&&(zfIntVersion == 2))||(format.equals("fx"))) {
} else if (((format.equals("zf")) && (zfIntVersion == 2)) || (format.equals("fx"))) {
if (zfProfile.equals("m")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.MINIMUM;
} else if (zfProfile.equals("w")) {
@@ -539,7 +549,6 @@ public class Toecount {
ze.setFacturX();
}
ze.setZUGFeRDXMLData(Files.readAllBytes(Paths.get(xmlName)));
ze.export(outName);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
/** **********************************************************************
*
* Copyright 2019 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.framework.TestCase;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class BaseTest extends TestCase {
/**
* Create the test case
*
* @param testName name of the test case
*/
public BaseTest(String testName) {
super(testName);
}
/**
* @return the suite of tests being tested
*/
public static Test suite() {
return new TestSuite(BaseTest.class);
}
public void testCorrectDigits() {
assertEquals("0.00", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal(0),2));
assertEquals("-1.10", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("-1.10"),2));
assertEquals("-1.10", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("-1.1"),2));
assertEquals("-1.01", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("-1.01"),2));
assertEquals("20000123.35", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("20000123.3489"),2));
assertEquals("20000123.34", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("20000123.3419"),2));
assertEquals("12.00", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("12"),2));
assertEquals("12", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("12"),0));
assertEquals("20000123.342", ZUGFeRD2PullProvider.nDigitFormat(new BigDecimal("20000123.3419"),3));
}
}

View File

@@ -27,6 +27,7 @@ import org.junit.runners.MethodSorters;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MustangReaderWriterCustomXMLTest extends TestCase {
@@ -49,7 +50,7 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
// //////// TESTS
// //////////////////////////////////////////////////////////////////////////////////////////
public void testCustomZF2Export() throws Exception {
public void testCustomZF2Export() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506custom.pdf";
// the writing part
@@ -61,8 +62,10 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
.load(SOURCE_PDF);
final byte[] UTF8ByteOrderMark = new byte[]{(byte) 0xef, (byte) 0xbb, (byte) 0xbf};
/* we have much more information than just in the basic profile (comfort or extended) but it's perfectly valid to provide more information, just not less. */
String ownZUGFeRDXML = new String(UTF8ByteOrderMark) + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
/* we have much more information than just in the basic profile (comfort or extended) but it's perfectly valid to provide more information, just not less.
* test the export with bom (which is valid) because this will also test the import (which also needs to work and needs to be tested)
* */
String ownZUGFeRDXML = new String(UTF8ByteOrderMark, StandardCharsets.UTF_8) + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<rsm:CrossIndustryInvoice xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:rsm=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100\" xmlns:ram=\"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100\" xmlns:udt=\"urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100\">\n" +
" <rsm:ExchangedDocumentContext>\n" +
" <ram:TestIndicator><udt:Indicator>false</udt:Indicator></ram:TestIndicator>\n" +
@@ -286,10 +289,10 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
* ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds
* metadata, writes to @{code ./target/testout-*} and then imports to check the
* values. It would not make sense to have it run before the less complex
* importer test (which is probably redundant) --> as only Name Ascending is
* importer test (which is probably redundant). As only Name Ascending is
* supported for Test Unit sequence, I renamed the Exporter Test test-Z-Export
*/
public void testCustomZF1Export() throws Exception {
public void testCustomZF1Export() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505custom.pdf";
// the writing part

View File

@@ -170,13 +170,11 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
/**
* The importer test imports from ./src/test/MustangGnuaccountingBeispielRE-20170509_505.pdf to check the values.
* --> as only Name Ascending is supported for Test Unit sequence, I renamed the this test-A-Export to run before
* As only Name Ascending is supported for Test Unit sequence, I renamed the this test-A-Import to run before
* testZExport
*
* @throws IOException
*/
public void testAImport() throws IOException {
public void testAImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
@@ -195,12 +193,11 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
/**
* The exporter test bases on @{code ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds metadata,
* writes
* to @{code ./target/testout-*} and then imports to check the values.
* It would not make sense to have it run before the less complex importer test (which is probably redundant)
* --> as only Name Ascending is supported for Test Unit sequence, I renamed the Exporter Test test-Z-Export
* writes to @{code ./target/testout-*} and then imports to check the values.
* It would not make sense to have it run before the less complex importer test (which is probably redundant).
* As only Name Ascending is supported for Test Unit sequence, I renamed the Exporter Test test-Z-Export
*/
public void testEdgeExport() throws Exception {
public void testEdgeExport() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505newEdge.pdf";
// the writing part
@@ -218,6 +215,8 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryDocument"));
ze.export(TARGET_PDF);
} catch (IOException e) {
fail("IOException should not happen in testEdgeExport");
}
// now check the contents (like MustangReaderTest)

View File

@@ -30,6 +30,7 @@ import org.apache.xmpbox.xml.DomXmpParser;
import org.apache.xmpbox.xml.XmpParsingException;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -184,15 +185,12 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
// //////////////////////////////////////////////////////////////////////////////////////////
/**
* The importer test imports from
* ./src/test/MustangGnuaccountingBeispielRE-20170509_505.pdf to check the
* values. --> as only Name Ascending is supported for Test Unit sequence, I
* renamed the this test-A-Export to run before testZExport
*
* @throws IOException
* The importer test imports from ./src/test/MustangGnuaccountingBeispielRE-20170509_505.pdf to check the
* values. As only Name Ascending is supported for Test Unit sequence, I renamed the this testAImport
* to run before testZExport
*/
public void testAImport() throws IOException {
public void testAImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
@@ -206,7 +204,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), "RE-20170509/505");
}
public void testForeignImport() throws IOException {
public void testForeignImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/zugferd_invoice.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
@@ -343,10 +341,10 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
* ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds
* metadata, writes to @{code ./target/testout-*} and then imports to check the
* values. It would not make sense to have it run before the less complex
* importer test (which is probably redundant) --> as only Name Ascending is
* importer test (which is probably redundant). As only Name Ascending is
* supported for Test Unit sequence, I renamed the Exporter Test test-Z-Export
*/
public void testZExport() throws Exception {
public void testZExport() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506new.pdf";
@@ -370,6 +368,8 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
// assertFalse(pdfContent.indexOf("<pdfaSchema:prefix>zf</pdfaSchema:prefix>") == -1);
// assertFalse(pdfContent.indexOf("urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#") == -1);
} catch (IOException e) {
fail("IOException should not happen in testZExport");
}
// now check the contents (like MustangReaderTest)
@@ -385,6 +385,37 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), getNumber());
}
/**
* Quick and dirty copy of testZExport to check if v1 files contain
* the correct profile string when the comfort profile is selected.
*/
public void testZExportv1Profile() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506new.pdf";
// the writing part
try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setZUGFeRDVersion(1).setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.COMFORT).load(SOURCE_PDF)) {
ze.PDFattachZugferdFile(this);
ze.disableAutoClose(true);
ze.export(TARGET_PDF);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos);
ze.close();
String pdfContent = baos.toString("UTF-8");
assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.BASIC) >= 0);
assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.EXTENDED) >= 0);
assertTrue(pdfContent.indexOf(DocumentContextParameterTypeConstants.COMFORT) >= 0);
} catch (IOException e) {
fail("IOException should not happen in testZExport");
}
}
public void testFXExport() throws Exception {
@@ -423,11 +454,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), getNumber());
}
/**
* @throws Exception
* @Test(expected = IndexOutOfBoundsException.class)
*/
public void testExceptionOnPDF14() throws Exception {
public void testExceptionOnPDF14() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505new.pdf";

View File

@@ -34,6 +34,10 @@ public class ResourceUtilities {
/**
* Loading a File into a String using a certain encoding and file path
* @param encoding the charset used in the file
* @param path the path to the file
* @return the file contents as a String
* @throws IOException if the file cannot be read
*/
public static String readFile(Charset encoding, String path) throws IOException {
byte[] content = Files.readAllBytes(Paths.get(path));
@@ -42,6 +46,10 @@ public class ResourceUtilities {
/**
* Saving a String as a file to a certain file path
* @param encoding the charset used in the file
* @param path the path to the file
* @param content the contents of the file
* @throws FileNotFoundException if the file cannot be found
*/
public static void saveFile(Charset encoding, String path, String content) throws FileNotFoundException {
@@ -147,7 +155,7 @@ public class ResourceUtilities {
/**
* To fix the 3 slashes bug for File URI: For example:
* file:/C:/work/test.txt -> file:///C:/work/test.txt
* file:/C:/work/test.txt gives file:///C:/work/test.txt
*
* @param u - the File URI
* @return the String of the URI

View File

@@ -41,7 +41,7 @@ import static org.junit.Assert.assertFalse;
* will be migrated to <code>src/test/resources/migration/output</code>.
* If there is a similar named file as test reference <code>src/test/resources/migration/reference</code> a test
* will be triggered for every single file.
* <p/>
*
* Note: Any 'ZUGFeRD1' will be exchanged to 'ZUGFeRD2' during migration and adequate reference will be searched for.
*/
@RunWith(Parameterized.class)

View File

@@ -0,0 +1,211 @@
/** **********************************************************************
*
* Copyright 2019 Jochen Staerk
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ZF2EdgeTest extends MustangReaderTestCase {
final String TARGET_PDF = "./target/testout-ZF2newEdge.pdf";
@Override
public Date getDeliveryDate() {
return new GregorianCalendar(2017, Calendar.MAY, 7).getTime();
}
@Override
public Date getDueDate() {
return new GregorianCalendar(2017, Calendar.MAY, 30).getTime();
}
@Override
public Date getIssueDate() {
return new GregorianCalendar(2017, Calendar.MAY, 9).getTime();
}
@Override
public String getNumber() {
return "RE-20170509/505";
}
@Override
public String getOwnCountry() {
return "DE";
}
@Override
public String getOwnLocation() {
return "Stadthausen";
}
@Override
public String getOwnOrganisationName() {
return "Bei Spiel GmbH";
}
@Override
public String getOwnStreet() {
return "Ecke 12";
}
@Override
public IZUGFeRDExportableContact getOwnContact() {
return new SenderContact();
}
@Override
public String getOwnTaxID() {
return "22/815/0815/4";
}
@Override
public String getOwnVATID() {
return "DE136695976";
}
@Override
public String getOwnZIP() {
return "12345";
}
@Override
public IZUGFeRDExportableContact getRecipient() {
return new RecipientContact();
}
@Override
public String getOwnOrganisationFullPlaintextInfo() {
return null;
}
@Override
public String getCurrency() {
return "EUR";
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
Item[] allItems = new Item[3];
Product designProduct = new Product("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung", "HUR", new BigDecimal("7.000000"));
Product balloonProduct = new Product("", "Bestellerweiterung für E&F Umbau", "C62", new BigDecimal("19.000000"));
Product airProduct = new Product("", "Heiße Luft pro Liter", "LTR", new BigDecimal("19.000000"));
allItems[0] = new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct);
allItems[1] = new Item(new BigDecimal("0.79"), new BigDecimal("400"), balloonProduct);
allItems[2] = new Item(new BigDecimal("0.10"), new BigDecimal("200"), airProduct);
return allItems;
}
@Override
public String getPaymentTermDescription() {
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy");
return "Zahlbar ohne Abzug bis zum " + germanDateFormat.format(getDueDate());
}
@Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFCharges() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return null;
}
@Override
public String getReferenceNumber() {
return "AB321";
}
/**
* Create the test case
*
* @param testName name of the test case
*/
public ZF2EdgeTest(String testName) {
super(testName);
}
/**
* @return the suite of tests being tested
*/
public static Test suite() {
return new TestSuite(ZF2EdgeTest.class);
}
// //////// TESTS //////////////////////////////////////////////////////////////////////////////////////////
/**
* The exporter test bases on @{code ./src/test/MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf}, adds metadata,
* writes to @{code ./target/testout-*} and then imports to check the values.
*/
public void testEdgeExport() {
// the writing part
try (InputStream SOURCE_PDF =
this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf");
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA3Factory()
.setProducer("My Application")
.setCreator(System.getProperty("user.name"))
.setZUGFeRDVersion(2)
.ignorePDFAErrors()
.load(SOURCE_PDF)) {
ze.setZUGFeRDVersion(1);
ze.PDFattachZugferdFile(this);
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryDocument"));
ze.export(TARGET_PDF);
} catch (IOException e) {
fail("IOException should not be raised in testEdgeExport");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF);
// Reading ZUGFeRD
assertEquals(zi.getAmount(), "571.04");
assertEquals(zi.getBIC(), getTradeSettlementPayment()[0].getOwnBIC());
assertEquals(zi.getBLZ(), getTradeSettlementPayment()[0].getOwnBLZ());
assertEquals(zi.getIBAN(), getTradeSettlementPayment()[0].getOwnIBAN());
assertEquals(zi.getKTO(), getTradeSettlementPayment()[0].getOwnKto());
assertEquals(zi.getHolder(), getOwnOrganisationName());
assertEquals(zi.getForeignReference(), getNumber());
}
}