From 622cec740cc0b298f0052d27be803c806242c64d Mon Sep 17 00:00:00 2001 From: Jochen Staerk Date: Tue, 26 May 2015 19:26:16 +0200 Subject: [PATCH] added Desc key to PDF file embed structure, corrected profile, introduced ChargeTotalAmount and AllowanceTotalAmount, introduced VATFormat for ApplicablePercent and priceFormat for ChargeAmount and BasisQuantity --- .../ZUGFeRD/ZUGFeRDExporter.java | 121 ++++++++---------- 1 file changed, 51 insertions(+), 70 deletions(-) diff --git a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java index 4a54abcb..9729ce89 100644 --- a/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java +++ b/mustang/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java @@ -12,7 +12,10 @@ package org.mustangproject.ZUGFeRD; import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashMap; @@ -78,8 +81,9 @@ public class ZUGFeRDExporter { * pass a IZUGFeRDExportableTransaction for the XML to be populated. */ byte[] zugferdData = null; - - private String currencyFormat(BigDecimal value, char decimalDelimiter) { + + + private String nDigitFormat(BigDecimal value, int scale) { /* * I needed 123,45, locale independent.I tried * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is @@ -106,18 +110,30 @@ public class ZUGFeRDExporter { * * results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00 */ - value=value.setScale( 2, BigDecimal.ROUND_HALF_UP ); // first, round so that e.g. 1.189999999999999946709294817992486059665679931640625 becomes 1.19 - long totalCent = value.multiply(new BigDecimal(100)).intValue(); //now get the cents - long eurOnly = value.longValue(); - long centOnly = Math.abs(totalCent % 100); - StringBuffer res = new StringBuffer(); - res.append(eurOnly); - res.append(decimalDelimiter); - if (centOnly < 10) { - res.append('0'); - } - res.append(centOnly); - return res.toString(); + value=value.setScale( scale, BigDecimal.ROUND_HALF_UP ); // first, round so that e.g. 1.189999999999999946709294817992486059665679931640625 becomes 1.19 + char[] repeat = new char[scale]; + Arrays.fill(repeat, '0'); + + DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(); + otherSymbols.setDecimalSeparator('.'); + DecimalFormat dec = new DecimalFormat("0."+new String(repeat), otherSymbols); + return dec.format(value); + + } + + private String vatFormat(BigDecimal value) { + return nDigitFormat(value, 2); + } + + private String currencyFormat(BigDecimal value) { + return nDigitFormat(value, 2); + } + + private String priceFormat(BigDecimal value) { + return nDigitFormat(value, 2); + } + private String quantityFormat(BigDecimal value) { + return nDigitFormat(value, 2); } /** @@ -218,7 +234,7 @@ public class ZUGFeRDExporter { + " \n" //$NON-NLS-1$ + " false\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " urn:ferd:invoice:rc:comfort\n" //$NON-NLS-1$ + + " urn:ferd:CrossIndustryDocument:invoice:1p0:comfort\n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ @@ -361,14 +377,16 @@ public class ZUGFeRDExporter { + " "+zugferdDateFormat.format(trans.getDueDate())+"\n"//20130704 //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " "+currencyFormat(trans.getTotal(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " 0.00\n" //$NON-NLS-1$ + + " 0.00\n" //$NON-NLS-1$ + + " "+currencyFormat(trans.getTotal())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " 5.80\n" // + " 14.73\n" - + " "+currencyFormat(trans.getTotal(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " "+currencyFormat(trans.getTotalGross().subtract(trans.getTotal()), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " "+currencyFormat(trans.getTotalGross(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+currencyFormat(trans.getTotal())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+currencyFormat(trans.getTotalGross().subtract(trans.getTotal()))+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+currencyFormat(trans.getTotalGross())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " 0.00\n" - + " "+currencyFormat(trans.getTotalGross(), '.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+currencyFormat(trans.getTotalGross())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n"; //$NON-NLS-1$ // + " \n" @@ -390,8 +408,8 @@ public class ZUGFeRDExporter { + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " "+currencyFormat(currentItem.getPriceGross(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " 1\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+priceFormat(currentItem.getPriceGross())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " 1.0000\n" //$NON-NLS-1$ //$NON-NLS-2$ // + " \n" // + " false\n" // + " 0.6667\n" @@ -399,22 +417,22 @@ public class ZUGFeRDExporter { // + " \n" + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " "+currencyFormat(currentItem.getPrice(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ - + " 1\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+priceFormat(currentItem.getPrice())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " 1.0000\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " "+currentItem.getQuantity()+"\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + " "+quantityFormat(currentItem.getQuantity())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " VAT\n" //$NON-NLS-1$ + " S\n" //$NON-NLS-1$ - + " "+currentItem.getProduct().getVATPercent()+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+vatFormat(currentItem.getProduct().getVATPercent())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ - + " "+currencyFormat(currentItem.getTotalGross(),'.')+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + + " "+currencyFormat(currentItem.getTotalGross())+"\n" //$NON-NLS-1$ //$NON-NLS-2$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ + " \n" //$NON-NLS-1$ @@ -445,20 +463,6 @@ public class ZUGFeRDExporter { */ public void PDFattachZugferdFile(PDDocument doc, IZUGFeRDExportableTransaction trans) throws IOException { - // embedded files are stored in a named tree - PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode(); - - String filename="ZUGFeRD-invoice.xml"; //$NON-NLS-1$ - // first create the file specification, which holds the embedded file - PDComplexFileSpecification fs = new PDComplexFileSpecification(); - fs.setFile(filename); - - COSDictionary dict = fs.getCOSDictionary(); - // Relation "Source" for linking with eg. catalog - dict.setName("AFRelationship", "Alternative"); // as defined in Zugferd standard //$NON-NLS-1$ //$NON-NLS-2$ - - dict.setString("UF", filename); //$NON-NLS-1$ - if (zugferdData == null) // XML ZUGFeRD data not set externally, needs to be built { // create a dummy file stream, this would probably normally be a @@ -475,34 +479,8 @@ public class ZUGFeRDExporter { } } - ByteArrayInputStream fakeFile = new ByteArrayInputStream(zugferdData); - PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile); - // now lets some of the optional parameters - ef.setSubtype("text/xml");// as defined in Zugferd standard //$NON-NLS-1$ - ef.setSize(zugferdData.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. - efTree.setNames(Collections.singletonMap(filename, fs)); - PDDocumentNameDictionary names = new PDDocumentNameDictionary( - doc.getDocumentCatalog()); - names.setEmbeddedFiles(efTree); - doc.getDocumentCatalog().setNames(names); - // AF entry (Array) in catalog with the FileSpec - COSArray cosArray = new COSArray(); - cosArray.add(fs); - doc.getDocumentCatalog().getCOSDictionary().setItem("AF", cosArray); //$NON-NLS-1$ + 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); } @@ -511,18 +489,21 @@ public class ZUGFeRDExporter { * * @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 subType, byte[] data) + 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", "Alternative"); + dict.setName("AFRelationship", relationship); dict.setString("UF", filename); + dict.setString("Desc", description); ByteArrayInputStream fakeFile = new ByteArrayInputStream(data); PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile);