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 1.7.2
===== =====
2019-07-08
Support BuyerReference (r+w), as well as SpecifiedLegalOrganization (w) and DefinedTradeContact (w) Support BuyerReference (r+w), as well as SpecifiedLegalOrganization (w) and DefinedTradeContact (w)
use dom4j to format output xml document use dom4j to format output xml document
corrected some exception logging glitches 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 1.7.1

27
pom.xml
View File

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

View File

@@ -18,7 +18,7 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
public class CustomXMLProvider implements IXMLProvider { public class CustomXMLProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData; 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; import java.io.InputStream;
public interface IExporterFactory { 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; 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) * metadata level, this will not e.g. convert graphics to JPG-2000)
* *
* @param pdfBinary binary of a PDF/A1 compliant document * @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; 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) * metadata level, this will not e.g. convert graphics to JPG-2000)
* *
* @param pdfSource source to read a PDF/A1 compliant document from * @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; public ZUGFeRDExporter load(InputStream pdfSource) throws IOException;

View File

@@ -21,5 +21,7 @@ package org.mustangproject.ZUGFeRD;
public interface IProfileProvider { public interface IProfileProvider {
public String getProfile(); public String getProfile();
public void setProfile(ZUGFeRDConformanceLevel level);
} }

View File

@@ -19,11 +19,11 @@
package org.mustangproject.ZUGFeRD; 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 * @author jstaerk
* @version 1.2.0 * @version 1.2.0
* @date 2014-05-10 * dated 2014-05-10
*/ */
@@ -38,6 +38,24 @@ public interface IZUGFeRDExportableContact {
return null; 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 * First and last name of the recipient

View File

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

View File

@@ -29,10 +29,19 @@ package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
public interface IZUGFeRDExportableTransaction { 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. * 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 * 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 * @return null or full sender institution name, details, manager and tax registration
@@ -115,6 +124,8 @@ public interface IZUGFeRDExportableTransaction {
/** /**
* the creditors payment informations * the creditors payment informations
*
* @return an array of IZUGFeRDTradeSettlementPayment
*/ */
IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment(); IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment();
@@ -236,4 +247,23 @@ public interface IZUGFeRDExportableTransaction {
return null; 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 * @author jstaerk
* @version 1.2.0 * @version 1.2.0
* @date 2015-10-29 * dated 2015-10-29
*/ */
public class VATAmount { public class VATAmount {
public VATAmount(BigDecimal basis, BigDecimal calculated, String documentCode) { public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) {
super(); super();
this.basis = basis; this.basis = basis;
this.calculated = calculated; this.calculated = calculated;
this.documentCode = documentCode; this.categoryCode = categoryCode;
} }
BigDecimal basis, calculated; BigDecimal basis, calculated;
String documentCode; String categoryCode;
public BigDecimal getBasis() { public BigDecimal getBasis() {
return basis; return basis;
@@ -58,20 +58,39 @@ public class VATAmount {
this.calculated = calculated; this.calculated = calculated;
} }
/**
*
* @deprecated Use {@link #getCategoryCode() instead}
* @return String with category code
*/
@Deprecated
public String getDocumentCode() { public String getDocumentCode() {
return documentCode; return categoryCode;
} }
/**
* @param documentCode as String
* @deprecated Use {@link #setCategoryCode(String)} instead
*/
@Deprecated
public void setDocumentCode(String documentCode) { 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) { 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) { 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 { public class XMPSchemaZugferd extends XMPSchema {
/** /***
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file * 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) { public XMPSchemaZugferd(XMPMetadata metadata, ZUGFeRDConformanceLevel conformanceLevel, String URN, String prefix, String filename) {
super(metadata, URN, prefix, "ZUGFeRD Schema"); super(metadata, URN, prefix, "ZUGFeRD Schema");

View File

@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType; import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import org.mustangproject.ZUGFeRD.model.ZFNamespacePrefixMapper; import org.mustangproject.ZUGFeRD.model.ZFNamespacePrefixMapper;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
@@ -27,13 +28,27 @@ import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller; import javax.xml.bind.Marshaller;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
public class ZUGFeRD1PullProvider implements IXMLProvider { public class ZUGFeRD1PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData; protected byte[] zugferdData;
private Marshaller marshaller; private Marshaller marshaller;
private boolean isTest; 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) { private String createZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) {
JAXBElement<CrossIndustryDocumentType> jaxElement = JAXBElement<CrossIndustryDocumentType> jaxElement =
new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).convertToModel(); new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).withProfile(getProfile()).convertToModel();
try { try {
return marshalJaxToXMLString(jaxElement); return marshalJaxToXMLString(jaxElement);

View File

@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -70,6 +71,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData; protected byte[] zugferdData;
private IZUGFeRDExportableTransaction trans; 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 * 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() { 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 * I needed 123,45, locale independent.I tried
* NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale * 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 * http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html in the
* end I decided to calculate myself and take eur+sparator+cents * 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. return value.setScale(scale, RoundingMode.HALF_UP).toPlainString();
// 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);
} }
private String vatFormat(BigDecimal value) { private String vatFormat(BigDecimal value) {
return nDigitFormat(value, 2); return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
} }
private String currencyFormat(BigDecimal value) { private String currencyFormat(BigDecimal value) {
return nDigitFormat(value, 2); return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
} }
private String priceFormat(BigDecimal value) { private String priceFormat(BigDecimal value) {
return nDigitFormat(value, 4); return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
} }
private String quantityFormat(BigDecimal value) { private String quantityFormat(BigDecimal value) {
return nDigitFormat(value, 4); return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
} }
@Override @Override
@@ -158,6 +145,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
} }
private BigDecimal getTotalPrepaid() {
return trans.getTotalPrepaidAmount();
}
private BigDecimal getTotalGross() { private BigDecimal getTotalGross() {
BigDecimal res = getTotal(); BigDecimal res = getTotal();
@@ -214,9 +205,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$ SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
String senderReg = ""; String senderReg = "";
if (trans.getOwnOrganisationFullPlaintextInfo() != null) { if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
senderReg = "" + "<ram:IncludedCINote>\n" + " <ram:Content>\n" senderReg = "" + "<ram:IncludedNote>\n" + " <ram:Content>\n"
+ trans.getOwnOrganisationFullPlaintextInfo() + " </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: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: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:CountryID>" + trans.getRecipient().getCountry() + "</ram:CountryID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:PostalTradeAddress>\n" //$NON-NLS-1$ + " </ram:PostalTradeAddress>\n"; //$NON-NLS-1$
+ " <ram:SpecifiedTaxRegistration>\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:ID schemeID=\"VA\">" + trans.getRecipient().getVATID() + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ + " </ram:SpecifiedTaxRegistration>\n"; //$NON-NLS-1$
+ " </ram:BuyerTradeParty>\n" //$NON-NLS-1$ }
xml += " </ram:BuyerTradeParty>\n" //$NON-NLS-1$
// + " <BuyerOrderReferencedDocument>\n" // + " <BuyerOrderReferencedDocument>\n"
// + " <IssueDateTime format=\"102\">20130301</IssueDateTime>\n" // + " <IssueDateTime format=\"102\">20130301</IssueDateTime>\n"
// + " <ID>2013-471331</ID>\n" // + " <ID>2013-471331</ID>\n"
@@ -412,8 +405,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$ + " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$ + " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " <ram:BICID>" + payment.getOwnBIC() + "</ram:BICID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <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$ // + " <ram:Name>"+trans.getOwnBankName()+"</ram:Name>\n" //$NON-NLS-1$
// //$NON-NLS-2$ // //$NON-NLS-2$
+ " </ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$ + " </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$ + " <ram:GrandTotalAmount>" + currencyFormat(getTotalGross()) + "</ram:GrandTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// // // //
// currencyID=\"EUR\" // currencyID=\"EUR\"
// + " <TotalPrepaidAmount currencyID=\"EUR\">0.00</TotalPrepaidAmount>\n" + " <ram:TotalPrepaidAmount currencyID=\"EUR\">" + currencyFormat(getTotalPrepaid()) + "</ram:TotalPrepaidAmount>\n"
+ " <ram:DuePayableAmount>" + currencyFormat(getTotalGross()) + "</ram:DuePayableAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:DuePayableAmount>" + currencyFormat(getTotalGross().subtract(getTotalPrepaid())) + "</ram:DuePayableAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// // // //
// currencyID=\"EUR\" // currencyID=\"EUR\"
+ " </ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$ + " </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. * 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". * 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- * 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. * means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful * visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people. * metainformation for blind people.
* <p/> * <p>
* Feel free to pass "A" as new level if you know what you are doing :-) * 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 * @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. * 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". * 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- * 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. * means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful * visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people. * metainformation for blind people.
* <p/> * <p>
* Feel free to pass "A" as new level if you know what you are doing :-) * Feel free to pass "A" as new level if you know what you are doing :-)
* *
* @param newLevel "A", "U" or "B"
* @deprecated Use * @deprecated Use
* {@link #setConformanceLevel(org.mustangproject.ZUGFeRD.PDFAConformanceLevel)} * {@link #setConformanceLevel(org.mustangproject.ZUGFeRD.PDFAConformanceLevel)}
* instead * instead
@@ -251,27 +253,45 @@ public class ZUGFeRDExporter implements Closeable {
ignoreA1Errors = true; 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 Use the factory method {@link #createFromPDFA3(String)} instead
*/ */
@Deprecated @Deprecated
public void loadPDFA3(String filename) throws IOException { public void loadPDFA3(String filename) throws IOException {
doc = PDDocument.load(new File(filename)); 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 { public static ZUGFeRDExporter createFromPDFA3(String filename) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(new File(filename))); return new ZUGFeRDExporter(PDDocument.load(new File(filename)));
} }
/** /**
* @deprecated Use the factory method {@link #createFromPDFA3(InputStream)} * load from a pdf which is already A/3 from filestream
* instead * @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 @Deprecated
public void loadPDFA3(InputStream file) throws IOException { public void loadPDFA3(InputStream file) throws IOException {
doc = PDDocument.load(file); 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 { public static ZUGFeRDExporter createFromPDFA3(InputStream pdfSource) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(pdfSource)); 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 * 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) * 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 use the {@link ZUGFeRDExporterFromA1Factory} instead
*/ */
@Deprecated @Deprecated
@@ -291,7 +317,18 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog(); 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 use the {@link ZUGFeRDExporterFromA1Factory} instead
*/ */
@Deprecated @Deprecated
@@ -303,6 +340,7 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog(); return doc.getDocumentCatalog();
} }
private IExporterFactory createPDFA1Factory() { private IExporterFactory createPDFA1Factory() {
ZUGFeRDExporterFromA1Factory factory = new ZUGFeRDExporterFromA1Factory(); ZUGFeRDExporterFromA1Factory factory = new ZUGFeRDExporterFromA1Factory();
if (ignoreA1Errors) { if (ignoreA1Errors) {
@@ -325,10 +363,11 @@ public class ZUGFeRDExporter implements Closeable {
* populate the XML. This parameter may be null, if so the XML data * populate the XML. This parameter may be null, if so the XML data
* should hav ebeen set via * should hav ebeen set via
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code> * <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF
*/ */
public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) throws IOException { public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) throws IOException {
prepareDocument(); prepareDocument();
((IProfileProvider) xmlProvider).setProfile(profile);
xmlProvider.generateXML(trans); xmlProvider.generateXML(trans);
String filename = getFilenameForVersion(ZFVersion); String filename = getFilenameForVersion(ZFVersion);
PDFAttachGenericFile(doc, filename, "Alternative", 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 { public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); 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 { public void export(OutputStream output) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
@@ -376,7 +426,7 @@ public class ZUGFeRDExporter implements Closeable {
* @param description Human-readable description of the file content * @param description Human-readable description of the file content
* @param subType type of the data e.g. could be "text/xml" - mime like * @param subType type of the data e.g. could be "text/xml" - mime like
* @param data the binary data of the file/attachment * @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, public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException { String subType, byte[] data) throws IOException {
@@ -446,6 +496,7 @@ public class ZUGFeRDExporter implements Closeable {
* API or component. * API or component.
* *
* @param zugferdData XML data to be set as a byte array (XML file in raw form). * @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 { public void setZUGFeRDXMLData(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider(); 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 * neccessary PDF/A schema extension description to be able to add this
* information to RDF * information to RDF
* *
* @param metadata * @param metadata the PDFbox XMPMetadata object
*/ */
protected void addXMP(XMPMetadata metadata) { protected void addXMP(XMPMetadata metadata) {
@@ -607,7 +658,7 @@ public class ZUGFeRDExporter implements Closeable {
/** /**
* the human author (use factory method instead) * the human author (use factory method instead)
* *
* @param creator * @param creator the (human) name who created the PDF
*/ */
@Deprecated @Deprecated
public void setCreator(String creator) { public void setCreator(String creator) {
@@ -617,7 +668,7 @@ public class ZUGFeRDExporter implements Closeable {
/** /**
* the CreatorTool attribute for the PDF * the CreatorTool attribute for the PDF
* *
* @param creator * @param creatorTool the application which created the PDF
*/ */
protected void setCreatorTool(String creatorTool) { protected void setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool; this.creatorTool = creatorTool;
@@ -626,7 +677,7 @@ public class ZUGFeRDExporter implements Closeable {
/** /**
* the authoring software (use factory method instead) * the authoring software (use factory method instead)
* *
* @param producer * @param producer the authoring software
*/ */
@Deprecated @Deprecated
public void setProducer(String producer) { 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. * 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". * 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- * 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. * means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful * visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people. * metainformation for blind people.
* <p/> * <p>
* Feel free to pass "A" as new level if you know what you are doing :-) * Feel free to pass "A" as new level if you know what you are doing :-)
*/ */
public IExporterFactory setConformanceLevel(PDFAConformanceLevel newLevel) { 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.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.io.*; import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -65,16 +68,16 @@ public class ZUGFeRDImporter {
*/ */
private byte[] rawXML = null; private byte[] rawXML = null;
private String xmpString = null; // XMP metadata private String xmpString = null; // XMP metadata
private Document document;
public ZUGFeRDImporter(String pdfFilename) { public ZUGFeRDImporter(String pdfFilename) {
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pdfFilename)); try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
extractLowLevel(bis); extractLowLevel(bis);
bis.close();
} catch (IOException e) { } catch (IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
} }
public ZUGFeRDImporter(InputStream pdfStream) { public ZUGFeRDImporter(InputStream pdfStream) {
@@ -125,7 +128,10 @@ public class ZUGFeRDImporter {
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException { 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 * currently (in the release candidate of version 1) only one attached file with
* the name ZUGFeRD-invoice.xml is allowed * 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$ if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml"))) { //$NON-NLS-1$
containsMeta = true; containsMeta = true;
PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile(); PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
// String embeddedFilename = filePath + filename; // String embeddedFilename = filePath + filename;
// File file = new File(filePath + filename); // File file = new File(filePath + filename);
@@ -142,15 +147,13 @@ public class ZUGFeRDImporter {
// ByteArrayOutputStream(); // ByteArrayOutputStream();
// FileOutputStream fos = new FileOutputStream(file); // FileOutputStream fos = new FileOutputStream(file);
rawXML = embeddedFile.toByteArray(); setRawXML(embeddedFile.toByteArray());
setMeta(new String(rawXML));
// fos.write(embeddedFile.getByteArray()); // fos.write(embeddedFile.getByteArray());
// fos.close(); // fos.close();
} }
if (filename.startsWith("additional_data")) { if (filename.startsWith("additional_data")) {
PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile(); PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
additionalXMLs.put(filename, embeddedFile.toByteArray()); additionalXMLs.put(filename, embeddedFile.toByteArray());
@@ -173,13 +176,52 @@ public class ZUGFeRDImporter {
System.err.println(output); 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(); DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
xmlFact.setNamespaceAware(false); xmlFact.setNamespaceAware(false);
DocumentBuilder builder = xmlFact.newDocumentBuilder(); DocumentBuilder builder = xmlFact.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(rawXML)); ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
//prettyPrint(doc); is.skip(guessBOMSize(is));
return doc; 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) { private String extractString(String xpathStr) {
@@ -192,10 +234,7 @@ public class ZUGFeRDImporter {
XPathFactory xpathFact = XPathFactory.newInstance(); XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath(); XPath xpath = xpathFact.newXPath();
result = xpath.evaluate(xpathStr, document); result = xpath.evaluate(xpathStr, document);
} catch (ParserConfigurationException e) { } catch (XPathExpressionException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
} catch (IOException | SAXException | TransformerException | XPathExpressionException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
@@ -300,8 +339,8 @@ public class ZUGFeRDImporter {
/** /**
* @param meta raw XML to be set * @param meta raw XML to be set
*/ */
public void setMeta(String meta) { public void setMeta(String meta) throws IOException {
this.rawXML = meta.getBytes(); setRawXML(meta.getBytes());
} }
/** /**
@@ -346,6 +385,7 @@ public class ZUGFeRDImporter {
/** /**
* Returns the raw XML data as extracted from the ZUGFeRD PDF file. * Returns the raw XML data as extracted from the ZUGFeRD PDF file.
* @return the raw ZUGFeRD XML data
*/ */
public byte[] getRawXML() { public byte[] getRawXML() {
return rawXML; return rawXML;
@@ -369,7 +409,7 @@ public class ZUGFeRDImporter {
static String convertStreamToString(java.io.InputStream is) { 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 // 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 // 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() : ""; return s.hasNext() ? s.next() : "";
} }

View File

@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -30,51 +31,7 @@ import java.util.List;
import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBElement;
import org.mustangproject.ZUGFeRD.model.AmountType; import org.mustangproject.ZUGFeRD.model.*;
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;
class ZUGFeRDTransactionModelConverter { class ZUGFeRDTransactionModelConverter {
private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd");
@@ -84,6 +41,7 @@ class ZUGFeRDTransactionModelConverter {
private final Totals totals; private final Totals totals;
private boolean isTest; private boolean isTest;
private String currency = "EUR"; private String currency = "EUR";
private String profile;
ZUGFeRDTransactionModelConverter(IZUGFeRDExportableTransaction trans) { ZUGFeRDTransactionModelConverter(IZUGFeRDExportableTransaction trans) {
@@ -113,7 +71,7 @@ class ZUGFeRDTransactionModelConverter {
DocumentContextParameterType contextParameter = xmlFactory DocumentContextParameterType contextParameter = xmlFactory
.createDocumentContextParameterType(); .createDocumentContextParameterType();
IDType idType = xmlFactory.createIDType(); IDType idType = xmlFactory.createIDType();
idType.setValue(DocumentContextParameterTypeConstants.EXTENDED); idType.setValue(profile);
contextParameter.setID(idType); contextParameter.setID(idType);
context.getGuidelineSpecifiedDocumentContextParameter().add( context.getGuidelineSpecifiedDocumentContextParameter().add(
contextParameter); contextParameter);
@@ -149,7 +107,7 @@ class ZUGFeRDTransactionModelConverter {
document.setTypeCode(documentCodeType); document.setTypeCode(documentCodeType);
TextType name = xmlFactory.createTextType(); TextType name = xmlFactory.createTextType();
name.setValue("RECHNUNG"); name.setValue(trans.getDocumentName());
document.getName().add(name); document.getName().add(name);
if (trans.getOwnOrganisationFullPlaintextInfo() != null) { if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
@@ -167,7 +125,7 @@ class ZUGFeRDTransactionModelConverter {
if (trans.getReferenceNumber() != null && !"".equals(trans.getReferenceNumber())) { if (trans.getReferenceNumber() != null && !"".equals(trans.getReferenceNumber())) {
NoteType referenceInfo = xmlFactory.createNoteType(); NoteType referenceInfo = xmlFactory.createNoteType();
TextType referenceInfoContent = xmlFactory.createTextType(); TextType referenceInfoContent = xmlFactory.createTextType();
referenceInfoContent.setValue("Ursprungsbeleg: " + trans.getReferenceNumber()); referenceInfoContent.setValue(trans.getReferenceNumber());
referenceInfo.getContent().add(referenceInfoContent); referenceInfo.getContent().add(referenceInfoContent);
document.getIncludedNote().add(referenceInfo); document.getIncludedNote().add(referenceInfo);
} }
@@ -199,6 +157,15 @@ class ZUGFeRDTransactionModelConverter {
tradeAgreement.setBuyerTradeParty(getBuyer()); tradeAgreement.setBuyerTradeParty(getBuyer());
tradeAgreement.setSellerTradeParty(getSeller()); 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; return tradeAgreement;
} }
@@ -212,6 +179,12 @@ class ZUGFeRDTransactionModelConverter {
buyerID.setValue(trans.getRecipient().getID()); buyerID.setValue(trans.getRecipient().getID());
buyerTradeParty.getID().add(buyerID); 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(); TextType buyerName = xmlFactory.createTextType();
buyerName.setValue(trans.getRecipient().getName()); buyerName.setValue(trans.getRecipient().getName());
@@ -422,7 +395,7 @@ class ZUGFeRDTransactionModelConverter {
TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType(); TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType();
VATAmount vatAmount = VATPercentAmountMap.get(currentTaxPercent); VATAmount vatAmount = VATPercentAmountMap.get(currentTaxPercent);
taxCategoryCode.setValue(vatAmount.getDocumentCode()); taxCategoryCode.setValue(vatAmount.getCategoryCode());
tradeTax.setCategoryCode(taxCategoryCode); tradeTax.setCategoryCode(taxCategoryCode);
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
@@ -686,9 +659,14 @@ class ZUGFeRDTransactionModelConverter {
grandTotalAmount.setValue(currencyFormat(totals.getTotalGross())); grandTotalAmount.setValue(currencyFormat(totals.getTotalGross()));
monetarySummation.getGrandTotalAmount().add(grandTotalAmount); monetarySummation.getGrandTotalAmount().add(grandTotalAmount);
AmountType totalPrepaidAmount = xmlFactory.createAmountType();
totalPrepaidAmount.setCurrencyID(currency);
totalPrepaidAmount.setValue(currencyFormat(trans.getTotalPrepaidAmount()));
monetarySummation.getTotalPrepaidAmount().add(totalPrepaidAmount);
AmountType duePayableAmount = xmlFactory.createAmountType(); AmountType duePayableAmount = xmlFactory.createAmountType();
duePayableAmount.setCurrencyID(currency); duePayableAmount.setCurrencyID(currency);
duePayableAmount.setValue(currencyFormat(totals.getTotalGross())); duePayableAmount.setValue(currencyFormat(totals.getTotalGross().subtract(trans.getTotalPrepaidAmount())));
monetarySummation.getDuePayableAmount().add(duePayableAmount); monetarySummation.getDuePayableAmount().add(duePayableAmount);
return monetarySummation; return monetarySummation;
@@ -843,70 +821,26 @@ class ZUGFeRDTransactionModelConverter {
private BigDecimal vatFormat(BigDecimal value) { private BigDecimal vatFormat(BigDecimal value) {
return nDigitFormat(value, 2); return value.setScale(2, RoundingMode.HALF_UP);
} }
private BigDecimal currencyFormat(BigDecimal value) { private BigDecimal currencyFormat(BigDecimal value) {
return nDigitFormat(value, 2); return value.setScale(2, RoundingMode.HALF_UP);
} }
private BigDecimal priceFormat(BigDecimal value) { private BigDecimal priceFormat(BigDecimal value) {
return nDigitFormat(value, 4); return value.setScale(4, RoundingMode.HALF_UP);
} }
private BigDecimal quantityFormat(BigDecimal value) { 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 * 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
* (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT) 190 Eur * (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT) 190 Eur
@@ -924,7 +858,7 @@ class ZUGFeRDTransactionModelConverter {
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent(); BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalc lc = new LineCalc(currentItem); 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); VATAmount current = hm.get(percent);
if (current == null) { if (current == null) {
hm.put(percent, itemVATAmount); hm.put(percent, itemVATAmount);
@@ -958,7 +892,7 @@ class ZUGFeRDTransactionModelConverter {
VATAmount itemVATAmount = new VATAmount( VATAmount itemVATAmount = new VATAmount(
logisticsServiceCharge.getTotalAmount(), logisticsServiceCharge.getTotalAmount(),
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); VATAmount current = hm.get(percent);
if (current == null) { if (current == null) {
hm.put(percent, itemVATAmount); hm.put(percent, itemVATAmount);
@@ -973,7 +907,7 @@ class ZUGFeRDTransactionModelConverter {
BigDecimal percent = charge.getTaxPercent(); BigDecimal percent = charge.getTaxPercent();
VATAmount itemVATAmount = new VATAmount( VATAmount itemVATAmount = new VATAmount(
charge.getTotalAmount(), charge.getTotalAmount() 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); VATAmount current = hm.get(percent);
if (current == null) { if (current == null) {
hm.put(percent, itemVATAmount); hm.put(percent, itemVATAmount);
@@ -992,12 +926,18 @@ class ZUGFeRDTransactionModelConverter {
return this; return this;
} }
public ZUGFeRDTransactionModelConverter withProfile(String profile) {
this.profile = profile;
return this;
}
private class LineCalc { private class LineCalc {
private BigDecimal totalGross; private BigDecimal totalGross;
private BigDecimal itemTotalNetAmount; private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount; private BigDecimal itemTotalVATAmount;
private BigDecimal itemNetAmount; private BigDecimal itemNetAmount;
private String categoryCode;
public LineCalc(IZUGFeRDExportableItem currentItem) { public LineCalc(IZUGFeRDExportableItem currentItem) {
@@ -1040,6 +980,7 @@ class ZUGFeRDTransactionModelConverter {
.add(totalCharge) .add(totalCharge)
.divide(currentItem.getQuantity(), 4, .divide(currentItem.getQuantity(), 4,
BigDecimal.ROUND_HALF_UP); BigDecimal.ROUND_HALF_UP);
categoryCode = currentItem.getCategoryCode();
} }
@@ -1057,6 +998,8 @@ class ZUGFeRDTransactionModelConverter {
return itemNetAmount; return itemNetAmount;
} }
public String getCategoryCode() { return categoryCode; }
} }
private class Totals { 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; package org.mustangproject.ZUGFeRD.model;
public class DocumentContextParameterTypeConstants { 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 COMFORT = "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort";
public static final String EXTENDED = "urn:ferd:CrossIndustryDocument:invoice:1p0:extended"; 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 javax.xml.transform.TransformerException;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; 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.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; 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[--source-xml <filename>]: set input XML file\r\n"
+ "\t\t[--out <filename>]: set output PDF 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[--format <fx|zf>]: enable factur-x or ZUGFeRD\r\n"
+ "\t\t[--version <1|2>]: set ZUGFeRD version\r\n" + "\t\t[--version <1|2>]: set ZUGFeRD version\r\n" + "\t\t[--profile <...>]: set ZUGFeRD profile\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 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"); + "\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 * Asks the user (repeatedly, if neccessary) on the command line for a String
* (offering a defaultValue) conforming to a Regex pattern * (offering a defaultValue) conforming to a Regex pattern
* *
* @param prompt the question to be asked to the user * @param prompt
* @param defaultValue the default return value if user hits enter * the question to be asked to the user
* @param pattern a regex of acceptable values * @param defaultValue
* the default return value if user hits enter
* @param pattern
* a regex of acceptable values
* @return the user answer conforming to pattern * @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 { protected static String getStringFromUser(String prompt, String defaultValue, String pattern) throws Exception {
String input = ""; String input = "";
@@ -157,15 +166,15 @@ public class Toecount {
/** /**
* Prompts the user for a input or output filename * Prompts the user for a input or output filename
* *
* @param the text the user is asked * @param prompt the text the user is asked
* @param a default Filename * @param defaultFilename a default Filename
* @param expectedExtension will warn if filename does not match expected file extension * @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 ensureFileExists will warn if file does NOT exist (for input files)
* @param ensureFileNotExists will warn if file DOES exist (for output files) * @param ensureFileNotExists will warn if file DOES exist (for output files)
* @return String * @return String
*/ */
protected static String getFilenameFromUser(String prompt, String defaultFilename, String expectedExtension, protected static String getFilenameFromUser(String prompt, String defaultFilename, String expectedExtension,
boolean ensureFileExists, boolean ensureFileNotExists) { boolean ensureFileExists, boolean ensureFileNotExists) {
boolean fileExistenceOK = false; boolean fileExistenceOK = false;
String selectedName = ""; String selectedName = "";
do { do {
@@ -235,6 +244,8 @@ public class Toecount {
// Option: Help // Option: Help
Option<Boolean> helpOption = parser.addBooleanOption('h', "help"); Option<Boolean> helpOption = parser.addBooleanOption('h', "help");
// Option: Action
Option<String> actionOption = parser.addStringOption('a', "action");
// Generic options available for multiple command // Generic options available for multiple command
// --source: input file // --source: input file
@@ -292,14 +303,15 @@ public class Toecount {
} }
// Retrieve all options // Retrieve all options
String action = parser.getOptionValue(actionOption);
String directoryName = parser.getOptionValue(dirnameOption); String directoryName = parser.getOptionValue(dirnameOption);
Boolean filesFromStdIn = parser.getOptionValue(filesFromStdInOption, Boolean.FALSE); Boolean filesFromStdIn = parser.getOptionValue(filesFromStdInOption, Boolean.FALSE);
Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE); Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE) || ((action!=null)&&(action.equals("combine")));
Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE); Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE) || ((action!=null)&&(action.equals("extract")));
Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE); Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE) || ((action!=null)&&(action.equals("help")));
Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE); Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE) || ((action!=null)&&(action.equals("upgrade")));
Boolean ignoreFileExt = parser.getOptionValue(ignoreFileExtOption, Boolean.FALSE); 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 sourceName = parser.getOptionValue(sourceOption);
String sourceXMLName = parser.getOptionValue(sourceXmlOption); String sourceXMLName = parser.getOptionValue(sourceXmlOption);
String outName = parser.getOptionValue(outOption); String outName = parser.getOptionValue(outOption);
@@ -325,7 +337,7 @@ public class Toecount {
System.exit(2); System.exit(2);
} }
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e); Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
System.exit(-1); System.exit(-1);
} }
@@ -341,7 +353,7 @@ public class Toecount {
System.out.println("ZUGFeRD 1.0 XML source set to " + xmlName); System.out.println("ZUGFeRD 1.0 XML source set to " + xmlName);
} }
if (outName == null) { 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 { } else {
System.out.println("ZUGFeRD 1.0 XML source set to " + outName); System.out.println("ZUGFeRD 1.0 XML source set to " + outName);
} }
@@ -421,7 +433,7 @@ public class Toecount {
} }
private static void performCombine(String pdfName, String xmlName, String outName, String format, String zfVersion, private static void performCombine(String pdfName, String xmlName, String outName, String format, String zfVersion,
String zfProfile) throws Exception { String zfProfile) throws Exception {
/* /*
* ZUGFeRDExporter ze= new ZUGFeRDExporterFromA1Factory() * ZUGFeRDExporter ze= new ZUGFeRDExporterFromA1Factory()
* .setProducer("toecount") .setCreator(System.getProperty("user.name")) * .setProducer("toecount") .setCreator(System.getProperty("user.name"))
@@ -459,7 +471,6 @@ public class Toecount {
System.out.println("Format set to " + format); System.out.println("Format set to " + format);
} }
if (zfVersion == null) { if (zfVersion == null) {
try { try {
zfVersion = getStringFromUser("Version (1 or 2)", "1", "1|2"); zfVersion = getStringFromUser("Version (1 or 2)", "1", "1|2");
@@ -473,13 +484,12 @@ public class Toecount {
if (zfProfile == null) { if (zfProfile == null) {
try { try {
if (format.equals("zf")&&(zfIntVersion == 1)) { if (format.equals("zf") && (zfIntVersion == 1)) {
zfProfile = getStringFromUser("Profile b)asic, c)omfort or e)xtended", "e", zfProfile = getStringFromUser("Profile b)asic, c)omfort or e)xtended", "e", "B|b|C|c|E|e");
"B|b|C|c|E|e");
} else { } else {
zfProfile = getStringFromUser( zfProfile = getStringFromUser(
"Profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED", "Profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED", "E",
"E", "M|m|W|w|B|b|C|c|E|e|X|x|"); "M|m|W|w|B|b|C|c|E|e|X|x|");
} }
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e); Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
@@ -494,12 +504,12 @@ public class Toecount {
ensureFileExists(pdfName); ensureFileExists(pdfName);
ensureFileExists(xmlName); ensureFileExists(xmlName);
ensureFileNotExists(outName); 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)"); 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")) { if (zfProfile.equals("b")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.BASIC; zfConformanceLevelProfile = ZUGFeRDConformanceLevel.BASIC;
} else if (zfProfile.equals("c")) { } else if (zfProfile.equals("c")) {
@@ -509,7 +519,7 @@ public class Toecount {
} else { } else {
throw new Exception(String.format("Unknown ZUGFeRD profile '%s'", zfProfile)); 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")) { if (zfProfile.equals("m")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.MINIMUM; zfConformanceLevelProfile = ZUGFeRDConformanceLevel.MINIMUM;
} else if (zfProfile.equals("w")) { } else if (zfProfile.equals("w")) {
@@ -538,7 +548,6 @@ public class Toecount {
if (format.equals("fx")) { if (format.equals("fx")) {
ze.setFacturX(); ze.setFacturX();
} }
ze.setZUGFeRDXMLData(Files.readAllBytes(Paths.get(xmlName))); ze.setZUGFeRDXMLData(Files.readAllBytes(Paths.get(xmlName)));

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.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MustangReaderWriterCustomXMLTest extends TestCase { public class MustangReaderWriterCustomXMLTest extends TestCase {
@@ -49,7 +50,7 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
// //////// TESTS // //////// TESTS
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
public void testCustomZF2Export() throws Exception { public void testCustomZF2Export() {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506custom.pdf"; final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506custom.pdf";
// the writing part // the writing part
@@ -61,8 +62,10 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
.load(SOURCE_PDF); .load(SOURCE_PDF);
final byte[] UTF8ByteOrderMark = new byte[]{(byte) 0xef, (byte) 0xbb, (byte) 0xbf}; 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. */ /* 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" + * 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: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" + " <rsm:ExchangedDocumentContext>\n" +
" <ram:TestIndicator><udt:Indicator>false</udt:Indicator></ram:TestIndicator>\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 * ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds
* metadata, writes to @{code ./target/testout-*} and then imports to check the * 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 * 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 * 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"; final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505custom.pdf";
// the writing part // 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. * 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 * testZExport
*
* @throws IOException
*/ */
public void testAImport() throws IOException { public void testAImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf"); InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream); 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, * The exporter test bases on @{code ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds metadata,
* writes * writes to @{code ./target/testout-*} and then imports to check the values.
* 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).
* 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
* --> 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"; final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505newEdge.pdf";
// the writing part // the writing part
@@ -218,6 +215,8 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryDocument")); assertTrue(theXML.contains("<rsm:CrossIndustryDocument"));
ze.export(TARGET_PDF); ze.export(TARGET_PDF);
} catch (IOException e) {
fail("IOException should not happen in testEdgeExport");
} }
// now check the contents (like MustangReaderTest) // 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.apache.xmpbox.xml.XmpParsingException;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -184,15 +185,12 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
// ////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////
/** /**
* The importer test imports from * The importer test imports from ./src/test/MustangGnuaccountingBeispielRE-20170509_505.pdf to check the
* ./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
* values. --> as only Name Ascending is supported for Test Unit sequence, I * to run before testZExport
* renamed the this test-A-Export to run before testZExport
*
* @throws IOException
*/ */
public void testAImport() throws IOException { public void testAImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf"); InputStream inputStream = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream); ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
@@ -206,7 +204,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), "RE-20170509/505"); assertEquals(zi.getForeignReference(), "RE-20170509/505");
} }
public void testForeignImport() throws IOException { public void testForeignImport() {
InputStream inputStream = this.getClass().getResourceAsStream("/zugferd_invoice.pdf"); InputStream inputStream = this.getClass().getResourceAsStream("/zugferd_invoice.pdf");
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream); ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
@@ -343,16 +341,16 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
* ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds * ./src/test/MustangGnuaccountingBeispielRE-20140703_502blanko.pdf}, adds
* metadata, writes to @{code ./target/testout-*} and then imports to check the * 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 * 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 * 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"; final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20171118_506new.pdf";
// the writing part // the writing part
try (InputStream SOURCE_PDF = this.getClass() try (InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setZUGFeRDVersion(2).setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.EN16931).load(SOURCE_PDF)) { ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setZUGFeRDVersion(2).setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.EN16931).load(SOURCE_PDF)) {
@@ -369,7 +367,9 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1); // assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
// assertFalse(pdfContent.indexOf("<pdfaSchema:prefix>zf</pdfaSchema:prefix>") == -1); // assertFalse(pdfContent.indexOf("<pdfaSchema:prefix>zf</pdfaSchema:prefix>") == -1);
// assertFalse(pdfContent.indexOf("urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#") == -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) // now check the contents (like MustangReaderTest)
@@ -385,6 +385,37 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), getNumber()); 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 { public void testFXExport() throws Exception {
@@ -423,11 +454,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
assertEquals(zi.getForeignReference(), getNumber()); assertEquals(zi.getForeignReference(), getNumber());
} }
/** public void testExceptionOnPDF14() {
* @throws Exception
* @Test(expected = IndexOutOfBoundsException.class)
*/
public void testExceptionOnPDF14() throws Exception {
final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20170509_505new.pdf"; 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 * 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 { public static String readFile(Charset encoding, String path) throws IOException {
byte[] content = Files.readAllBytes(Paths.get(path)); 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 * 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 { 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: * 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 * @param u - the File URI
* @return the String of the 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>. * 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 * 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. * 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. * Note: Any 'ZUGFeRD1' will be exchanged to 'ZUGFeRD2' during migration and adequate reference will be searched for.
*/ */
@RunWith(Parameterized.class) @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());
}
}