diff --git a/mustang/doc/MustangDev.en.odt b/mustang/doc/MustangDev.en.odt index 7cc7c98d..c9bbe3b7 100644 Binary files a/mustang/doc/MustangDev.en.odt and b/mustang/doc/MustangDev.en.odt differ diff --git a/mustang/doc/MustangWriter.java b/mustang/doc/MustangWriter.java index ceed24e7..0f440de9 100644 --- a/mustang/doc/MustangWriter.java +++ b/mustang/doc/MustangWriter.java @@ -167,7 +167,7 @@ public class MustangWriter implements IZUGFeRDExportableTransaction { @Override public String getOwnBIC() { - return "COBADEFXXX"; + return "COBADEFFXXX"; } @Override diff --git a/mustang/doc/ZugferdDev.de.odt b/mustang/doc/ZugferdDev.de.odt index 29d40ea6..0db0d097 100644 Binary files a/mustang/doc/ZugferdDev.de.odt and b/mustang/doc/ZugferdDev.de.odt differ diff --git a/mustang/doc/ZugferdDev.de.pdf b/mustang/doc/ZugferdDev.de.pdf index b6166af7..19877b94 100644 Binary files a/mustang/doc/ZugferdDev.de.pdf and b/mustang/doc/ZugferdDev.de.pdf differ diff --git a/mustang/doc/ZugferdDev.en.odt b/mustang/doc/ZugferdDev.en.odt index c47a9034..1377da4d 100644 Binary files a/mustang/doc/ZugferdDev.en.odt and b/mustang/doc/ZugferdDev.en.odt differ diff --git a/mustang/doc/ZugferdDev.en.pdf b/mustang/doc/ZugferdDev.en.pdf index 9c680f8f..d2d7722b 100644 Binary files a/mustang/doc/ZugferdDev.en.pdf and b/mustang/doc/ZugferdDev.en.pdf differ diff --git a/mustang/doc/konikmustangvergleich.odt b/mustang/doc/konikmustangvergleich.odt new file mode 100644 index 00000000..4f9a749b Binary files /dev/null and b/mustang/doc/konikmustangvergleich.odt differ diff --git a/mustang/pom.xml b/mustang/pom.xml index 9d09a237..43d45fa2 100644 --- a/mustang/pom.xml +++ b/mustang/pom.xml @@ -3,24 +3,66 @@ com.github.rayman2200 mustang-sample-project - 1.3.0-SNAPSHOT + 1.3.0.1-SNAPSHOT org.mustangproject.ZUGFeRD mustang - 1.3.0-SNAPSHOT + 1.3.0.1-SNAPSHOT jar Mustang The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs http://www.mustangproject.org/ - + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.3 + + true + + + + package + + shade + + + + + + + + + + + + + org.glassfish.jaxb jaxb-runtime 2.2.11 + + + org.apache.pdfbox + preflight + 1.8.8 + + + org.apache.pdfbox + pdfbox + 1.8.8 + @@ -56,5 +98,4 @@ - diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java index 8c49a4d6..1f80dad2 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java @@ -4,7 +4,7 @@ package org.mustangproject.ZUGFeRD; * Neccessary interface for ZUGFeRD exporter * Licensed under the APLv2 * @date 2014-05-10 - * @version 1.0 + * @version 1.2.0 * @author jstaerk * */ diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java index 99044349..9e9c06bd 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java @@ -4,7 +4,7 @@ package org.mustangproject.ZUGFeRD; * Neccessary interface for ZUGFeRD exporter * Licensed under the APLv2 * @date 2014-05-10 - * @version 1.0 + * @version 1.2.0 * @author jstaerk * */ diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java index 8f552eb2..5cc43930 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java @@ -5,7 +5,7 @@ import java.math.BigDecimal; * Necessary interface for ZUGFeRD exporter * Licensed under the APLv2 * @date 2014-05-10 - * @version 1.0 + * @version 1.2.0 * @author jstaerk * */ diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java index 3d9af400..fb48e203 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java @@ -4,7 +4,7 @@ package org.mustangproject.ZUGFeRD; * Neccessary interface for ZUGFeRD exporter * Licensed under the APLv2 * @date 2014-05-10 to 2014-06-25 - * @version 1.0.2 + * @version 1.2.0 * @author jstaerk * */ @@ -127,7 +127,7 @@ public interface IZUGFeRDExportableTransaction { * get main invoice currency used on the invoice * @return */ - String getInvoiceCurrency(); + String getCurrency(); /** * get payment information text. e.g. Bank transfer diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java index 2c7eac5e..ab120d12 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java @@ -1,7 +1,14 @@ package org.mustangproject.ZUGFeRD; import java.math.BigDecimal; - +/** + * Mustangproject's ZUGFeRD implementation + * ZUGFeRD exporter helper class + * Licensed under the APLv2 + * @date 2015-10-29 + * @version 1.2.0 + * @author jstaerk + * */ public class VATAmount { public VATAmount(BigDecimal basis, BigDecimal calculated) { diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java index 9d055c9b..2bee5ca5 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java @@ -4,7 +4,7 @@ package org.mustangproject.ZUGFeRD; * ZUGFeRD exporter helper class * Licensed under the APLv2 * @date 2014-05-10 - * @version 1.0 + * @version 1.2.0 * @author jstaerk * */ import org.apache.jempbox.impl.XMLUtil; @@ -112,6 +112,12 @@ import org.w3c.dom.Element; Element propertyNode = schema.getOwnerDocument().createElement( "pdfaSchema:property"); //$NON-NLS-1$ + Element valueTypeNode = schema.getOwnerDocument().createElement( + "pdfaSchema:valueType"); //$NON-NLS-1$ + + Element seqListValueType = schema.getOwnerDocument().createElement( + "rdf:Seq"); //$NON-NLS-1$ + Element sequenceNode = schema.getOwnerDocument().createElement( "rdf:Seq"); //$NON-NLS-1$ @@ -150,6 +156,9 @@ import org.w3c.dom.Element; bagListNode.appendChild(propertyNode); + valueTypeNode.appendChild(seqListValueType); + bagListNode.appendChild(valueTypeNode); + bagNode.appendChild(bagListNode); schemasNode.appendChild(bagNode); diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java index a32214ec..fcf9b673 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java @@ -4,7 +4,7 @@ package org.mustangproject.ZUGFeRD; * ZUGFeRD exporter helper class * Licensed under the APLv2 * @date 2014-05-10 - * @version 1.0 + * @version 1.2.0s * @author jstaerk * */ import org.apache.jempbox.impl.XMLUtil; @@ -15,7 +15,7 @@ import org.w3c.dom.Element; public class XMPSchemaZugferd extends XMPSchemaBasic { - private String conformanceLevel = "COMFORT"; + private String conformanceLevel = "EXTENDED"; /** * This is what needs to be added to the RDF metadata - basically the name of the embedded diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java index 8f25dee2..fcaf9403 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java @@ -5,13 +5,14 @@ package org.mustangproject.ZUGFeRD; * APLv2 * * @date 2014-07-12 - * @version 1.1 + * @version 1.2.0 * @author jstaerk * */ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -23,11 +24,12 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; + +import javax.activation.FileDataSource; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; - import javax.xml.transform.TransformerException; import org.apache.jempbox.xmp.XMPMetadata; @@ -39,6 +41,7 @@ import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSBase; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.exceptions.COSVisitorException; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentInformation; @@ -48,375 +51,353 @@ import org.apache.pdfbox.pdmodel.common.COSObjectable; import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; +import org.apache.pdfbox.preflight.PreflightDocument; +import org.apache.pdfbox.preflight.ValidationResult; +import org.apache.pdfbox.preflight.exception.ValidationException; +import org.apache.pdfbox.preflight.parser.PreflightParser; +import org.apache.pdfbox.preflight.utils.ByteArrayDataSource; import org.mustangproject.ZUGFeRD.model.*; public class ZUGFeRDExporter { - /** - * * - * You will need Apache PDFBox. To use the ZUGFeRD exporter, implement - * IZUGFeRDExportableTransaction in yourTransaction (which will require you - * to implement Product, Item and Contact) then call doc = - * PDDocument.load(PDFfilename); // automatically add Zugferd to all - * outgoing invoices ZUGFeRDExporter ze = new ZUGFeRDExporter(); - * ze.PDFmakeA3compliant(doc, "Your application name", - * System.getProperty("user.name"), true); ze.PDFattachZugferdFile(doc, - * yourTransaction); - * - * doc.save(PDFfilename); - * - * @author jstaerk - * @throws javax.xml.bind.JAXBException - * - */ - public ZUGFeRDExporter() throws JAXBException { - jaxbContext = JAXBContext.newInstance("org.mustangproject.ZUGFeRD.model"); - marshaller = jaxbContext.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - } + /** + * * You will need Apache PDFBox. To use the ZUGFeRD exporter, implement + * IZUGFeRDExportableTransaction in yourTransaction (which will require you + * to implement Product, Item and Contact) then call doc = + * PDDocument.load(PDFfilename); // automatically add Zugferd to all + * outgoing invoices ZUGFeRDExporter ze = new ZUGFeRDExporter(); + * ze.PDFmakeA3compliant(doc, "Your application name", + * System.getProperty("user.name"), true); ze.PDFattachZugferdFile(doc, + * yourTransaction); + * + * doc.save(PDFfilename); + * + * @author jstaerk + * @throws javax.xml.bind.JAXBException + * + */ + public ZUGFeRDExporter() throws JAXBException { + jaxbContext = JAXBContext + .newInstance("org.mustangproject.ZUGFeRD.model"); + marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + } - private class LineCalc { + private class LineCalc { - private BigDecimal totalGross; - private BigDecimal itemTotalNetAmount; - private BigDecimal itemTotalVATAmount; - private BigDecimal itemNetAmount; + private BigDecimal totalGross; + private BigDecimal itemTotalNetAmount; + private BigDecimal itemTotalVATAmount; + private BigDecimal itemNetAmount; - public LineCalc(IZUGFeRDExportableItem currentItem) { - BigDecimal totalAllowance = BigDecimal.ZERO; - BigDecimal totalCharge = BigDecimal.ZERO; - - if(currentItem.getItemAllowances() != null){ - for(IZUGFeRDAllowanceCharge itemAllowance : currentItem.getItemAllowances()){ - totalAllowance = itemAllowance.getTotalAmount().add(totalAllowance); - } - } - - if(currentItem.getItemCharges() != null){ - for(IZUGFeRDAllowanceCharge itemCharge : currentItem.getItemCharges()){ - totalCharge = itemCharge.getTotalAmount().add(totalCharge); - } - } - - BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100), 4, BigDecimal.ROUND_HALF_UP).add(BigDecimal.ONE); -// priceGross=currentItem.getPrice().multiply(multiplicator); - - totalGross = currentItem.getPrice().multiply(currentItem.getQuantity()).subtract(totalAllowance).add(totalCharge).multiply(multiplicator); - itemTotalNetAmount = currentItem.getPrice().multiply(currentItem.getQuantity()).subtract(totalAllowance).add(totalCharge).setScale(2, BigDecimal.ROUND_HALF_UP); - itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount); - itemNetAmount = currentItem.getPrice().multiply(currentItem.getQuantity()).subtract(totalAllowance).add(totalCharge).divide(currentItem.getQuantity(), 4, BigDecimal.ROUND_HALF_UP); - } + public LineCalc(IZUGFeRDExportableItem currentItem) { + BigDecimal totalAllowance = BigDecimal.ZERO; + BigDecimal totalCharge = BigDecimal.ZERO; - public BigDecimal getItemTotalNetAmount() { - return itemTotalNetAmount; - } + if (currentItem.getItemAllowances() != null) { + for (IZUGFeRDAllowanceCharge itemAllowance : currentItem + .getItemAllowances()) { + totalAllowance = itemAllowance.getTotalAmount().add( + totalAllowance); + } + } - public BigDecimal getItemTotalVATAmount() { - return itemTotalVATAmount; - } - - public BigDecimal getItemNetAmount(){ - return itemNetAmount; - } + if (currentItem.getItemCharges() != null) { + for (IZUGFeRDAllowanceCharge itemCharge : currentItem + .getItemCharges()) { + totalCharge = itemCharge.getTotalAmount().add(totalCharge); + } + } - } - - private class Totals{ - private BigDecimal totalNetAmount; - private BigDecimal totalGrossAmount; - private BigDecimal lineTotalAmount; - private BigDecimal totalTaxAmount; - - public Totals(){ - BigDecimal res = BigDecimal.ZERO; - for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { - LineCalc lc = new LineCalc(currentItem); - res = res.add(lc.getItemTotalNetAmount()); - } - // Set line total - this.lineTotalAmount = res; + BigDecimal multiplicator = currentItem.getProduct().getVATPercent() + .divide(new BigDecimal(100), 4, BigDecimal.ROUND_HALF_UP) + .add(BigDecimal.ONE); + // priceGross=currentItem.getPrice().multiply(multiplicator); - if(trans.getZFAllowances() != null){ - for (IZUGFeRDAllowanceCharge headerAllowance : trans.getZFAllowances()){ - res = res.subtract(headerAllowance.getTotalAmount()); - } - } + totalGross = currentItem.getPrice() + .multiply(currentItem.getQuantity()) + .subtract(totalAllowance).add(totalCharge) + .multiply(multiplicator); + itemTotalNetAmount = currentItem.getPrice() + .multiply(currentItem.getQuantity()) + .subtract(totalAllowance).add(totalCharge) + .setScale(2, BigDecimal.ROUND_HALF_UP); + itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount); + itemNetAmount = currentItem + .getPrice() + .multiply(currentItem.getQuantity()) + .subtract(totalAllowance) + .add(totalCharge) + .divide(currentItem.getQuantity(), 4, + BigDecimal.ROUND_HALF_UP); + } - if(trans.getZFLogisticsServiceCharges()!= null){ - for (IZUGFeRDAllowanceCharge logisticsServiceCharge : trans.getZFLogisticsServiceCharges()){ - res = res.add(logisticsServiceCharge.getTotalAmount()); - } - } + public BigDecimal getItemTotalNetAmount() { + return itemTotalNetAmount; + } - if(trans.getZFCharges()!= null){ - for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()){ - res = res.add(charge.getTotalAmount()); - } - } - - // Set total net amount - this.totalNetAmount = res; + public BigDecimal getItemTotalVATAmount() { + return itemTotalVATAmount; + } - HashMap VATPercentAmountMap = getVATPercentAmountMap(); - for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) { - VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); - res = res.add(amount.getCalculated()); - } - - // Set total gross amount - this.totalGrossAmount = res; - - this.totalTaxAmount = this.totalGrossAmount.subtract(this.totalNetAmount); - } - - public BigDecimal getTotalNet(){ - return totalNetAmount; - } - - public BigDecimal getTotalGross(){ - return totalGrossAmount; - } - - public BigDecimal getLineTotal(){ - return lineTotalAmount; - } - - public BigDecimal getTaxTotal(){ - return totalTaxAmount; - } - - - } + public BigDecimal getItemNetAmount() { + return itemNetAmount; + } - //// MAIN CLASS - private String conformanceLevel = "U"; - private String versionStr = "1.3.0"; + } - // BASIC, COMFORT etc - may be set from outside. - private String ZUGFeRDConformanceLevel = null; + private class Totals { + private BigDecimal totalNetAmount; + private BigDecimal totalGrossAmount; + private BigDecimal lineTotalAmount; + private BigDecimal totalTaxAmount; - /** - * Data (XML invoice) to be added to the ZUGFeRD PDF. It may be externally - * set, in which case passing a IZUGFeRDExportableTransaction is not - * necessary. By default it is null meaning the caller needs to pass a - * IZUGFeRDExportableTransaction for the XML to be populated. - */ - byte[] zugferdData = null; - private boolean isTest; - IZUGFeRDExportableTransaction trans = null; + public Totals() { + BigDecimal res = BigDecimal.ZERO; + for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { + LineCalc lc = new LineCalc(currentItem); + res = res.add(lc.getItemTotalNetAmount()); + } + // Set line total + this.lineTotalAmount = res; - 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'); + if (trans.getZFAllowances() != null) { + for (IZUGFeRDAllowanceCharge headerAllowance : trans + .getZFAllowances()) { + res = res.subtract(headerAllowance.getTotalAmount()); + } + } - DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(); - otherSymbols.setDecimalSeparator('.'); - DecimalFormat dec = new DecimalFormat("0." + new String(repeat), otherSymbols); - return new BigDecimal(dec.format(value)); + if (trans.getZFLogisticsServiceCharges() != null) { + for (IZUGFeRDAllowanceCharge logisticsServiceCharge : trans + .getZFLogisticsServiceCharges()) { + res = res.add(logisticsServiceCharge.getTotalAmount()); + } + } - } + if (trans.getZFCharges() != null) { + for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) { + res = res.add(charge.getTotalAmount()); + } + } - private BigDecimal vatFormat(BigDecimal value) { - return nDigitFormat(value, 2); - } + // Set total net amount + this.totalNetAmount = res; - private BigDecimal currencyFormat(BigDecimal value) { - return nDigitFormat(value, 2); - } + HashMap VATPercentAmountMap = getVATPercentAmountMap(); + for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) { + VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); + res = res.add(amount.getCalculated()); + } - private BigDecimal priceFormat(BigDecimal value) { - return nDigitFormat(value, 4); - } + // Set total gross amount + this.totalGrossAmount = res; - private BigDecimal quantityFormat(BigDecimal value) { - return nDigitFormat(value, 4); - } + this.totalTaxAmount = this.totalGrossAmount + .subtract(this.totalNetAmount); + } - /** - * All files are PDF/A-3, setConformance refers to the level conformance. - * - * PDF/A-3 has three coformance levels, called "A", "U" and "B". - * - * PDF/A-3-B where B means only visually preservable, U -standard for - * Mustang- means visually and unicode preservable and A means full - * compliance, i.e. visually, unicode and structurally preservable and - * tagged PDF, i.e. useful metainformation for blind people. - * - * Feel free to pass "A" as new level if you know what you are doing :-) - * - * - */ - public void setConformanceLevel(String newLevel) { - conformanceLevel = newLevel; - } + public BigDecimal getTotalNet() { + return totalNetAmount; + } - /** - * enables the flag to indicate a test invoice in the XML structure - * - */ - public void setTest() { - isTest = true; - } + public BigDecimal getTotalGross() { + return totalGrossAmount; + } - /** - * 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) - * - */ - public PDDocumentCatalog PDFmakeA3compliant(PDDocument doc, String producer, String creator, - boolean attachZugferdHeaders) throws IOException, - TransformerException { - String fullProducer = producer + " (via mustangproject.org " + versionStr + ")"; - PDDocumentCatalog cat = doc.getDocumentCatalog(); - PDMetadata metadata = new PDMetadata(doc); - cat.setMetadata(metadata); - // we're using the jempbox org.apache.jempbox.xmp.XMPMetadata version, - // not the xmpbox one - XMPMetadata xmp = new XMPMetadata(); + public BigDecimal getLineTotal() { + return lineTotalAmount; + } - XMPSchemaPDFAId pdfaid = new XMPSchemaPDFAId(xmp); - pdfaid.setAbout(""); //$NON-NLS-1$ - xmp.addSchema(pdfaid); + public BigDecimal getTaxTotal() { + return totalTaxAmount; + } - XMPSchemaDublinCore dc = xmp.addDublinCoreSchema(); - dc.addCreator(creator); - dc.setAbout(""); //$NON-NLS-1$ + } - XMPSchemaBasic xsb = xmp.addBasicSchema(); - xsb.setAbout(""); //$NON-NLS-1$ + // // MAIN CLASS + private String conformanceLevel = "U"; + private String versionStr = "1.3.0"; - xsb.setCreatorTool(creator); - xsb.setCreateDate(GregorianCalendar.getInstance()); - // PDDocumentInformation pdi=doc.getDocumentInformation(); - PDDocumentInformation pdi = new PDDocumentInformation(); - pdi.setProducer(fullProducer); - pdi.setAuthor(creator); - doc.setDocumentInformation(pdi); + // BASIC, COMFORT etc - may be set from outside. + private String ZUGFeRDConformanceLevel = null; - XMPSchemaPDF pdf = xmp.addPDFSchema(); - pdf.setProducer(fullProducer); - pdf.setAbout(""); //$NON-NLS-1$ + /** + * Data (XML invoice) to be added to the ZUGFeRD PDF. It may be externally + * set, in which case passing a IZUGFeRDExportableTransaction is not + * necessary. By default it is null meaning the caller needs to pass a + * IZUGFeRDExportableTransaction for the XML to be populated. + */ + byte[] zugferdData = null; + private boolean isTest; + IZUGFeRDExportableTransaction trans = null; + private boolean ignoreA1Errors; + private PDDocument doc; + private String currency = "EUR"; - /* - // Mandatory: PDF/A3-a is tagged PDF which has to be expressed using a - // MarkInfo dictionary (PDF A/3 Standard sec. 6.7.2.2) - PDMarkInfo markinfo = new PDMarkInfo(); - markinfo.setMarked(true); - doc.getDocumentCatalog().setMarkInfo(markinfo); - */ - /* - * - To be on the safe side, we use level B without Markinfo because we can not - guarantee that the user correctly tagged the templates for the PDF. + 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'); - * */ - pdfaid.setConformance(conformanceLevel);//$NON-NLS-1$ //$NON-NLS-1$ + DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(); + otherSymbols.setDecimalSeparator('.'); + DecimalFormat dec = new DecimalFormat("0." + new String(repeat), + otherSymbols); + return new BigDecimal(dec.format(value)); - pdfaid.setPart(3); + } - if (attachZugferdHeaders) { - addZugferdXMP(xmp); /* - * this is the only line where we do something - * Zugferd-specific, i.e. add PDF metadata - * specifically for Zugferd, not generically for - * a embedded file - */ + private BigDecimal vatFormat(BigDecimal value) { + return nDigitFormat(value, 2); + } - } + private BigDecimal currencyFormat(BigDecimal value) { + return nDigitFormat(value, 2); + } - metadata.importXMPMetadata(xmp); - return cat; - } + private BigDecimal priceFormat(BigDecimal value) { + return nDigitFormat(value, 4); + } - private static final ObjectFactory xmlFactory = new ObjectFactory(); - private final JAXBContext jaxbContext; - private final Marshaller marshaller; - private static final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); //$NON-NLS-1$ - private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$ - - private Totals totals; + private BigDecimal quantityFormat(BigDecimal value) { + return nDigitFormat(value, 4); + } - private String getZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) throws JAXBException { - this.trans = trans; - - this.totals = new Totals(); + /** + * All files are PDF/A-3, setConformance refers to the level conformance. + * + * PDF/A-3 has three coformance levels, called "A", "U" and "B". + * + * PDF/A-3-B where B means only visually preservable, U -standard for + * Mustang- means visually and unicode preservable and A means full + * compliance, i.e. visually, unicode and structurally preservable and + * tagged PDF, i.e. useful metainformation for blind people. + * + * Feel free to pass "A" as new level if you know what you are doing :-) + * + * + */ + public void setConformanceLevel(String newLevel) { + conformanceLevel = newLevel; + } - CrossIndustryDocumentType invoice = xmlFactory.createCrossIndustryDocumentType(); + /** + * enables the flag to indicate a test invoice in the XML structure + * + */ + public void setTest() { + isTest = true; + } - invoice.setSpecifiedExchangedDocumentContext(this.getDocumentContext()); - invoice.setHeaderExchangedDocument(this.getDocument()); - invoice.setSpecifiedSupplyChainTradeTransaction(this.getTradeTransaction()); + public void ignoreA1Errors() { + ignoreA1Errors = true; + } - JAXBElement jaxElement = xmlFactory.createCrossIndustryDocument(invoice); + private boolean getA1ParserValidationResult(PreflightParser parser) { + ValidationResult result = null; - ByteArrayOutputStream outputXml = new ByteArrayOutputStream(); - marshaller.marshal(jaxElement, outputXml); + try { - return outputXml.toString(); - } + /* + * Parse the PDF file with PreflightParser that inherits from the + * NonSequentialParser. Some additional controls are present to + * check a set of PDF/A requirements. (Stream length consistency, + * EOL after some Keyword...) + */ + parser.parse(); - private ExchangedDocumentContextType getDocumentContext() { + /* + * Once the syntax validation is done, the parser can provide a + * PreflightDocument (that inherits from PDDocument) This document + * process the end of PDF/A validation. + */ + PreflightDocument document = parser.getPreflightDocument(); + document.validate(); - ExchangedDocumentContextType context = xmlFactory.createExchangedDocumentContextType(); - DocumentContextParameterType contextParameter = xmlFactory.createDocumentContextParameterType(); - IDType idType = xmlFactory.createIDType(); - idType.setValue(DocumentContextParameterType.COMFORT); - contextParameter.setID(idType); - context.getGuidelineSpecifiedDocumentContextParameter().add(contextParameter); - - IndicatorType testIndicator = xmlFactory.createIndicatorType(); - testIndicator.setIndicator(isTest); - context.setTestIndicator(testIndicator); + // Get validation result + result = document.getResult(); + document.close(); - return context; - } + } catch (ValidationException e) { + /* + * the parse method can throw a SyntaxValidationException if the PDF + * file can't be parsed. In this case, the exception contains an + * instance of ValidationResult + */ + return false; + } catch (IOException e) { + return false; + } - private ExchangedDocumentType getDocument() { + // display validation result + return result.isValid(); - ExchangedDocumentType document = xmlFactory.createExchangedDocumentType(); + } - IDType id = xmlFactory.createIDType(); - id.setValue(trans.getNumber()); - document.setID(id); + public boolean isValidA1(String filename) { + FileDataSource fd = new FileDataSource(filename); + PreflightParser parser; + try { + parser = new PreflightParser(fd); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } - DateTimeType issueDateTime = xmlFactory.createDateTimeType(); - DateTimeType.DateTimeString issueDateTimeString = xmlFactory.createDateTimeTypeDateTimeString(); - issueDateTimeString.setFormat(DateTimeType.DateTimeString.DATE); - issueDateTimeString.setValue(zugferdDateFormat.format(trans.getIssueDate())); - issueDateTime.setDateTimeString(issueDateTimeString); - document.setIssueDateTime(issueDateTime); + return getA1ParserValidationResult(parser); + } - DocumentCodeType documentCodeType = xmlFactory.createDocumentCodeType(); - documentCodeType.setValue(DocumentCodeType.INVOICE); - document.setTypeCode(documentCodeType); + /*** + * Will return a boolean if the inputstream is valid PDF/A-1 and close the input stream + * @param file + * @return + */ + public boolean isValidA1(InputStream file) { + + ByteArrayDataSource fd; + try { + fd = new ByteArrayDataSource(file); + PreflightParser parser = new PreflightParser(fd); + return getA1ParserValidationResult(parser); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } TextType name = xmlFactory.createTextType(); name.setValue("RECHNUNG"); @@ -440,785 +421,1139 @@ public class ZUGFeRDExporter { document.getIncludedNote().add(referenceInfo); } - return document; - } + public void loadPDFA3(String filename) { + + try { + doc = PDDocument.load(filename); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public void loadPDFA3(InputStream file) { + + try { + doc = PDDocument.load(file); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + /** + * 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) + * + */ + public PDDocumentCatalog PDFmakeA3compliant(String filename, + String producer, String creator, boolean attachZugferdHeaders) + throws IOException, TransformerException { + + if (!ignoreA1Errors && !isValidA1(filename)) { + throw new IOException("File is not a valid PDF/A-1 input file"); + } + loadPDFA3(filename); + + return makeDocPDFA3compliant(producer, creator, attachZugferdHeaders); + } + + public PDDocumentCatalog PDFmakeA3compliant(InputStream file, + String producer, String creator, boolean attachZugferdHeaders) + throws IOException, TransformerException { + /* cache the file content in memory, unfortunately the next step, isValidA1, + * will close the input stream but the step thereafter (loadPDFA3) needs + * and open one*/ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + int n = 0; + while ((n = file.read(buf)) >= 0) + baos.write(buf, 0, n); + byte[] content = baos.toByteArray(); + + InputStream is1 = new ByteArrayInputStream(content); + if (!ignoreA1Errors && !isValidA1(is1)) { + throw new IOException("File is not a valid PDF/A-1 input file"); + } + InputStream is2 = new ByteArrayInputStream(content); + loadPDFA3(is2); + + return makeDocPDFA3compliant(producer, creator, attachZugferdHeaders); + } + private PDDocumentCatalog makeDocPDFA3compliant(String producer, + String creator, boolean attachZugferdHeaders) throws IOException, + TransformerException { + String fullProducer = producer + " (via mustangproject.org " + + versionStr + ")"; + + PDDocumentCatalog cat = doc.getDocumentCatalog(); + PDMetadata metadata = new PDMetadata(doc); + cat.setMetadata(metadata); + // we're using the jempbox org.apache.jempbox.xmp.XMPMetadata version, + // not the xmpbox one + XMPMetadata xmp = new XMPMetadata(); + + XMPSchemaPDFAId pdfaid = new XMPSchemaPDFAId(xmp); + pdfaid.setAbout(""); //$NON-NLS-1$ + xmp.addSchema(pdfaid); + + XMPSchemaDublinCore dc = xmp.addDublinCoreSchema(); + dc.addCreator(creator); + dc.setAbout(""); //$NON-NLS-1$ + + XMPSchemaBasic xsb = xmp.addBasicSchema(); + xsb.setAbout(""); //$NON-NLS-1$ + + xsb.setCreatorTool(creator); + xsb.setCreateDate(GregorianCalendar.getInstance()); + // PDDocumentInformation pdi=doc.getDocumentInformation(); + PDDocumentInformation pdi = new PDDocumentInformation(); + pdi.setProducer(fullProducer); + pdi.setAuthor(creator); + doc.setDocumentInformation(pdi); + + XMPSchemaPDF pdf = xmp.addPDFSchema(); + pdf.setProducer(fullProducer); + pdf.setAbout(""); //$NON-NLS-1$ + + /* + * // Mandatory: PDF/A3-a is tagged PDF which has to be expressed using + * a // MarkInfo dictionary (PDF A/3 Standard sec. 6.7.2.2) PDMarkInfo + * markinfo = new PDMarkInfo(); markinfo.setMarked(true); + * doc.getDocumentCatalog().setMarkInfo(markinfo); + */ + /* + * + * To be on the safe side, we use level B without Markinfo because we + * can not guarantee that the user correctly tagged the templates for + * the PDF. + */ + pdfaid.setConformance(conformanceLevel);//$NON-NLS-1$ //$NON-NLS-1$ + + pdfaid.setPart(3); + + if (attachZugferdHeaders) { + addZugferdXMP(xmp); /* + * this is the only line where we do something + * Zugferd-specific, i.e. add PDF metadata + * specifically for Zugferd, not generically for + * a embedded file + */ + + } + + metadata.importXMPMetadata(xmp); + return cat; + } + + private static final ObjectFactory xmlFactory = new ObjectFactory(); + private final JAXBContext jaxbContext; + private final Marshaller marshaller; + private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat( + "yyyyMMdd"); //$NON-NLS-1$ + + private Totals totals; + + private String getZugferdXMLForTransaction( + IZUGFeRDExportableTransaction trans) throws JAXBException { + this.trans = trans; + this.totals = new Totals(); + currency = trans.getCurrency(); + + CrossIndustryDocumentType invoice = xmlFactory + .createCrossIndustryDocumentType(); + + invoice.setSpecifiedExchangedDocumentContext(this.getDocumentContext()); + invoice.setHeaderExchangedDocument(this.getDocument()); + invoice.setSpecifiedSupplyChainTradeTransaction(this + .getTradeTransaction()); + + JAXBElement jaxElement = xmlFactory + .createCrossIndustryDocument(invoice); + + ByteArrayOutputStream outputXml = new ByteArrayOutputStream(); + marshaller.marshal(jaxElement, outputXml); + + return outputXml.toString(); + } + + private ExchangedDocumentContextType getDocumentContext() { + + ExchangedDocumentContextType context = xmlFactory + .createExchangedDocumentContextType(); + DocumentContextParameterType contextParameter = xmlFactory + .createDocumentContextParameterType(); + IDType idType = xmlFactory.createIDType(); + idType.setValue(DocumentContextParameterType.EXTENDED); + contextParameter.setID(idType); + context.getGuidelineSpecifiedDocumentContextParameter().add( + contextParameter); + + IndicatorType testIndicator = xmlFactory.createIndicatorType(); + testIndicator.setIndicator(isTest); + context.setTestIndicator(testIndicator); + + return context; + } + + private ExchangedDocumentType getDocument() { + + ExchangedDocumentType document = xmlFactory + .createExchangedDocumentType(); + + IDType id = xmlFactory.createIDType(); + id.setValue(trans.getNumber()); + document.setID(id); + + DateTimeType issueDateTime = xmlFactory.createDateTimeType(); + DateTimeType.DateTimeString issueDateTimeString = xmlFactory + .createDateTimeTypeDateTimeString(); + issueDateTimeString.setFormat(DateTimeType.DateTimeString.DATE); + issueDateTimeString.setValue(zugferdDateFormat.format(trans + .getIssueDate())); + issueDateTime.setDateTimeString(issueDateTimeString); + document.setIssueDateTime(issueDateTime); + + DocumentCodeType documentCodeType = xmlFactory.createDocumentCodeType(); + documentCodeType.setValue(DocumentCodeType.INVOICE); + document.setTypeCode(documentCodeType); + + TextType name = xmlFactory.createTextType(); + name.setValue("RECHNUNG"); + document.getName().add(name); + + if (trans.getOwnOrganisationFullPlaintextInfo() != null) { + NoteType regularInfo = xmlFactory.createNoteType(); + CodeType regularInfoSubjectCode = xmlFactory.createCodeType(); + regularInfoSubjectCode.setValue(NoteType.REGULARINFO); + regularInfo.setSubjectCode(regularInfoSubjectCode); + TextType regularInfoContent = xmlFactory.createTextType(); + regularInfoContent.setValue(trans + .getOwnOrganisationFullPlaintextInfo()); + regularInfo.getContent().add(regularInfoContent); + document.getIncludedNote().add(regularInfo); + } + + return document; + } + + private SupplyChainTradeTransactionType getTradeTransaction() { + + SupplyChainTradeTransactionType transaction = xmlFactory + .createSupplyChainTradeTransactionType(); + transaction.getApplicableSupplyChainTradeAgreement().add( + this.getTradeAgreement()); + transaction.setApplicableSupplyChainTradeDelivery(this + .getTradeDelivery()); + transaction.setApplicableSupplyChainTradeSettlement(this + .getTradeSettlement()); + transaction.getIncludedSupplyChainTradeLineItem().addAll( + this.getLineItems()); + + return transaction; + } + + private SupplyChainTradeAgreementType getTradeAgreement() { + + SupplyChainTradeAgreementType tradeAgreement = xmlFactory + .createSupplyChainTradeAgreementType(); + + tradeAgreement.setBuyerTradeParty(this.getBuyer()); + tradeAgreement.setSellerTradeParty(this.getSeller()); + + return tradeAgreement; + } + + private TradePartyType getBuyer() { + + TradePartyType buyerTradeParty = xmlFactory.createTradePartyType(); + TextType buyerName = xmlFactory.createTextType(); + buyerName.setValue(trans.getRecipient().getName()); + buyerTradeParty.setName(buyerName); + + TradeAddressType buyerAddressType = xmlFactory.createTradeAddressType(); + TextType buyerCityName = xmlFactory.createTextType(); + buyerCityName.setValue(trans.getRecipient().getLocation()); + buyerAddressType.setCityName(buyerCityName); + + CountryIDType buyerCountryId = xmlFactory.createCountryIDType(); + buyerCountryId.setValue(trans.getRecipient().getCountry()); + buyerAddressType.setCountryID(buyerCountryId); + + TextType buyerAddress = xmlFactory.createTextType(); + buyerAddress.setValue(trans.getRecipient().getStreet()); + buyerAddressType.setLineOne(buyerAddress); + + CodeType buyerPostcode = xmlFactory.createCodeType(); + buyerPostcode.setValue(trans.getRecipient().getZIP()); + buyerAddressType.getPostcodeCode().add(buyerPostcode); + + buyerTradeParty.setPostalTradeAddress(buyerAddressType); + + // Ust-ID + TaxRegistrationType buyerTaxRegistration = xmlFactory + .createTaxRegistrationType(); + IDType buyerUstId = xmlFactory.createIDType(); + buyerUstId.setValue(trans.getRecipient().getVATID()); + buyerUstId.setSchemeID(TaxRegistrationType.USTID); + buyerTaxRegistration.setID(buyerUstId); + buyerTradeParty.getSpecifiedTaxRegistration().add(buyerTaxRegistration); + + return buyerTradeParty; + } + + private TradePartyType getSeller() { + + TradePartyType sellerTradeParty = xmlFactory.createTradePartyType(); + TextType sellerName = xmlFactory.createTextType(); + sellerName.setValue(trans.getOwnOrganisationName()); + sellerTradeParty.setName(sellerName); + + TradeAddressType sellerAddressType = xmlFactory + .createTradeAddressType(); + TextType sellerCityName = xmlFactory.createTextType(); + sellerCityName.setValue(trans.getOwnLocation()); + sellerAddressType.setCityName(sellerCityName); + + CountryIDType sellerCountryId = xmlFactory.createCountryIDType(); + sellerCountryId.setValue(trans.getOwnCountry()); + sellerAddressType.setCountryID(sellerCountryId); + + TextType sellerAddress = xmlFactory.createTextType(); + sellerAddress.setValue(trans.getOwnStreet()); + sellerAddressType.setLineOne(sellerAddress); + + CodeType sellerPostcode = xmlFactory.createCodeType(); + sellerPostcode.setValue(trans.getOwnZIP()); + sellerAddressType.getPostcodeCode().add(sellerPostcode); + + sellerTradeParty.setPostalTradeAddress(sellerAddressType); + + // Steuernummer + TaxRegistrationType sellerTaxRegistration = xmlFactory + .createTaxRegistrationType(); + IDType sellerTaxId = xmlFactory.createIDType(); + sellerTaxId.setValue(trans.getOwnTaxID()); + sellerTaxId.setSchemeID(TaxRegistrationType.TAXID); + sellerTaxRegistration.setID(sellerTaxId); + sellerTradeParty.getSpecifiedTaxRegistration().add( + sellerTaxRegistration); + + // Ust-ID + sellerTaxRegistration = xmlFactory.createTaxRegistrationType(); + IDType sellerUstId = xmlFactory.createIDType(); + sellerUstId.setValue(trans.getOwnVATID()); + sellerUstId.setSchemeID(TaxRegistrationType.USTID); + sellerTaxRegistration.setID(sellerUstId); + sellerTradeParty.getSpecifiedTaxRegistration().add( + sellerTaxRegistration); + + return sellerTradeParty; + } + + private SupplyChainTradeDeliveryType getTradeDelivery() { + + SupplyChainTradeDeliveryType tradeDelivery = xmlFactory + .createSupplyChainTradeDeliveryType(); + SupplyChainEventType deliveryEvent = xmlFactory + .createSupplyChainEventType(); + DateTimeType deliveryDate = xmlFactory.createDateTimeType(); + DateTimeType.DateTimeString deliveryDateString = xmlFactory + .createDateTimeTypeDateTimeString(); + deliveryDateString.setFormat(DateTimeType.DateTimeString.DATE); + deliveryDateString.setValue(zugferdDateFormat.format(trans + .getDeliveryDate())); + deliveryDate.setDateTimeString(deliveryDateString); + deliveryEvent.getOccurrenceDateTime().add(deliveryDate); + tradeDelivery.getActualDeliverySupplyChainEvent().add(deliveryEvent); + + return tradeDelivery; + } + + private SupplyChainTradeSettlementType getTradeSettlement() { + SupplyChainTradeSettlementType tradeSettlement = xmlFactory + .createSupplyChainTradeSettlementType(); + + TextType paymentReference = xmlFactory.createTextType(); + paymentReference.setValue(trans.getNumber()); + tradeSettlement.getPaymentReference().add(paymentReference); + + CodeType currencyCode = xmlFactory.createCodeType(); + currencyCode.setValue(currency); + tradeSettlement.setInvoiceCurrencyCode(currencyCode); + + tradeSettlement.getSpecifiedTradeSettlementPaymentMeans().add( + this.getPaymentData()); + tradeSettlement.getApplicableTradeTax().addAll(this.getTradeTax()); + tradeSettlement.getSpecifiedTradePaymentTerms().addAll( + this.getPaymentTerms()); + if (trans.getZFAllowances() != null) { + tradeSettlement.getSpecifiedTradeAllowanceCharge().addAll( + this.getHeaderAllowances()); + } + if (trans.getZFLogisticsServiceCharges() != null) { + tradeSettlement.getSpecifiedLogisticsServiceCharge().addAll( + this.getHeaderLogisticsServiceCharges()); + } + if (trans.getZFCharges() != null) { + tradeSettlement.getSpecifiedTradeAllowanceCharge().addAll( + this.getHeaderCharges()); + } + + tradeSettlement.setSpecifiedTradeSettlementMonetarySummation(this + .getMonetarySummation()); + + return tradeSettlement; + } + + private TradeSettlementPaymentMeansType getPaymentData() { + TradeSettlementPaymentMeansType paymentData = xmlFactory + .createTradeSettlementPaymentMeansType(); + PaymentMeansCodeType paymentDataType = xmlFactory + .createPaymentMeansCodeType(); + paymentDataType.setValue(PaymentMeansCodeType.BANKACCOUNT); + paymentData.setTypeCode(paymentDataType); + + TextType paymentInfo = xmlFactory.createTextType(); + String paymentInfoText = trans.getOwnPaymentInfoText(); + if (paymentInfoText == null) { + paymentInfoText = ""; + } + paymentInfo.setValue(paymentInfoText); + paymentData.getInformation().add(paymentInfo); + + CreditorFinancialAccountType bankAccount = xmlFactory + .createCreditorFinancialAccountType(); + IDType iban = xmlFactory.createIDType(); + iban.setValue(trans.getOwnIBAN()); + bankAccount.setIBANID(iban); + paymentData.setPayeePartyCreditorFinancialAccount(bankAccount); + + CreditorFinancialInstitutionType bankData = xmlFactory + .createCreditorFinancialInstitutionType(); + IDType bicId = xmlFactory.createIDType(); + bicId.setValue(trans.getOwnBIC()); + bankData.setBICID(bicId); + TextType bankName = xmlFactory.createTextType(); + bankName.setValue(trans.getOwnBankName()); + bankData.setName(bankName); + paymentData.setPayeeSpecifiedCreditorFinancialInstitution(bankData); + return paymentData; + } + + private Collection getTradeTax() { + List tradeTaxTypes = new ArrayList(); + + HashMap VATPercentAmountMap = this + .getVATPercentAmountMap(); + for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) { + + TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); + TaxTypeCodeType taxTypeCode = xmlFactory.createTaxTypeCodeType(); + taxTypeCode.setValue(TaxTypeCodeType.SALESTAX); + tradeTax.setTypeCode(taxTypeCode); + + TaxCategoryCodeType taxCategoryCode = xmlFactory + .createTaxCategoryCodeType(); + taxCategoryCode.setValue(TaxCategoryCodeType.STANDARDRATE); + tradeTax.setCategoryCode(taxCategoryCode); + + VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); + + PercentType taxPercent = xmlFactory.createPercentType(); + taxPercent.setValue(vatFormat(currentTaxPercent)); + tradeTax.setApplicablePercent(taxPercent); + + AmountType calculatedTaxAmount = xmlFactory.createAmountType(); + calculatedTaxAmount.setCurrencyID(currency); + calculatedTaxAmount + .setValue(currencyFormat(amount.getCalculated())); + tradeTax.getCalculatedAmount().add(calculatedTaxAmount); + + AmountType basisTaxAmount = xmlFactory.createAmountType(); + basisTaxAmount.setCurrencyID(currency); + basisTaxAmount.setValue(currencyFormat(amount.getBasis())); + tradeTax.getBasisAmount().add(basisTaxAmount); + + tradeTaxTypes.add(tradeTax); + } + + return tradeTaxTypes; + } + + private Collection getHeaderAllowances() { + List headerAllowances = new ArrayList(); + + for (IZUGFeRDAllowanceCharge iAllowance : trans.getZFAllowances()) { + + TradeAllowanceChargeType allowance = xmlFactory + .createTradeAllowanceChargeType(); + + IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); + chargeIndicator.setIndicator(false); + allowance.setChargeIndicator(chargeIndicator); + + AmountType actualAmount = xmlFactory.createAmountType(); + actualAmount.setCurrencyID(currency); + actualAmount.setValue(currencyFormat(iAllowance.getTotalAmount())); + allowance.getActualAmount().add(actualAmount); + + TextType reason = xmlFactory.createTextType(); + reason.setValue(iAllowance.getReason()); + allowance.setReason(reason); + + TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); + + PercentType vatPercent = xmlFactory.createPercentType(); + vatPercent.setValue(currencyFormat(iAllowance.getTaxPercent())); + tradeTax.setApplicablePercent(vatPercent); + + /* + * Only in extended AmountType basisAmount = + * xmlFactory.createAmountType(); + * basisAmount.setCurrencyID(trans.getInvoiceCurrency()); + * basisAmount.setValue(amount.getBasis()); + * allowance.setBasisAmount(basisAmount); + */ + TaxCategoryCodeType taxType = xmlFactory + .createTaxCategoryCodeType(); + taxType.setValue(TaxCategoryCodeType.STANDARDRATE); + tradeTax.setCategoryCode(taxType); + + TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); + taxCode.setValue(TaxTypeCodeType.SALESTAX); + tradeTax.setTypeCode(taxCode); + + allowance.getCategoryTradeTax().add(tradeTax); + headerAllowances.add(allowance); + + } + + return headerAllowances; + } + + private Collection getHeaderCharges() { + List headerCharges = new ArrayList(); - private SupplyChainTradeTransactionType getTradeTransaction() { + for (IZUGFeRDAllowanceCharge iCharge : trans.getZFCharges()) { - SupplyChainTradeTransactionType transaction = xmlFactory.createSupplyChainTradeTransactionType(); - transaction.getApplicableSupplyChainTradeAgreement().add(this.getTradeAgreement()); - transaction.setApplicableSupplyChainTradeDelivery(this.getTradeDelivery()); - transaction.setApplicableSupplyChainTradeSettlement(this.getTradeSettlement()); - transaction.getIncludedSupplyChainTradeLineItem().addAll(this.getLineItems()); + TradeAllowanceChargeType charge = xmlFactory + .createTradeAllowanceChargeType(); - return transaction; - } + IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); + chargeIndicator.setIndicator(true); + charge.setChargeIndicator(chargeIndicator); + + AmountType actualAmount = xmlFactory.createAmountType(); + actualAmount.setCurrencyID(currency); + actualAmount.setValue(currencyFormat(iCharge.getTotalAmount())); + charge.getActualAmount().add(actualAmount); + + TextType reason = xmlFactory.createTextType(); + reason.setValue(iCharge.getReason()); + charge.setReason(reason); + + TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); + + PercentType vatPercent = xmlFactory.createPercentType(); + vatPercent.setValue(currencyFormat(iCharge.getTaxPercent())); + tradeTax.setApplicablePercent(vatPercent); - private SupplyChainTradeAgreementType getTradeAgreement() { - - SupplyChainTradeAgreementType tradeAgreement = xmlFactory.createSupplyChainTradeAgreementType(); - - tradeAgreement.setBuyerTradeParty(this.getBuyer()); - tradeAgreement.setSellerTradeParty(this.getSeller()); - - return tradeAgreement; - } - - private TradePartyType getBuyer() { - - TradePartyType buyerTradeParty = xmlFactory.createTradePartyType(); - TextType buyerName = xmlFactory.createTextType(); - buyerName.setValue(trans.getRecipient().getName()); - buyerTradeParty.setName(buyerName); - - TradeAddressType buyerAddressType = xmlFactory.createTradeAddressType(); - TextType buyerCityName = xmlFactory.createTextType(); - buyerCityName.setValue(trans.getRecipient().getLocation()); - buyerAddressType.setCityName(buyerCityName); - - CountryIDType buyerCountryId = xmlFactory.createCountryIDType(); - buyerCountryId.setValue(trans.getRecipient().getCountry()); - buyerAddressType.setCountryID(buyerCountryId); - - TextType buyerAddress = xmlFactory.createTextType(); - buyerAddress.setValue(trans.getRecipient().getStreet()); - buyerAddressType.setLineOne(buyerAddress); - - CodeType buyerPostcode = xmlFactory.createCodeType(); - buyerPostcode.setValue(trans.getRecipient().getZIP()); - buyerAddressType.getPostcodeCode().add(buyerPostcode); - - buyerTradeParty.setPostalTradeAddress(buyerAddressType); - - // Ust-ID - TaxRegistrationType buyerTaxRegistration = xmlFactory.createTaxRegistrationType(); - IDType buyerUstId = xmlFactory.createIDType(); - buyerUstId.setValue(trans.getRecipient().getVATID()); - buyerUstId.setSchemeID(TaxRegistrationType.USTID); - buyerTaxRegistration.setID(buyerUstId); - buyerTradeParty.getSpecifiedTaxRegistration().add(buyerTaxRegistration); - - return buyerTradeParty; - } - - private TradePartyType getSeller() { - - TradePartyType sellerTradeParty = xmlFactory.createTradePartyType(); - TextType sellerName = xmlFactory.createTextType(); - sellerName.setValue(trans.getOwnOrganisationName()); - sellerTradeParty.setName(sellerName); - - TradeAddressType sellerAddressType = xmlFactory.createTradeAddressType(); - TextType sellerCityName = xmlFactory.createTextType(); - sellerCityName.setValue(trans.getOwnLocation()); - sellerAddressType.setCityName(sellerCityName); - - CountryIDType sellerCountryId = xmlFactory.createCountryIDType(); - sellerCountryId.setValue(trans.getOwnCountry()); - sellerAddressType.setCountryID(sellerCountryId); - - TextType sellerAddress = xmlFactory.createTextType(); - sellerAddress.setValue(trans.getOwnStreet()); - sellerAddressType.setLineOne(sellerAddress); - - CodeType sellerPostcode = xmlFactory.createCodeType(); - sellerPostcode.setValue(trans.getOwnZIP()); - sellerAddressType.getPostcodeCode().add(sellerPostcode); - - sellerTradeParty.setPostalTradeAddress(sellerAddressType); - - // Steuernummer - TaxRegistrationType sellerTaxRegistration = xmlFactory.createTaxRegistrationType(); - IDType sellerTaxId = xmlFactory.createIDType(); - sellerTaxId.setValue(trans.getOwnTaxID()); - sellerTaxId.setSchemeID(TaxRegistrationType.TAXID); - sellerTaxRegistration.setID(sellerTaxId); - sellerTradeParty.getSpecifiedTaxRegistration().add(sellerTaxRegistration); - - // Ust-ID - sellerTaxRegistration = xmlFactory.createTaxRegistrationType(); - IDType sellerUstId = xmlFactory.createIDType(); - sellerUstId.setValue(trans.getOwnVATID()); - sellerUstId.setSchemeID(TaxRegistrationType.USTID); - sellerTaxRegistration.setID(sellerUstId); - sellerTradeParty.getSpecifiedTaxRegistration().add(sellerTaxRegistration); - - return sellerTradeParty; - } - - private SupplyChainTradeDeliveryType getTradeDelivery() { - - SupplyChainTradeDeliveryType tradeDelivery = xmlFactory.createSupplyChainTradeDeliveryType(); - SupplyChainEventType deliveryEvent = xmlFactory.createSupplyChainEventType(); - DateTimeType deliveryDate = xmlFactory.createDateTimeType(); - DateTimeType.DateTimeString deliveryDateString = xmlFactory.createDateTimeTypeDateTimeString(); - deliveryDateString.setFormat(DateTimeType.DateTimeString.DATE); - deliveryDateString.setValue(zugferdDateFormat.format(trans.getDeliveryDate())); - deliveryDate.setDateTimeString(deliveryDateString); - deliveryEvent.getOccurrenceDateTime().add(deliveryDate); - tradeDelivery.getActualDeliverySupplyChainEvent().add(deliveryEvent); - - return tradeDelivery; - } - - private SupplyChainTradeSettlementType getTradeSettlement() { - SupplyChainTradeSettlementType tradeSettlement = xmlFactory.createSupplyChainTradeSettlementType(); - - TextType paymentReference = xmlFactory.createTextType(); - paymentReference.setValue(trans.getNumber()); - tradeSettlement.getPaymentReference().add(paymentReference); - - CodeType currencyCode = xmlFactory.createCodeType(); - currencyCode.setValue(trans.getInvoiceCurrency()); - tradeSettlement.setInvoiceCurrencyCode(currencyCode); - - tradeSettlement.getSpecifiedTradeSettlementPaymentMeans().add(this.getPaymentData()); - tradeSettlement.getApplicableTradeTax().addAll(this.getTradeTax()); - tradeSettlement.getSpecifiedTradePaymentTerms().addAll(this.getPaymentTerms()); - if(trans.getZFAllowances() != null){ - tradeSettlement.getSpecifiedTradeAllowanceCharge().addAll(this.getHeaderAllowances()); - } - if(trans.getZFLogisticsServiceCharges()!= null){ - tradeSettlement.getSpecifiedLogisticsServiceCharge().addAll(this.getHeaderLogisticsServiceCharges()); - } - if(trans.getZFCharges()!= null){ - tradeSettlement.getSpecifiedTradeAllowanceCharge().addAll(this.getHeaderCharges()); - } - - tradeSettlement.setSpecifiedTradeSettlementMonetarySummation(this.getMonetarySummation()); - - return tradeSettlement; - } - - private TradeSettlementPaymentMeansType getPaymentData() { - TradeSettlementPaymentMeansType paymentData = xmlFactory.createTradeSettlementPaymentMeansType(); - PaymentMeansCodeType paymentDataType = xmlFactory.createPaymentMeansCodeType(); - paymentDataType.setValue(PaymentMeansCodeType.BANKACCOUNT); - paymentData.setTypeCode(paymentDataType); - - TextType paymentInfo = xmlFactory.createTextType(); - paymentInfo.setValue(trans.getOwnPaymentInfoText()); - paymentData.getInformation().add(paymentInfo); - - CreditorFinancialAccountType bankAccount = xmlFactory.createCreditorFinancialAccountType(); - IDType iban = xmlFactory.createIDType(); - iban.setValue(trans.getOwnIBAN()); - bankAccount.setIBANID(iban); - paymentData.setPayeePartyCreditorFinancialAccount(bankAccount); - - CreditorFinancialInstitutionType bankData = xmlFactory.createCreditorFinancialInstitutionType(); - IDType bicId = xmlFactory.createIDType(); - bicId.setValue(trans.getOwnBIC()); - bankData.setBICID(bicId); - TextType bankName = xmlFactory.createTextType(); - bankName.setValue(trans.getOwnBankName()); - bankData.setName(bankName); - paymentData.setPayeeSpecifiedCreditorFinancialInstitution(bankData); - return paymentData; - } - - private Collection getTradeTax() { - List tradeTaxTypes = new ArrayList(); - - HashMap VATPercentAmountMap = this.getVATPercentAmountMap(); - for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) { - - TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); - TaxTypeCodeType taxTypeCode = xmlFactory.createTaxTypeCodeType(); - taxTypeCode.setValue(TaxTypeCodeType.SALESTAX); - tradeTax.setTypeCode(taxTypeCode); - - TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType(); - taxCategoryCode.setValue(TaxCategoryCodeType.STANDARDRATE); - tradeTax.setCategoryCode(taxCategoryCode); - - VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); - - PercentType taxPercent = xmlFactory.createPercentType(); - taxPercent.setValue(vatFormat(currentTaxPercent)); - tradeTax.setApplicablePercent(taxPercent); - - AmountType calculatedTaxAmount = xmlFactory.createAmountType(); - calculatedTaxAmount.setCurrencyID(trans.getInvoiceCurrency()); - calculatedTaxAmount.setValue(currencyFormat(amount.getCalculated())); - tradeTax.getCalculatedAmount().add(calculatedTaxAmount); - - AmountType basisTaxAmount = xmlFactory.createAmountType(); - basisTaxAmount.setCurrencyID(trans.getInvoiceCurrency()); - basisTaxAmount.setValue(currencyFormat(amount.getBasis())); - tradeTax.getBasisAmount().add(basisTaxAmount); - - tradeTaxTypes.add(tradeTax); - } - - return tradeTaxTypes; - } - - private Collection getHeaderAllowances() { - List headerAllowances = new ArrayList(); - - for (IZUGFeRDAllowanceCharge iAllowance : trans.getZFAllowances()) { - - TradeAllowanceChargeType allowance = xmlFactory.createTradeAllowanceChargeType(); - - IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); - chargeIndicator.setIndicator(false); - allowance.setChargeIndicator(chargeIndicator); - - AmountType actualAmount = xmlFactory.createAmountType(); - actualAmount.setCurrencyID(trans.getInvoiceCurrency()); - actualAmount.setValue(currencyFormat(iAllowance.getTotalAmount())); - allowance.getActualAmount().add(actualAmount); - - TextType reason = xmlFactory.createTextType(); - reason.setValue(iAllowance.getReason()); - allowance.setReason(reason); - - TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); - - PercentType vatPercent = xmlFactory.createPercentType(); - vatPercent.setValue(currencyFormat(iAllowance.getTaxPercent())); - tradeTax.setApplicablePercent(vatPercent); - - /* Only in extended - AmountType basisAmount = xmlFactory.createAmountType(); - basisAmount.setCurrencyID(trans.getInvoiceCurrency()); - basisAmount.setValue(amount.getBasis()); - allowance.setBasisAmount(basisAmount); - */ - TaxCategoryCodeType taxType = xmlFactory.createTaxCategoryCodeType(); - taxType.setValue(TaxCategoryCodeType.STANDARDRATE); - tradeTax.setCategoryCode(taxType); - - TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); - taxCode.setValue(TaxTypeCodeType.SALESTAX); - tradeTax.setTypeCode(taxCode); - - allowance.getCategoryTradeTax().add(tradeTax); - headerAllowances.add(allowance); - - } - - return headerAllowances; - } - - private Collection getHeaderCharges() { - List headerCharges = new ArrayList(); - - for (IZUGFeRDAllowanceCharge iCharge : trans.getZFCharges()) { - - TradeAllowanceChargeType charge = xmlFactory.createTradeAllowanceChargeType(); - - IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); - chargeIndicator.setIndicator(true); - charge.setChargeIndicator(chargeIndicator); - - AmountType actualAmount = xmlFactory.createAmountType(); - actualAmount.setCurrencyID(trans.getInvoiceCurrency()); - actualAmount.setValue(currencyFormat(iCharge.getTotalAmount())); - charge.getActualAmount().add(actualAmount); - - TextType reason = xmlFactory.createTextType(); - reason.setValue(iCharge.getReason()); - charge.setReason(reason); - - TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); - - PercentType vatPercent = xmlFactory.createPercentType(); - vatPercent.setValue(currencyFormat(iCharge.getTaxPercent())); - tradeTax.setApplicablePercent(vatPercent); - - /* Only in extended - AmountType basisAmount = xmlFactory.createAmountType(); - basisAmount.setCurrencyID(trans.getInvoiceCurrency()); - basisAmount.setValue(amount.getBasis()); - allowance.setBasisAmount(basisAmount); - */ - TaxCategoryCodeType taxType = xmlFactory.createTaxCategoryCodeType(); - taxType.setValue(TaxCategoryCodeType.STANDARDRATE); - tradeTax.setCategoryCode(taxType); - - TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); - taxCode.setValue(TaxTypeCodeType.SALESTAX); - tradeTax.setTypeCode(taxCode); - - charge.getCategoryTradeTax().add(tradeTax); - headerCharges.add(charge); - - } - - return headerCharges; - } - - private Collection getHeaderLogisticsServiceCharges() { - List headerServiceCharge = new ArrayList(); - - for (IZUGFeRDAllowanceCharge iServiceCharge : trans.getZFLogisticsServiceCharges()) { - - LogisticsServiceChargeType serviceCharge = xmlFactory.createLogisticsServiceChargeType(); - - AmountType actualAmount = xmlFactory.createAmountType(); - actualAmount.setCurrencyID(trans.getInvoiceCurrency()); - actualAmount.setValue(currencyFormat(iServiceCharge.getTotalAmount())); - serviceCharge.getAppliedAmount().add(actualAmount); - - TextType reason = xmlFactory.createTextType(); - reason.setValue(iServiceCharge.getReason()); - serviceCharge.getDescription().add(reason); - - TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); - - PercentType vatPercent = xmlFactory.createPercentType(); - vatPercent.setValue(currencyFormat(iServiceCharge.getTaxPercent())); - tradeTax.setApplicablePercent(vatPercent); - - /* Only in extended - AmountType basisAmount = xmlFactory.createAmountType(); - basisAmount.setCurrencyID(trans.getInvoiceCurrency()); - basisAmount.setValue(amount.getBasis()); - allowance.setBasisAmount(basisAmount); - */ - TaxCategoryCodeType taxType = xmlFactory.createTaxCategoryCodeType(); - taxType.setValue(TaxCategoryCodeType.STANDARDRATE); - tradeTax.setCategoryCode(taxType); - - TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); - taxCode.setValue(TaxTypeCodeType.SALESTAX); - tradeTax.setTypeCode(taxCode); - - - - serviceCharge.getAppliedTradeTax().add(tradeTax); - headerServiceCharge.add(serviceCharge); - - } - - return headerServiceCharge; - } - - private Collection getPaymentTerms() { - List paymentTerms = new ArrayList(); - - TradePaymentTermsType paymentTerm = xmlFactory.createTradePaymentTermsType(); - DateTimeType dueDate = xmlFactory.createDateTimeType(); - DateTimeType.DateTimeString dueDateString = xmlFactory.createDateTimeTypeDateTimeString(); - dueDateString.setFormat(DateTimeType.DateTimeString.DATE); - dueDateString.setValue(zugferdDateFormat.format(trans.getDueDate())); - dueDate.setDateTimeString(dueDateString); - paymentTerm.setDueDateDateTime(dueDate); - - TextType paymentTermDescr = xmlFactory.createTextType(); - - paymentTermDescr.setValue(trans.getPaymentTermDescription()); - paymentTerm.getDescription().add(paymentTermDescr); - - paymentTerms.add(paymentTerm); - - return paymentTerms; - } - - private TradeSettlementMonetarySummationType getMonetarySummation() { - TradeSettlementMonetarySummationType monetarySummation = xmlFactory.createTradeSettlementMonetarySummationType(); - - // AllowanceTotalAmount = sum of all allowances - AmountType allowanceTotalAmount = xmlFactory.createAmountType(); - allowanceTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - if(trans.getZFAllowances() != null){ - BigDecimal totalHeaderAllowance = BigDecimal.ZERO; - for(IZUGFeRDAllowanceCharge headerAllowance : trans.getZFAllowances()){ - totalHeaderAllowance = headerAllowance.getTotalAmount().add(totalHeaderAllowance); - } - allowanceTotalAmount.setValue(currencyFormat(totalHeaderAllowance)); - }else{ - allowanceTotalAmount.setValue(currencyFormat(BigDecimal.ZERO)); - } - monetarySummation.getAllowanceTotalAmount().add(allowanceTotalAmount); - - // ChargeTotalAmount = sum of all Logistic service charges + normal charges - BigDecimal totalCharge = BigDecimal.ZERO; - AmountType totalChargeAmount = xmlFactory.createAmountType(); - totalChargeAmount.setCurrencyID(trans.getInvoiceCurrency()); - if(trans.getZFLogisticsServiceCharges()!= null){ - for(IZUGFeRDAllowanceCharge logisticsServiceCharge : trans.getZFLogisticsServiceCharges()){ - totalCharge = logisticsServiceCharge.getTotalAmount().add(totalCharge); - } - } - if(trans.getZFCharges()!= null){ - for(IZUGFeRDAllowanceCharge charge : trans.getZFCharges()){ - totalCharge = charge.getTotalAmount().add(totalCharge); - } - } - - totalChargeAmount.setValue(currencyFormat(totalCharge)); - - monetarySummation.getChargeTotalAmount().add(totalChargeAmount); - - /*AmountType chargeTotalAmount = xmlFactory.createAmountType(); - chargeTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - chargeTotalAmount.setValue(currencyFormat(BigDecimal.ZERO)); - monetarySummation.getChargeTotalAmount().add(chargeTotalAmount);*/ - - AmountType lineTotalAmount = xmlFactory.createAmountType(); - lineTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - lineTotalAmount.setValue(currencyFormat(totals.getLineTotal())); - monetarySummation.getLineTotalAmount().add(lineTotalAmount); - - AmountType taxBasisTotalAmount = xmlFactory.createAmountType(); - taxBasisTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - taxBasisTotalAmount.setValue(currencyFormat(totals.getTotalNet())); - monetarySummation.getTaxBasisTotalAmount().add(taxBasisTotalAmount); - - AmountType taxTotalAmount = xmlFactory.createAmountType(); - taxTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - taxTotalAmount.setValue(currencyFormat(totals.getTaxTotal())); - monetarySummation.getTaxTotalAmount().add(taxTotalAmount); - - AmountType grandTotalAmount = xmlFactory.createAmountType(); - grandTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); - grandTotalAmount.setValue(currencyFormat(totals.getTotalGross())); - monetarySummation.getGrandTotalAmount().add(grandTotalAmount); - - AmountType duePayableAmount = xmlFactory.createAmountType(); - duePayableAmount.setCurrencyID(trans.getInvoiceCurrency()); - duePayableAmount.setValue(currencyFormat(totals.getTotalGross())); - monetarySummation.getDuePayableAmount().add(duePayableAmount); - - return monetarySummation; - } - - private Collection getLineItems() { - - ArrayList lineItems = new ArrayList(); - int lineID = 0; - for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { - lineID++; - LineCalc lc = new LineCalc(currentItem); - SupplyChainTradeLineItemType lineItem = xmlFactory.createSupplyChainTradeLineItemType(); - - DocumentLineDocumentType lineDocument = xmlFactory.createDocumentLineDocumentType(); - IDType lineNumber = xmlFactory.createIDType(); - lineNumber.setValue(Integer.toString(lineID)); - lineDocument.setLineID(lineNumber); - lineItem.setAssociatedDocumentLineDocument(lineDocument); - - SupplyChainTradeAgreementType tradeAgreement = xmlFactory.createSupplyChainTradeAgreementType(); - TradePriceType grossTradePrice = xmlFactory.createTradePriceType(); - QuantityType grossQuantity = xmlFactory.createQuantityType(); - grossQuantity.setUnitCode(currentItem.getProduct().getUnit()); - grossQuantity.setValue(quantityFormat(BigDecimal.ONE)); - grossTradePrice.setBasisQuantity(grossQuantity); - - AmountType grossChargeAmount = xmlFactory.createAmountType(); - grossChargeAmount.setCurrencyID(trans.getInvoiceCurrency()); - grossChargeAmount.setValue(priceFormat(currentItem.getPrice())); - grossTradePrice.getChargeAmount().add(grossChargeAmount); - tradeAgreement.getGrossPriceProductTradePrice().add(grossTradePrice); - - if(currentItem.getItemAllowances() != null){ - for(IZUGFeRDAllowanceCharge itemAllowance : currentItem.getItemAllowances()){ - TradeAllowanceChargeType eItemAllowance = xmlFactory.createTradeAllowanceChargeType(); - IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); - chargeIndicator.setIndicator(false); - eItemAllowance.setChargeIndicator(chargeIndicator); - AmountType actualAmount = xmlFactory.createAmountType(); - actualAmount.setCurrencyID(trans.getInvoiceCurrency()); - actualAmount.setValue(priceFormat(itemAllowance.getTotalAmount().divide(currentItem.getQuantity(), 4, BigDecimal.ROUND_HALF_UP))); - eItemAllowance.getActualAmount().add(actualAmount); - TextType reason = xmlFactory.createTextType(); - reason.setValue(itemAllowance.getReason()); - eItemAllowance.setReason(reason); - grossTradePrice.getAppliedTradeAllowanceCharge().add(eItemAllowance); - } - } - - if(currentItem.getItemCharges() != null){ - for(IZUGFeRDAllowanceCharge itemCharge : currentItem.getItemCharges()){ - TradeAllowanceChargeType eItemCharge = xmlFactory.createTradeAllowanceChargeType(); - AmountType actualAmount = xmlFactory.createAmountType(); - actualAmount.setCurrencyID(trans.getInvoiceCurrency()); - actualAmount.setValue(priceFormat(itemCharge.getTotalAmount().divide(currentItem.getQuantity(), 4, BigDecimal.ROUND_HALF_UP))); - eItemCharge.getActualAmount().add(actualAmount); - TextType reason = xmlFactory.createTextType(); - reason.setValue(itemCharge.getReason()); - eItemCharge.setReason(reason); - IndicatorType chargeIndicator = xmlFactory.createIndicatorType(); - chargeIndicator.setIndicator(true); - eItemCharge.setChargeIndicator(chargeIndicator); - grossTradePrice.getAppliedTradeAllowanceCharge().add(eItemCharge); - } - } - - TradePriceType netTradePrice = xmlFactory.createTradePriceType(); - QuantityType netQuantity = xmlFactory.createQuantityType(); - netQuantity.setUnitCode(currentItem.getProduct().getUnit()); - netQuantity.setValue(quantityFormat(BigDecimal.ONE)); - netTradePrice.setBasisQuantity(netQuantity); - - AmountType netChargeAmount = xmlFactory.createAmountType(); - netChargeAmount.setCurrencyID(trans.getInvoiceCurrency()); - netChargeAmount.setValue(priceFormat(lc.getItemNetAmount())); - netTradePrice.getChargeAmount().add(netChargeAmount); - tradeAgreement.getNetPriceProductTradePrice().add(netTradePrice); - - lineItem.setSpecifiedSupplyChainTradeAgreement(tradeAgreement); - - SupplyChainTradeDeliveryType tradeDelivery = xmlFactory.createSupplyChainTradeDeliveryType(); - QuantityType billedQuantity = xmlFactory.createQuantityType(); - billedQuantity.setUnitCode(currentItem.getProduct().getUnit()); - billedQuantity.setValue(quantityFormat(currentItem.getQuantity())); - tradeDelivery.setBilledQuantity(billedQuantity); - lineItem.setSpecifiedSupplyChainTradeDelivery(tradeDelivery); - - SupplyChainTradeSettlementType tradeSettlement = xmlFactory.createSupplyChainTradeSettlementType(); - TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); - - TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType(); - taxCategoryCode.setValue(TaxCategoryCodeType.STANDARDRATE); - tradeTax.setCategoryCode(taxCategoryCode); - - TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); - taxCode.setValue(TaxTypeCodeType.SALESTAX); - tradeTax.setTypeCode(taxCode); - - PercentType taxPercent = xmlFactory.createPercentType(); - taxPercent.setValue(vatFormat(currentItem.getProduct().getVATPercent())); - tradeTax.setApplicablePercent(taxPercent); - tradeSettlement.getApplicableTradeTax().add(tradeTax); - - TradeSettlementMonetarySummationType monetarySummation = xmlFactory.createTradeSettlementMonetarySummationType(); - AmountType itemAmount = xmlFactory.createAmountType(); - itemAmount.setCurrencyID(trans.getInvoiceCurrency()); - itemAmount.setValue(currencyFormat(lc.getItemTotalNetAmount())); - monetarySummation.getLineTotalAmount().add(itemAmount); - tradeSettlement.setSpecifiedTradeSettlementMonetarySummation(monetarySummation); - - lineItem.setSpecifiedSupplyChainTradeSettlement(tradeSettlement); - - TradeProductType tradeProduct = xmlFactory.createTradeProductType(); - TextType productName = xmlFactory.createTextType(); - productName.setValue(currentItem.getProduct().getName()); - tradeProduct.getName().add(productName); - - TextType productDescription = xmlFactory.createTextType(); - productDescription.setValue(currentItem.getProduct().getDescription()); - tradeProduct.getDescription().add(productDescription); - lineItem.setSpecifiedTradeProduct(tradeProduct); - - lineItems.add(lineItem); - } - - return lineItems; - - } - - /** - * 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 - * - * @return - * - */ - private HashMap getVATPercentAmountMap() { - return getVATPercentAmountMap(false); - } - - private HashMap getVATPercentAmountMap(Boolean itemOnly){ - HashMap hm = new HashMap(); - - for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { - BigDecimal percent = currentItem.getProduct().getVATPercent(); - LineCalc lc = new LineCalc(currentItem); - VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount()); - VATAmount current = hm.get(percent); - if (current == null) { - hm.put(percent, itemVATAmount); - } else { - hm.put(percent, current.add(itemVATAmount)); - } - } - if(itemOnly){ - return hm; - } - if(trans.getZFAllowances() != null){ - for (IZUGFeRDAllowanceCharge headerAllowance : trans.getZFAllowances()){ - BigDecimal percent = headerAllowance.getTaxPercent(); - VATAmount itemVATAmount = new VATAmount(headerAllowance.getTotalAmount(), headerAllowance.getTotalAmount().multiply(percent).divide(new BigDecimal(100))); - VATAmount current = hm.get(percent); - if (current == null) { - hm.put(percent, itemVATAmount); - } else { - hm.put(percent, current.subtract(itemVATAmount)); - } - } - } - - if(trans.getZFLogisticsServiceCharges()!= null){ - for (IZUGFeRDAllowanceCharge logisticsServiceCharge : trans.getZFLogisticsServiceCharges()){ - BigDecimal percent = logisticsServiceCharge.getTaxPercent(); - VATAmount itemVATAmount = new VATAmount(logisticsServiceCharge.getTotalAmount(), logisticsServiceCharge.getTotalAmount().multiply(percent).divide(new BigDecimal(100))); - VATAmount current = hm.get(percent); - if (current == null) { - hm.put(percent, itemVATAmount); - } else { - hm.put(percent, current.add(itemVATAmount)); - } - } - } - - if(trans.getZFCharges()!= null){ - for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()){ - BigDecimal percent = charge.getTaxPercent(); - VATAmount itemVATAmount = new VATAmount(charge.getTotalAmount(), charge.getTotalAmount().multiply(percent).divide(new BigDecimal(100))); - VATAmount current = hm.get(percent); - if (current == null) { - hm.put(percent, itemVATAmount); - } else { - hm.put(percent, current.add(itemVATAmount)); - } - } - } - - return hm; - } - - /** - * Embeds the Zugferd XML structure in a file named ZUGFeRD-invoice.xml. - * - * @param doc PDDocument to attach an XML invoice to - * @param trans a IZUGFeRDExportableTransaction that provides the data-model - * to populate the XML. This parameter may be null, if so the XML data - * should hav ebeen set via - * setZUGFeRDXMLData(byte[] zugferdData) - */ - public void PDFattachZugferdFile(PDDocument doc, IZUGFeRDExportableTransaction trans) throws IOException, JAXBException { - - if (zugferdData == null) // XML ZUGFeRD data not set externally, needs to be built - { - // create a dummy file stream, this would probably normally be a - // FileInputStream - - byte[] zugferdRaw = getZugferdXMLForTransaction(trans).getBytes(); //$NON-NLS-1$ - - if ((zugferdRaw[0] == (byte) 0xEF) && (zugferdRaw[1] == (byte) 0xBB) && (zugferdRaw[2] == (byte) 0xBF)) { - // I don't like BOMs, lets remove it - zugferdData = new byte[zugferdRaw.length - 3]; - System.arraycopy(zugferdRaw, 3, zugferdData, 0, zugferdRaw.length - 3); - } else { - zugferdData = zugferdRaw; - } - } - - PDFAttachGenericFile(doc, "ZUGFeRD-invoice.xml", "Alternative", "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)", "text/xml", zugferdData); - } - - /** - * Embeds an external file (generic - any type allowed) in the PDF. - * - * @param doc PDDocument to attach the file to. - * @param filename name of the file that will become attachment name in the - * PDF - * @param relationship how the file relates to the content, e.g. - * "Alternative" - * @param description Human-readable description of the file content - * @param subType type of the data e.g. could be "text/xml" - mime like - * @param data the binary data of the file/attachment - */ - public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description, String subType, byte[] data) - throws IOException { - PDComplexFileSpecification fs = new PDComplexFileSpecification(); - fs.setFile(filename); - - COSDictionary dict = fs.getCOSDictionary(); - dict.setName("AFRelationship", relationship); - dict.setString("UF", filename); - dict.setString("Desc", description); - - ByteArrayInputStream fakeFile = new ByteArrayInputStream(data); - PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile); - ef.setSubtype(subType); - ef.setSize(data.length); - ef.setCreationDate(new GregorianCalendar()); - - ef.setModDate(GregorianCalendar.getInstance()); - - fs.setEmbeddedFile(ef); - - // In addition make sure the embedded file is set under /UF - dict = fs.getCOSDictionary(); - COSDictionary efDict = (COSDictionary) dict.getDictionaryObject(COSName.EF); - COSBase lowerLevelFile = efDict.getItem(COSName.F); - efDict.setItem(COSName.UF, lowerLevelFile); - - // now add the entry to the embedded file tree and set in the document. - PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog()); - PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles(); - if (efTree == null) { - efTree = new PDEmbeddedFilesNameTreeNode(); - } - - Map namesMap = new HashMap(); - Map oldNamesMap = efTree.getNames(); - if (oldNamesMap != null) { - for (String key : oldNamesMap.keySet()) { - namesMap.put(key, oldNamesMap.get(key)); - } - } - namesMap.put(filename, fs); - efTree.setNames(namesMap); - - names.setEmbeddedFiles(efTree); - doc.getDocumentCatalog().setNames(names); - - // AF entry (Array) in catalog with the FileSpec - COSArray cosArray = (COSArray) doc.getDocumentCatalog().getCOSDictionary().getItem("AF"); - if (cosArray == null) { - cosArray = new COSArray(); - } - cosArray.add(fs); - doc.getDocumentCatalog().getCOSDictionary().setItem("AF", cosArray); - } - - /** - * Sets the ZUGFeRD XML data to be attached as a single byte array. This is - * useful for use-cases where the XML has already been produced by some - * external API or component. - * - * @param zugferdData XML data to be set as a byte array (XML file in raw - * form). - */ - public void setZUGFeRDXMLData(byte[] zugferdData) { - this.zugferdData = zugferdData; - } - - /** - * Sets the ZUGFeRD conformance level (override). - * - * @param ZUGFeRDConformanceLevel the new conformance level - */ - public void setZUGFeRDConformanceLevel(String ZUGFeRDConformanceLevel) { - this.ZUGFeRDConformanceLevel = ZUGFeRDConformanceLevel; - } - - /** - * * - * This will add both the RDF-indication which embedded file is Zugferd and - * the neccessary PDF/A schema extension description to be able to add this - * information to RDF - * - * @param metadata - */ - private void addZugferdXMP(XMPMetadata metadata) { - - XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, this.ZUGFeRDConformanceLevel); - zf.setAbout(""); //$NON-NLS-1$ - metadata.addSchema(zf); - - XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(metadata); - pdfaex.setAbout(""); //$NON-NLS-1$ - metadata.addSchema(pdfaex); - - } + /* + * Only in extended AmountType basisAmount = + * xmlFactory.createAmountType(); + * basisAmount.setCurrencyID(trans.getInvoiceCurrency()); + * basisAmount.setValue(amount.getBasis()); + * allowance.setBasisAmount(basisAmount); + */ + TaxCategoryCodeType taxType = xmlFactory + .createTaxCategoryCodeType(); + taxType.setValue(TaxCategoryCodeType.STANDARDRATE); + tradeTax.setCategoryCode(taxType); + + TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); + taxCode.setValue(TaxTypeCodeType.SALESTAX); + tradeTax.setTypeCode(taxCode); + + charge.getCategoryTradeTax().add(tradeTax); + headerCharges.add(charge); + + } + + return headerCharges; + } + + private Collection getHeaderLogisticsServiceCharges() { + List headerServiceCharge = new ArrayList(); + + for (IZUGFeRDAllowanceCharge iServiceCharge : trans + .getZFLogisticsServiceCharges()) { + + LogisticsServiceChargeType serviceCharge = xmlFactory + .createLogisticsServiceChargeType(); + + AmountType actualAmount = xmlFactory.createAmountType(); + actualAmount.setCurrencyID(currency); + actualAmount.setValue(currencyFormat(iServiceCharge + .getTotalAmount())); + serviceCharge.getAppliedAmount().add(actualAmount); + + TextType reason = xmlFactory.createTextType(); + reason.setValue(iServiceCharge.getReason()); + serviceCharge.getDescription().add(reason); + + TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); + + PercentType vatPercent = xmlFactory.createPercentType(); + vatPercent.setValue(currencyFormat(iServiceCharge.getTaxPercent())); + tradeTax.setApplicablePercent(vatPercent); + + /* + * Only in extended AmountType basisAmount = + * xmlFactory.createAmountType(); + * basisAmount.setCurrencyID(trans.getInvoiceCurrency()); + * basisAmount.setValue(amount.getBasis()); + * allowance.setBasisAmount(basisAmount); + */ + TaxCategoryCodeType taxType = xmlFactory + .createTaxCategoryCodeType(); + taxType.setValue(TaxCategoryCodeType.STANDARDRATE); + tradeTax.setCategoryCode(taxType); + + TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); + taxCode.setValue(TaxTypeCodeType.SALESTAX); + tradeTax.setTypeCode(taxCode); + + serviceCharge.getAppliedTradeTax().add(tradeTax); + headerServiceCharge.add(serviceCharge); + + } + + return headerServiceCharge; + } + + private Collection getPaymentTerms() { + List paymentTerms = new ArrayList(); + + TradePaymentTermsType paymentTerm = xmlFactory + .createTradePaymentTermsType(); + DateTimeType dueDate = xmlFactory.createDateTimeType(); + DateTimeType.DateTimeString dueDateString = xmlFactory + .createDateTimeTypeDateTimeString(); + dueDateString.setFormat(DateTimeType.DateTimeString.DATE); + dueDateString.setValue(zugferdDateFormat.format(trans.getDueDate())); + dueDate.setDateTimeString(dueDateString); + paymentTerm.setDueDateDateTime(dueDate); + + TextType paymentTermDescr = xmlFactory.createTextType(); + + String paymentTermDescription = trans.getPaymentTermDescription(); + if (paymentTermDescription == null) { + paymentTermDescription = ""; + } + paymentTermDescr.setValue(paymentTermDescription); + paymentTerm.getDescription().add(paymentTermDescr); + + paymentTerms.add(paymentTerm); + + return paymentTerms; + } + + private TradeSettlementMonetarySummationType getMonetarySummation() { + TradeSettlementMonetarySummationType monetarySummation = xmlFactory + .createTradeSettlementMonetarySummationType(); + + // AllowanceTotalAmount = sum of all allowances + AmountType allowanceTotalAmount = xmlFactory.createAmountType(); + allowanceTotalAmount.setCurrencyID(currency); + if (trans.getZFAllowances() != null) { + BigDecimal totalHeaderAllowance = BigDecimal.ZERO; + for (IZUGFeRDAllowanceCharge headerAllowance : trans + .getZFAllowances()) { + totalHeaderAllowance = headerAllowance.getTotalAmount().add( + totalHeaderAllowance); + } + allowanceTotalAmount.setValue(currencyFormat(totalHeaderAllowance)); + } else { + allowanceTotalAmount.setValue(currencyFormat(BigDecimal.ZERO)); + } + monetarySummation.getAllowanceTotalAmount().add(allowanceTotalAmount); + + // ChargeTotalAmount = sum of all Logistic service charges + normal + // charges + BigDecimal totalCharge = BigDecimal.ZERO; + AmountType totalChargeAmount = xmlFactory.createAmountType(); + totalChargeAmount.setCurrencyID(currency); + if (trans.getZFLogisticsServiceCharges() != null) { + for (IZUGFeRDAllowanceCharge logisticsServiceCharge : trans + .getZFLogisticsServiceCharges()) { + totalCharge = logisticsServiceCharge.getTotalAmount().add( + totalCharge); + } + } + if (trans.getZFCharges() != null) { + for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) { + totalCharge = charge.getTotalAmount().add(totalCharge); + } + } + + totalChargeAmount.setValue(currencyFormat(totalCharge)); + + monetarySummation.getChargeTotalAmount().add(totalChargeAmount); + + /* + * AmountType chargeTotalAmount = xmlFactory.createAmountType(); + * chargeTotalAmount.setCurrencyID(trans.getInvoiceCurrency()); + * chargeTotalAmount.setValue(currencyFormat(BigDecimal.ZERO)); + * monetarySummation.getChargeTotalAmount().add(chargeTotalAmount); + */ + + AmountType lineTotalAmount = xmlFactory.createAmountType(); + lineTotalAmount.setCurrencyID(currency); + lineTotalAmount.setValue(currencyFormat(totals.getLineTotal())); + monetarySummation.getLineTotalAmount().add(lineTotalAmount); + + AmountType taxBasisTotalAmount = xmlFactory.createAmountType(); + taxBasisTotalAmount.setCurrencyID(currency); + taxBasisTotalAmount.setValue(currencyFormat(totals.getTotalNet())); + monetarySummation.getTaxBasisTotalAmount().add(taxBasisTotalAmount); + + AmountType taxTotalAmount = xmlFactory.createAmountType(); + taxTotalAmount.setCurrencyID(currency); + taxTotalAmount.setValue(currencyFormat(totals.getTaxTotal())); + monetarySummation.getTaxTotalAmount().add(taxTotalAmount); + + AmountType grandTotalAmount = xmlFactory.createAmountType(); + grandTotalAmount.setCurrencyID(currency); + grandTotalAmount.setValue(currencyFormat(totals.getTotalGross())); + monetarySummation.getGrandTotalAmount().add(grandTotalAmount); + + AmountType duePayableAmount = xmlFactory.createAmountType(); + duePayableAmount.setCurrencyID(currency); + duePayableAmount.setValue(currencyFormat(totals.getTotalGross())); + monetarySummation.getDuePayableAmount().add(duePayableAmount); + + return monetarySummation; + } + + private Collection getLineItems() { + + ArrayList lineItems = new ArrayList(); + int lineID = 0; + for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { + lineID++; + LineCalc lc = new LineCalc(currentItem); + SupplyChainTradeLineItemType lineItem = xmlFactory + .createSupplyChainTradeLineItemType(); + + DocumentLineDocumentType lineDocument = xmlFactory + .createDocumentLineDocumentType(); + IDType lineNumber = xmlFactory.createIDType(); + lineNumber.setValue(Integer.toString(lineID)); + lineDocument.setLineID(lineNumber); + lineItem.setAssociatedDocumentLineDocument(lineDocument); + + SupplyChainTradeAgreementType tradeAgreement = xmlFactory + .createSupplyChainTradeAgreementType(); + TradePriceType grossTradePrice = xmlFactory.createTradePriceType(); + QuantityType grossQuantity = xmlFactory.createQuantityType(); + grossQuantity.setUnitCode(currentItem.getProduct().getUnit()); + grossQuantity.setValue(quantityFormat(BigDecimal.ONE)); + grossTradePrice.setBasisQuantity(grossQuantity); + + AmountType grossChargeAmount = xmlFactory.createAmountType(); + grossChargeAmount.setCurrencyID(currency); + grossChargeAmount.setValue(priceFormat(currentItem.getPrice())); + grossTradePrice.getChargeAmount().add(grossChargeAmount); + tradeAgreement.getGrossPriceProductTradePrice() + .add(grossTradePrice); + + if (currentItem.getItemAllowances() != null) { + for (IZUGFeRDAllowanceCharge itemAllowance : currentItem + .getItemAllowances()) { + TradeAllowanceChargeType eItemAllowance = xmlFactory + .createTradeAllowanceChargeType(); + IndicatorType chargeIndicator = xmlFactory + .createIndicatorType(); + chargeIndicator.setIndicator(false); + eItemAllowance.setChargeIndicator(chargeIndicator); + AmountType actualAmount = xmlFactory.createAmountType(); + actualAmount.setCurrencyID(currency); + actualAmount.setValue(priceFormat(itemAllowance + .getTotalAmount().divide(currentItem.getQuantity(), + 4, BigDecimal.ROUND_HALF_UP))); + eItemAllowance.getActualAmount().add(actualAmount); + TextType reason = xmlFactory.createTextType(); + reason.setValue(itemAllowance.getReason()); + eItemAllowance.setReason(reason); + grossTradePrice.getAppliedTradeAllowanceCharge().add( + eItemAllowance); + } + } + + if (currentItem.getItemCharges() != null) { + for (IZUGFeRDAllowanceCharge itemCharge : currentItem + .getItemCharges()) { + TradeAllowanceChargeType eItemCharge = xmlFactory + .createTradeAllowanceChargeType(); + AmountType actualAmount = xmlFactory.createAmountType(); + actualAmount.setCurrencyID(currency); + actualAmount.setValue(priceFormat(itemCharge + .getTotalAmount().divide(currentItem.getQuantity(), + 4, BigDecimal.ROUND_HALF_UP))); + eItemCharge.getActualAmount().add(actualAmount); + TextType reason = xmlFactory.createTextType(); + reason.setValue(itemCharge.getReason()); + eItemCharge.setReason(reason); + IndicatorType chargeIndicator = xmlFactory + .createIndicatorType(); + chargeIndicator.setIndicator(true); + eItemCharge.setChargeIndicator(chargeIndicator); + grossTradePrice.getAppliedTradeAllowanceCharge().add( + eItemCharge); + } + } + + TradePriceType netTradePrice = xmlFactory.createTradePriceType(); + QuantityType netQuantity = xmlFactory.createQuantityType(); + netQuantity.setUnitCode(currentItem.getProduct().getUnit()); + netQuantity.setValue(quantityFormat(BigDecimal.ONE)); + netTradePrice.setBasisQuantity(netQuantity); + + AmountType netChargeAmount = xmlFactory.createAmountType(); + netChargeAmount.setCurrencyID(currency); + netChargeAmount.setValue(priceFormat(lc.getItemNetAmount())); + netTradePrice.getChargeAmount().add(netChargeAmount); + tradeAgreement.getNetPriceProductTradePrice().add(netTradePrice); + + lineItem.setSpecifiedSupplyChainTradeAgreement(tradeAgreement); + + SupplyChainTradeDeliveryType tradeDelivery = xmlFactory + .createSupplyChainTradeDeliveryType(); + QuantityType billedQuantity = xmlFactory.createQuantityType(); + billedQuantity.setUnitCode(currentItem.getProduct().getUnit()); + billedQuantity.setValue(quantityFormat(currentItem.getQuantity())); + tradeDelivery.setBilledQuantity(billedQuantity); + lineItem.setSpecifiedSupplyChainTradeDelivery(tradeDelivery); + + SupplyChainTradeSettlementType tradeSettlement = xmlFactory + .createSupplyChainTradeSettlementType(); + TradeTaxType tradeTax = xmlFactory.createTradeTaxType(); + + TaxCategoryCodeType taxCategoryCode = xmlFactory + .createTaxCategoryCodeType(); + taxCategoryCode.setValue(TaxCategoryCodeType.STANDARDRATE); + tradeTax.setCategoryCode(taxCategoryCode); + + TaxTypeCodeType taxCode = xmlFactory.createTaxTypeCodeType(); + taxCode.setValue(TaxTypeCodeType.SALESTAX); + tradeTax.setTypeCode(taxCode); + + PercentType taxPercent = xmlFactory.createPercentType(); + taxPercent.setValue(vatFormat(currentItem.getProduct() + .getVATPercent())); + tradeTax.setApplicablePercent(taxPercent); + tradeSettlement.getApplicableTradeTax().add(tradeTax); + + TradeSettlementMonetarySummationType monetarySummation = xmlFactory + .createTradeSettlementMonetarySummationType(); + AmountType itemAmount = xmlFactory.createAmountType(); + itemAmount.setCurrencyID(currency); + itemAmount.setValue(currencyFormat(lc.getItemTotalNetAmount())); + monetarySummation.getLineTotalAmount().add(itemAmount); + tradeSettlement + .setSpecifiedTradeSettlementMonetarySummation(monetarySummation); + + lineItem.setSpecifiedSupplyChainTradeSettlement(tradeSettlement); + + TradeProductType tradeProduct = xmlFactory.createTradeProductType(); + TextType productName = xmlFactory.createTextType(); + productName.setValue(currentItem.getProduct().getName()); + tradeProduct.getName().add(productName); + + TextType productDescription = xmlFactory.createTextType(); + productDescription.setValue(currentItem.getProduct() + .getDescription()); + tradeProduct.getDescription().add(productDescription); + lineItem.setSpecifiedTradeProduct(tradeProduct); + + lineItems.add(lineItem); + } + + return lineItems; + + } + + /** + * 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 + * + * @return + * + */ + private HashMap getVATPercentAmountMap() { + return getVATPercentAmountMap(false); + } + + private HashMap getVATPercentAmountMap( + Boolean itemOnly) { + HashMap hm = new HashMap(); + + for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { + BigDecimal percent = currentItem.getProduct().getVATPercent(); + LineCalc lc = new LineCalc(currentItem); + VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), + lc.getItemTotalVATAmount()); + VATAmount current = hm.get(percent); + if (current == null) { + hm.put(percent, itemVATAmount); + } else { + hm.put(percent, current.add(itemVATAmount)); + } + } + if (itemOnly) { + return hm; + } + if (trans.getZFAllowances() != null) { + for (IZUGFeRDAllowanceCharge headerAllowance : trans + .getZFAllowances()) { + BigDecimal percent = headerAllowance.getTaxPercent(); + VATAmount itemVATAmount = new VATAmount( + headerAllowance.getTotalAmount(), headerAllowance + .getTotalAmount().multiply(percent) + .divide(new BigDecimal(100))); + VATAmount current = hm.get(percent); + if (current == null) { + hm.put(percent, itemVATAmount); + } else { + hm.put(percent, current.subtract(itemVATAmount)); + } + } + } + + if (trans.getZFLogisticsServiceCharges() != null) { + for (IZUGFeRDAllowanceCharge logisticsServiceCharge : trans + .getZFLogisticsServiceCharges()) { + BigDecimal percent = logisticsServiceCharge.getTaxPercent(); + VATAmount itemVATAmount = new VATAmount( + logisticsServiceCharge.getTotalAmount(), + logisticsServiceCharge.getTotalAmount() + .multiply(percent).divide(new BigDecimal(100))); + VATAmount current = hm.get(percent); + if (current == null) { + hm.put(percent, itemVATAmount); + } else { + hm.put(percent, current.add(itemVATAmount)); + } + } + } + + if (trans.getZFCharges() != null) { + for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) { + BigDecimal percent = charge.getTaxPercent(); + VATAmount itemVATAmount = new VATAmount( + charge.getTotalAmount(), charge.getTotalAmount() + .multiply(percent).divide(new BigDecimal(100))); + VATAmount current = hm.get(percent); + if (current == null) { + hm.put(percent, itemVATAmount); + } else { + hm.put(percent, current.add(itemVATAmount)); + } + } + } + + return hm; + } + + /** + * Embeds the Zugferd XML structure in a file named ZUGFeRD-invoice.xml. + * + * @param doc + * PDDocument to attach an XML invoice to + * @param trans + * a IZUGFeRDExportableTransaction that provides the data-model + * to populate the XML. This parameter may be null, if so the XML + * data should hav ebeen set via + * setZUGFeRDXMLData(byte[] zugferdData) + */ + public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) + throws IOException, JAXBException { + + if (zugferdData == null) // XML ZUGFeRD data not set externally, needs + // to be built + { + // create a dummy file stream, this would probably normally be a + // FileInputStream + + byte[] zugferdRaw = getZugferdXMLForTransaction(trans).getBytes(); //$NON-NLS-1$ + + if ((zugferdRaw[0] == (byte) 0xEF) + && (zugferdRaw[1] == (byte) 0xBB) + && (zugferdRaw[2] == (byte) 0xBF)) { + // I don't like BOMs, lets remove it + zugferdData = new byte[zugferdRaw.length - 3]; + System.arraycopy(zugferdRaw, 3, zugferdData, 0, + zugferdRaw.length - 3); + } else { + zugferdData = zugferdRaw; + } + } + + PDFAttachGenericFile( + doc, + "ZUGFeRD-invoice.xml", + "Alternative", + "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)", + "text/xml", zugferdData); + } + + public void export(String ZUGFeRDfilename) { + try { + doc.save(ZUGFeRDfilename); + } catch (COSVisitorException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Embeds an external file (generic - any type allowed) in the PDF. + * + * @param doc + * PDDocument to attach the file to. + * @param filename + * name of the file that will become attachment name in the PDF + * @param relationship + * how the file relates to the content, e.g. "Alternative" + * @param description + * Human-readable description of the file content + * @param subType + * type of the data e.g. could be "text/xml" - mime like + * @param data + * the binary data of the file/attachment + */ + public void PDFAttachGenericFile(PDDocument doc, String filename, + String relationship, String description, String subType, byte[] data) + throws IOException { + PDComplexFileSpecification fs = new PDComplexFileSpecification(); + fs.setFile(filename); + + COSDictionary dict = fs.getCOSDictionary(); + dict.setName("AFRelationship", relationship); + dict.setString("UF", filename); + dict.setString("Desc", description); + + ByteArrayInputStream fakeFile = new ByteArrayInputStream(data); + PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile); + ef.setSubtype(subType); + ef.setSize(data.length); + ef.setCreationDate(new GregorianCalendar()); + + ef.setModDate(GregorianCalendar.getInstance()); + + fs.setEmbeddedFile(ef); + + // In addition make sure the embedded file is set under /UF + dict = fs.getCOSDictionary(); + COSDictionary efDict = (COSDictionary) dict + .getDictionaryObject(COSName.EF); + COSBase lowerLevelFile = efDict.getItem(COSName.F); + efDict.setItem(COSName.UF, lowerLevelFile); + + // now add the entry to the embedded file tree and set in the document. + PDDocumentNameDictionary names = new PDDocumentNameDictionary( + doc.getDocumentCatalog()); + PDEmbeddedFilesNameTreeNode efTree = names.getEmbeddedFiles(); + if (efTree == null) { + efTree = new PDEmbeddedFilesNameTreeNode(); + } + + Map namesMap = new HashMap(); + Map oldNamesMap = efTree.getNames(); + if (oldNamesMap != null) { + for (String key : oldNamesMap.keySet()) { + namesMap.put(key, oldNamesMap.get(key)); + } + } + namesMap.put(filename, fs); + efTree.setNames(namesMap); + + names.setEmbeddedFiles(efTree); + doc.getDocumentCatalog().setNames(names); + + // AF entry (Array) in catalog with the FileSpec + COSArray cosArray = (COSArray) doc.getDocumentCatalog() + .getCOSDictionary().getItem("AF"); + if (cosArray == null) { + cosArray = new COSArray(); + } + cosArray.add(fs); + COSDictionary dict2 = doc.getDocumentCatalog().getCOSDictionary(); + COSArray array = new COSArray(); + array.add(fs.getCOSDictionary()); // see below + dict2.setItem("AF",array); + doc.getDocumentCatalog().getCOSDictionary().setItem("AF", cosArray); + } + + /** + * Sets the ZUGFeRD XML data to be attached as a single byte array. This is + * useful for use-cases where the XML has already been produced by some + * external API or component. + * + * @param zugferdData + * XML data to be set as a byte array (XML file in raw form). + */ + public void setZUGFeRDXMLData(byte[] zugferdData) { + this.zugferdData = zugferdData; + } + + /** + * Sets the ZUGFeRD conformance level (override). + * + * @param ZUGFeRDConformanceLevel + * the new conformance level + */ + public void setZUGFeRDConformanceLevel(String ZUGFeRDConformanceLevel) { + this.ZUGFeRDConformanceLevel = ZUGFeRDConformanceLevel; + } + + /** + * * This will add both the RDF-indication which embedded file is Zugferd + * and the neccessary PDF/A schema extension description to be able to add + * this information to RDF + * + * @param metadata + */ + private void addZugferdXMP(XMPMetadata metadata) { + + XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, + this.ZUGFeRDConformanceLevel); + zf.setAbout(""); //$NON-NLS-1$ + metadata.addSchema(zf); + + XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(metadata); + pdfaex.setAbout(""); //$NON-NLS-1$ + metadata.addSchema(pdfaex); + + } + + /**** + * Returns the PDFBox PDF Document + * + * @return + */ + public PDDocument getDoc() { + return doc; + } } diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java index 10c32cc1..dccb0d3d 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java @@ -15,6 +15,7 @@ import java.io.BufferedInputStream; import java.io.FileInputStream; import java.util.Map; +import javax.management.RuntimeErrorException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -34,12 +35,12 @@ import org.xml.sax.SAXException; public class ZUGFeRDImporter { /* - call extract(importFilename). - containsMeta() will return if ZUGFeRD data has been found, + call extract(importFilename). + containsMeta() will return if ZUGFeRD data has been found, afterwards you can call getBIC(), getIBAN() etc. */ - + /**@var if metadata has been found */ private boolean containsMeta=false; /**@var the reference (i.e. invoice number) of the sender */ @@ -48,16 +49,20 @@ public class ZUGFeRDImporter { private String IBAN; private String holder; private String amount; + private String dueDate; /** Raw XML form of the extracted data - may be directly obtained. */ private byte[] rawXML=null; private String bankName; private boolean amountFound; - + private boolean extracted=false; + private boolean parsed=false; + + /** * Extracts a ZUGFeRD invoice from a PDF document represented by a file name. * Errors are just logged to STDOUT. */ - public void extract(String pdfFilename) + public void extract(String pdfFilename) { try { @@ -66,7 +71,7 @@ public class ZUGFeRDImporter { { ioe.printStackTrace(); } - } + } /** * Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. @@ -106,6 +111,7 @@ public class ZUGFeRDImporter { // FileOutputStream fos = new FileOutputStream(file); rawXML = embeddedFile.getByteArray(); setMeta(new String(rawXML)); + extracted=true; // fos.write(embeddedFile.getByteArray()); // fos.close(); } @@ -123,12 +129,16 @@ public class ZUGFeRDImporter { } } - + public void parse() { DocumentBuilderFactory factory = null; DocumentBuilder builder = null; Document document = null; + if (!extracted) { + throw new RuntimeException("extract() or extractLowLevel() must be used before parsing."); + } + factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); //otherwise we can not act namespace independend, i.e. use document.getElementsByTagNameNS("*",... try { @@ -147,17 +157,17 @@ public class ZUGFeRDImporter { ex2.printStackTrace(); } NodeList ndList ; - + // rootNode = document.getDocumentElement(); // ApplicableSupplyChainTradeSettlement ndList = document.getDocumentElement() .getElementsByTagNameNS("*","PaymentReference"); //$NON-NLS-1$ - + for (int bookingIndex = 0; bookingIndex < ndList .getLength(); bookingIndex++) { Node booking = ndList.item(bookingIndex); // if there is a attribute in the tag number:value - + setForeignReference(booking.getTextContent()); } @@ -189,7 +199,7 @@ public class ZUGFeRDImporter { DE5656565 Commerzbank - + */ ndList = document.getElementsByTagNameNS("*","PayeePartyCreditorFinancialAccount"); //$NON-NLS-1$ @@ -200,7 +210,7 @@ public class ZUGFeRDImporter { // there are many "name" elements, so get the one below // SellerTradeParty NodeList bookingDetails = booking.getChildNodes(); - + for (int detailIndex = 0; detailIndex < bookingDetails .getLength(); detailIndex++) { @@ -208,7 +218,7 @@ public class ZUGFeRDImporter { if ((detail.getLocalName()!=null)&&(detail.getLocalName().equals("IBANID"))) { //$NON-NLS-1$ setIBAN(detail.getTextContent()); - } + } } } @@ -266,7 +276,7 @@ public class ZUGFeRDImporter { if (!amountFound) { - /* there is apparently no requirement to mention DuePayableAmount,, + /* there is apparently no requirement to mention DuePayableAmount,, * if it's not there, check for GrandTotalAmount */ ndList = document.getElementsByTagNameNS("*","GrandTotalAmount"); //$NON-NLS-1$ @@ -278,10 +288,28 @@ public class ZUGFeRDImporter { setAmount(booking.getTextContent()); } - - } - + } + + ndList = document.getElementsByTagNameNS("*","SpecifiedTradePaymentTerms"); //$NON-NLS-1$ + + for (int bookingIndex = 0; bookingIndex < ndList + .getLength(); bookingIndex++) { + Node booking = ndList.item(bookingIndex); + // there are many "name" elements, so get the one below + // SellerTradeParty + NodeList bookingDetails = booking.getChildNodes(); + for (int detailIndex = 0; detailIndex < bookingDetails + .getLength(); detailIndex++) { + Node detail = bookingDetails.item(detailIndex); + if ((detail.getLocalName()!=null)&&(detail.getLocalName().equals("DueDateDateTime"))) { //$NON-NLS-1$ + setDueDate(detail.getTextContent().trim()); + } + } + + } + + parsed=true; } @@ -290,18 +318,24 @@ public class ZUGFeRDImporter { } public String getForeignReference() { + if (!parsed) { + throw new RuntimeException("use parse() before requesting a value"); + } return foreignReference; } - public void setForeignReference(String foreignReference) { + private void setForeignReference(String foreignReference) { this.foreignReference = foreignReference; } public String getBIC() { + if (!parsed) { + throw new RuntimeException("use parse() before requesting a value"); + } return BIC; } @@ -312,6 +346,11 @@ public class ZUGFeRDImporter { } + private void setDueDate(String dueDate) { + this.dueDate = dueDate; + } + + private void setBankName(String bankname) { this.bankName = bankname; } @@ -319,23 +358,32 @@ public class ZUGFeRDImporter { public String getIBAN() { + if (!parsed) { + throw new RuntimeException("use parse() before requesting a value"); + } return IBAN; } public String getBankName() { + if (!parsed) { + throw new RuntimeException("use parse() before requesting a value"); + } return bankName; } - public void setIBAN(String IBAN) { + private void setIBAN(String IBAN) { this.IBAN = IBAN; } public String getHolder() { + if (rawXML==null) { + throw new RuntimeException("use parse() before requesting a value"); + } return holder; } @@ -348,9 +396,18 @@ public class ZUGFeRDImporter { public String getAmount() { + if (rawXML==null) { + throw new RuntimeException("use parse() before requesting a value"); + } return amount; } + public String getDueDate() { + if (rawXML==null) { + throw new RuntimeException("use parse() before requesting a value"); + } + return dueDate; + } private void setAmount(String amount) { this.amount = amount; @@ -362,7 +419,7 @@ public class ZUGFeRDImporter { public String getMeta() { if (rawXML==null){ - return null; + return null; } else { return new String(rawXML); } @@ -372,7 +429,7 @@ public class ZUGFeRDImporter { /** * Returns the raw XML data as extracted from the ZUGFeRD PDF file. */ - public byte[] getRawXML() + public byte[] getRawXML() { return rawXML; } @@ -381,8 +438,8 @@ public class ZUGFeRDImporter { * will return true if the metadata (just extract-ed or set with setMeta) contains ZUGFeRD XML * */ public boolean canParse() { - - + + //SpecifiedExchangedDocumentContext is in the schema, so a relatively good indication if zugferd is present - better than just invoice String meta=getMeta(); return (meta!=null)&&( meta.length()>0)&&( meta.contains("SpecifiedExchangedDocumentContext")); //$NON-NLS-1$ diff --git a/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterEdgeTest.java b/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterEdgeTest.java index 8006505d..6ce243a9 100644 --- a/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterEdgeTest.java +++ b/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterEdgeTest.java @@ -49,7 +49,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp @Override public String getOwnBIC() { - return "COBADEFXXX"; + return "COBADEFFXXX"; } @Override @@ -118,7 +118,13 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp { return null; } - + + @Override + public String getCurrency() + { + return "EUR"; + } + @Override public IZUGFeRDExportableItem[] getZFItems() { @@ -133,11 +139,6 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp return allItems; } - @Override - public String getInvoiceCurrency() { - return "EUR"; - } - @Override public String getOwnPaymentInfoText() { return "Überweisung"; @@ -258,7 +259,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp @Override public IZUGFeRDAllowanceCharge[] getItemAllowances() { - return null; + return null; } @Override @@ -330,7 +331,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp /** * Create the test case - * + * * @param testName * name of the test case */ @@ -353,7 +354,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp * The importer test imports from ./src/test/MustangGnuaccountingBeispielRE-20151008_504.pdf to check the values. * --> as only Name Ascending is supported for Test Unit sequence, I renamed the this test-A-Export to run before * testZExport - * + * * @throws IOException */ @@ -368,6 +369,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp String iban = null; String holder = null; String ref = null; + String dueDate = null; if (zi.canParse()) { @@ -376,6 +378,7 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp bic = zi.getBIC(); iban = zi.getIBAN(); holder = zi.getHolder(); + dueDate = zi.getDueDate(); ref = zi.getForeignReference(); } @@ -383,6 +386,8 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp assertEquals(bic, getOwnBIC()); assertEquals(iban, getOwnIBAN()); assertEquals(holder, getOwnOrganisationName()); + + assertEquals(dueDate, "20141029"); assertEquals(ref, getNumber()); } @@ -401,16 +406,13 @@ public class MustangReaderWriterEdgeTest extends TestCase implements IZUGFeRDExp final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20151008_504newEdge.pdf"; // the writing part - PDDocument doc; try { - doc = PDDocument.load(SOURCE_PDF); // automatically add Zugferd to all outgoing invoices ZUGFeRDExporter ze = new ZUGFeRDExporter(); - ze.PDFmakeA3compliant(doc, "My Application", System.getProperty("user.name"), true); - ze.PDFattachZugferdFile(doc, this); - doc.save(TARGET_PDF); - doc.close(); + ze.PDFmakeA3compliant(SOURCE_PDF, "My Application", System.getProperty("user.name"), true); + ze.PDFattachZugferdFile(this); + ze.export(TARGET_PDF); } catch (IOException e) { diff --git a/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterTest.java b/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterTest.java index 468551e2..c0d6b1ea 100644 --- a/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterTest.java +++ b/mustang/src/test/java/org/mustangproject/ZUGFeRD/MustangReaderWriterTest.java @@ -49,7 +49,7 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta @Override public String getOwnBIC() { - return "COBADEFXXX"; + return "COBADEFFXXX"; } @Override @@ -118,6 +118,12 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta return null; } + @Override + public String getCurrency() + { + return "EUR"; + } + @Override public IZUGFeRDExportableItem[] getZFItems() { @@ -132,11 +138,6 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta return allItems; } - @Override - public String getInvoiceCurrency() { - return "EUR"; - } - @Override public String getOwnPaymentInfoText() { return "Überweisung"; @@ -257,16 +258,16 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta { this.product = product; } - + @Override public IZUGFeRDAllowanceCharge[] getItemAllowances() { - return null; + return null; } @Override public IZUGFeRDAllowanceCharge[] getItemCharges() { return null; - } + } } @@ -332,7 +333,7 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta /** * Create the test case - * + * * @param testName * name of the test case */ @@ -355,7 +356,7 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta * The importer test imports from ./src/test/MustangGnuaccountingBeispielRE-20151008_504.pdf to check the values. * --> as only Name Ascending is supported for Test Unit sequence, I renamed the this test-A-Export to run before * testZExport - * + * * @throws IOException */ @@ -403,16 +404,13 @@ public class MustangReaderWriterTest extends TestCase implements IZUGFeRDExporta final String TARGET_PDF = "./target/testout-MustangGnuaccountingBeispielRE-20151008_504new.pdf"; // the writing part - PDDocument doc; try { - doc = PDDocument.load(SOURCE_PDF); // automatically add Zugferd to all outgoing invoices ZUGFeRDExporter ze = new ZUGFeRDExporter(); - ze.PDFmakeA3compliant(doc, "My Application", System.getProperty("user.name"), true); - ze.PDFattachZugferdFile(doc, this); - doc.save(TARGET_PDF); - doc.close(); + ze.PDFmakeA3compliant(SOURCE_PDF, "My Application", System.getProperty("user.name"), true); + ze.PDFattachZugferdFile(this); + ze.export(TARGET_PDF); } catch (IOException e) { diff --git a/mustang/src/test/resources/MustangGnuaccountingBeispielRE-20151008_504.pdf b/mustang/src/test/resources/MustangGnuaccountingBeispielRE-20151008_504.pdf index 236ea091..1771974d 100644 Binary files a/mustang/src/test/resources/MustangGnuaccountingBeispielRE-20151008_504.pdf and b/mustang/src/test/resources/MustangGnuaccountingBeispielRE-20151008_504.pdf differ diff --git a/mustang/toecount/pom.xml b/mustang/toecount/pom.xml index 01a276b5..cedff06d 100755 --- a/mustang/toecount/pom.xml +++ b/mustang/toecount/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.mustangproject.ZUGFeRD toecount - 0.0.6-SNAPSHOT + 0.0.8-SNAPSHOT ZUGFeRD PDF checker Shows statistics on how many PDFs in a directory are ZUGFeRD-Compliant @@ -27,7 +27,7 @@ org.mustangproject.ZUGFeRD mustang - 1.2.1-SNAPSHOT + 1.3.0-SNAPSHOT org.apache.pdfbox diff --git a/mustang/toecount/src/main/java/toecount/FileChecker.java b/mustang/toecount/src/main/java/toecount/FileChecker.java index c1ad9d64..33fe0dc0 100755 --- a/mustang/toecount/src/main/java/toecount/FileChecker.java +++ b/mustang/toecount/src/main/java/toecount/FileChecker.java @@ -30,11 +30,34 @@ public class FileChecker { return false; } ZUGFeRDImporter zi=new ZUGFeRDImporter(); - zi.extract(filename); - if (zi.canParse()) { - thisRun.incZUGFeRDCount(); - return true; - } else { + try { + zi.extract(filename); + if (zi.canParse()) { + thisRun.incZUGFeRDCount(); + return true; + } else { + return false; + } + } catch (NullPointerException e) { + // something really rare happened -- corrupted ZF? + /*** + * e.g. Exception in thread "main" java.lang.NullPointerException + at org.mustangproject.ZUGFeRD.ZUGFeRDImporter.extractLowLevel(ZUGFeRDImporter.java:90) + at org.mustangproject.ZUGFeRD.ZUGFeRDImporter.extract(ZUGFeRDImporter.java:64) + at toecount.FileChecker.checkForZUGFeRD(FileChecker.java:33) + at toecount.Toecount.main(Toecount.java:111) + * + */ + // Ignore nevertheless, most likely we're batch processing + return false; + } catch (Exception e2) { + /** + * probably thrown up from + AM org.apache.pdfbox.pdfparser.PDFParser parse, most likely + INFORMATION: Document is encrypted + but also other internal PDF errors possible like + ..Okt 23, 2015 11:17:53 AM org.apache.pdfbox.pdfparser.XrefTrailerResolver setStartxref + */ return false; } } diff --git a/mustang/toecount/src/main/java/toecount/Toecount.java b/mustang/toecount/src/main/java/toecount/Toecount.java index 6c0c412e..50c8d2a4 100755 --- a/mustang/toecount/src/main/java/toecount/Toecount.java +++ b/mustang/toecount/src/main/java/toecount/Toecount.java @@ -22,7 +22,7 @@ public class Toecount { } private static void printHelp() { System.out - .println("Mustangproject.org's Toecount 0.0.6 \r\n" + .println("Mustangproject.org's Toecount 0.0.7 \r\n" + "A Apache Public License command line tool for statistics on PDF invoices with\r\n" + "ZUGFeRD Metadata (http://www.zugferd.de)\r\n" + "\r\n" diff --git a/pdfa3-sample/pom.xml b/pdfa3-sample/pom.xml index aa81da5c..913f5bf1 100644 --- a/pdfa3-sample/pom.xml +++ b/pdfa3-sample/pom.xml @@ -3,8 +3,8 @@ com.github.rayman2200 mustang-sample-project - 1.3.0-SNAPSHOT + 1.3.0.1-SNAPSHOT - pdfa3-samples - \ No newline at end of file + pdfa3-samples + diff --git a/pdfa3-sample/src/main/java/pdfbox/PDFA3FileAttachment.java b/pdfa3-sample/src/main/java/pdfbox/PDFA3FileAttachment.java index 8b609d40..164218ce 100644 --- a/pdfa3-sample/src/main/java/pdfbox/PDFA3FileAttachment.java +++ b/pdfa3-sample/src/main/java/pdfbox/PDFA3FileAttachment.java @@ -30,6 +30,7 @@ import org.apache.jempbox.xmp.XMPSchemaBasic; import org.apache.jempbox.xmp.XMPSchemaDublinCore; import org.apache.jempbox.xmp.XMPSchemaPDF; import org.apache.jempbox.xmp.pdfa.XMPSchemaPDFAId; +import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; @@ -120,6 +121,10 @@ public class PDFA3FileAttachment // cosArray.add(fs); // catalog.setItem("AF", cosArray); + COSDictionary dict2 = catalog.getCOSDictionary(); + COSArray array = new COSArray(); + array.add(fs.getCOSDictionary()); // see below + dict2.setItem("AF",array); } /** diff --git a/pom.xml b/pom.xml index 5bb6a839..161fb086 100644 --- a/pom.xml +++ b/pom.xml @@ -2,13 +2,13 @@ 4.0.0 com.github.rayman2200 mustang-sample-project - 1.3.0-SNAPSHOT + 1.3.3-SNAPSHOT pom - scm:git:https://github.com/AlexanderSchmidt/PDFA3.git - scm:git:https://github.com/AlexanderSchmidt/PDFA3.git - https://github.com/AlexanderSchmidt/PDFA3.git + scm:git:https://github.com/jstaerk/mustangproject.git + scm:git:https://github.com/jstaerk/mustangproject.git + https://github.com/jstaerk/mustangproject HEAD @@ -24,6 +24,7 @@ UTF-8 github -Xdoclint:none + 1.6 @@ -35,7 +36,7 @@ org.apache.pdfbox pdfbox - 1.8.10 + 1.8.8 junit @@ -47,15 +48,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.2 - - 1.6 - 1.6 - - maven-deploy-plugin 2.8.2 @@ -76,8 +68,8 @@ **/* true - PDFA3 - AlexanderSchmidt + mustangproject + jstaerk