diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/Item.java b/library/src/main/java/org/mustangproject/ZUGFeRD/Item.java index b8c2285c..e6424456 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/Item.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/Item.java @@ -7,7 +7,8 @@ import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct; import java.math.BigDecimal; public class Item implements IZUGFeRDExportableItem { - protected BigDecimal price, quantity; + protected BigDecimal price, quantity, tax, grossPrice, lineTotalAmount; + protected String id; protected Product product; public Item(Product product, BigDecimal price, BigDecimal quantity) { @@ -16,6 +17,42 @@ public class Item implements IZUGFeRDExportableItem { this.product = product; } + public BigDecimal getLineTotalAmount() { + return lineTotalAmount; + } + + public Item setLineTotalAmount(BigDecimal lineTotalAmount) { + this.lineTotalAmount = lineTotalAmount; + return this; + } + + public BigDecimal getGrossPrice() { + return grossPrice; + } + + public Item setGrossPrice(BigDecimal grossPrice) { + this.grossPrice = grossPrice; + return this; + } + + public BigDecimal getTax() { + return tax; + } + + public Item setTax(BigDecimal tax) { + this.tax = tax; + return this; + } + + public Item setId(String id) { + this.id = id; + return this; + } + + public String getId() { + return id; + } + @Override public BigDecimal getPrice() { return price; @@ -26,6 +63,8 @@ public class Item implements IZUGFeRDExportableItem { return this; } + + @Override public BigDecimal getQuantity() { return quantity; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/Product.java b/library/src/main/java/org/mustangproject/ZUGFeRD/Product.java index d07186b8..8fcf1c06 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/Product.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/Product.java @@ -5,10 +5,9 @@ import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct; import java.math.BigDecimal; public class Product implements IZUGFeRDExportableProduct { - protected String unit, name, description; + protected String unit, name, description, sellerAssignedID, buyerAssignedID; protected BigDecimal VATPercent; - public Product(String name, String description, String unit, BigDecimal VATPercent) { this.unit = unit; this.name = name; @@ -17,6 +16,24 @@ public class Product implements IZUGFeRDExportableProduct { } + public String getSellerAssignedID() { + return sellerAssignedID; + } + + public Product setSellerAssignedID(String sellerAssignedID) { + this.sellerAssignedID = sellerAssignedID; + return this; + } + + public String getBuyerAssignedID() { + return buyerAssignedID; + } + + public Product setBuyerAssignedID(String buyerAssignedID) { + this.buyerAssignedID = buyerAssignedID; + return this; + } + @Override public String getUnit() { return unit; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java b/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java index 3ba0acc1..33775e9b 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java @@ -38,10 +38,18 @@ public class VATAmount { this.categoryCode = categoryCode; } - BigDecimal basis, calculated; + BigDecimal basis, calculated, applicablePercent; String categoryCode; + public BigDecimal getApplicablePercent() { + return applicablePercent; + } + + public void setApplicablePercent(BigDecimal applicablePercent) { + this.applicablePercent = applicablePercent; + } + public BigDecimal getBasis() { return basis; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java index fa5a4bfd..3e59bbe1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java @@ -17,13 +17,11 @@ package org.mustangproject.ZUGFeRD; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -259,7 +257,16 @@ public class ZUGFeRDImporter { * @return the document code */ public String getDocumentCode() { - return extractString("//HeaderExchangedDocument/TypeCode"); + try { + if (getVersion() == 1) { + return extractString("//HeaderExchangedDocument/TypeCode"); + } else { + return extractString("//ExchangedDocument/TypeCode"); + } + } catch (Exception e) { + e.printStackTrace(); + return ""; + } } @@ -267,7 +274,16 @@ public class ZUGFeRDImporter { * @return the referred document */ public String getReference() { - return extractString("//ApplicableHeaderTradeAgreement/BuyerReference"); + try { + if (getVersion() == 1) { + return extractString("//ApplicableSupplyChainTradeAgreement/BuyerReference"); + } else { + return extractString("//ApplicableHeaderTradeAgreement/BuyerReference"); + } + } catch (Exception e) { + e.printStackTrace(); + return ""; + } } @@ -458,6 +474,10 @@ public class ZUGFeRDImporter { return s.hasNext() ? s.next() : ""; } + /** + * returns an instance of PostalTradeAddress for SellerTradeParty section + * @return an instance of PostalTradeAddress + */ public PostalTradeAddress getSellerTradePartyAddress() { NodeList nl = null; @@ -512,6 +532,165 @@ public class ZUGFeRDImporter { return address; } + + /** + * returns a list of LineItems + * @return a List of LineItem instances + */ + public List getLineItemList() { + List nodeList = getLineItemNodes(); + List lineItemList = new ArrayList<>(); + + for (Node n: nodeList + ) { + Item lineItem = new Item(null, null, null); + lineItem.setProduct(new Product(null,null,null,null)); + + NodeList nl = n.getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node nn = nl.item(i); + Node node = null; + switch (nn.getNodeName()) { + case "ram:SpecifiedLineTradeAgreement": + case "ram:SpecifiedSupplyChainTradeAgreement": + + node = getNodeByName(nn.getChildNodes(), "ram:NetPriceProductTradePrice"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:ChargeAmount"); + lineItem.setPrice(tryBigDecimal(getNodeValue(node))); + } + + node = getNodeByName(nn.getChildNodes(), "ram:GrossPriceProductTradePrice"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:ChargeAmount"); + lineItem.setGrossPrice(tryBigDecimal(getNodeValue(node))); + } + break; + + case "ram:AssociatedDocumentLineDocument": + + node = getNodeByName(nn.getChildNodes(), "ram:LineID"); + lineItem.setId(getNodeValue(node)); + break; + + case "ram:SpecifiedTradeProduct": + + node = getNodeByName(nn.getChildNodes(), "ram:SellerAssignedID"); + lineItem.product.setSellerAssignedID(getNodeValue(node)); + + node = getNodeByName(nn.getChildNodes(), "ram:BuyerAssignedID"); + lineItem.product.setBuyerAssignedID(getNodeValue(node)); + + node = getNodeByName(nn.getChildNodes(), "ram:Name"); + lineItem.product.setName(getNodeValue(node)); + + node = getNodeByName(nn.getChildNodes(), "ram:Description"); + lineItem.product.setDescription(getNodeValue(node)); + break; + + case "ram:SpecifiedLineTradeDelivery": + case "ram:SpecifiedSupplyChainTradeDelivery": + node = getNodeByName(nn.getChildNodes(), "ram:BilledQuantity"); + lineItem.setQuantity(tryBigDecimal(getNodeValue(node))); + break; + + case "ram:SpecifiedLineTradeSettlement": + + node = getNodeByName(nn.getChildNodes(), "ram:ApplicableTradeTax"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:RateApplicablePercent"); + lineItem.product.setVATPercent(tryBigDecimal(getNodeValue(node))); + } + + node = getNodeByName(nn.getChildNodes(), "ram:ApplicableTradeTax"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:CalculatedAmount"); + lineItem.setTax(tryBigDecimal(getNodeValue(node))); + } + + node = getNodeByName(nn.getChildNodes(), "ram:SpecifiedTradeSettlementLineMonetarySummation"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:LineTotalAmount"); + lineItem.setLineTotalAmount(tryBigDecimal(getNodeValue(node))); + } + break; + case "ram:SpecifiedSupplyChainTradeSettlement": + //ZF 1! + + node = getNodeByName(nn.getChildNodes(), "ram:ApplicableTradeTax"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:ApplicablePercent"); + lineItem.product.setVATPercent(tryBigDecimal(getNodeValue(node))); + } + + node = getNodeByName(nn.getChildNodes(), "ram:ApplicableTradeTax"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:CalculatedAmount"); + lineItem.setTax(tryBigDecimal(getNodeValue(node))); + } + + node = getNodeByName(nn.getChildNodes(), "ram:SpecifiedTradeSettlementMonetarySummation"); + if (node != null) { + node = getNodeByName(node.getChildNodes(), "ram:LineTotalAmount"); + lineItem.setLineTotalAmount(tryBigDecimal(getNodeValue(node))); + } + break; + } + } + lineItemList.add(lineItem); + } + return lineItemList; + } + + /** + * returns a List of LineItem Nodes from ZUGFeRD XML + * @return a List of Node instances + */ + public List getLineItemNodes() { + List lineItemNodes = new ArrayList<>(); + NodeList nl = null; + try { + if (getVersion() == 1) { + nl = getNodeListByPath("//CrossIndustryDocument//SpecifiedSupplyChainTradeTransaction//IncludedSupplyChainTradeLineItem"); + } else { + nl = getNodeListByPath("//CrossIndustryInvoice//SupplyChainTradeTransaction//IncludedSupplyChainTradeLineItem"); + } + } catch (Exception e) { + e.printStackTrace(); + } + + for (int i = 0; i < nl.getLength(); i++) { + Node n = nl.item(i); + lineItemNodes.add(n); + } + return lineItemNodes; + } + + /** + * Returns a node, found by name. If more nodes with the same name are present, the first occurence will be returned + * @param nl - A NodeList which may contains the searched node + * @param name The nodes name + * @return a Node or null, if nothing is found + */ + private Node getNodeByName(NodeList nl, String name) { + for (int i = 0; i < nl.getLength(); i++) { + if (nl.item(i).getNodeName() == name) { + return nl.item(i); + } else if (nl.item(i).getChildNodes().getLength() > 0) { + Node node = getNodeByName(nl.item(i).getChildNodes(), name); + if (node != null) { + return node; + } + } + } + return null; + } + + /** + * Get a NodeList by providing an path + * @param path a compliable Path + * @return a Nodelist or null, if an error occurs + */ public NodeList getNodeListByPath(String path) { XPathFactory xpathFact = XPathFactory.newInstance(); @@ -527,4 +706,34 @@ public class ZUGFeRDImporter { } } + /** + * returns the value of an node + * @param node the Node to get the value from + * @return A String or empty String, if no value was found + */ + private String getNodeValue(Node node) { + if (node != null) { + if (node.getFirstChild() != null) { + return node.getFirstChild().getNodeValue(); + } + } + return ""; + } + + /** + * tries to convert an String to BigDecimal. + * @param nodeValue The value as String + * @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs + */ + private BigDecimal tryBigDecimal(String nodeValue) { + try { + return new BigDecimal(nodeValue); + } catch (Exception e) { + try { + return new BigDecimal(Float.valueOf(nodeValue)); + } catch (Exception ex) { + return new BigDecimal("0.00"); + } + } + } } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2Test.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2Test.java index 2f2b85ee..b80b8b37 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2Test.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2Test.java @@ -32,6 +32,7 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ZF2Test extends MustangReaderTestCase { @@ -207,7 +208,8 @@ public class ZF2Test extends MustangReaderTestCase { // Reading ZUGFeRD assertEquals(zi.getAmount(), "571.04"); assertEquals(zi.getHolder(), getOwnOrganisationName()); - + assertEquals(zi.getDocumentCode(),"380"); + assertEquals(zi.getReference(),"AB321"); assertEquals(zi.getAmount(), "571.04"); assertEquals(zi.getBIC(), getTradeSettlementPayment()[0].getOwnBIC()); assertEquals(zi.getIBAN(),getTradeSettlementPayment()[0].getOwnIBAN()); @@ -220,13 +222,23 @@ public class ZF2Test extends MustangReaderTestCase { assertEquals(zi.getSellerTradePartyAddress().getCountrySubDivisionName(), null); assertEquals(zi.getSellerTradePartyAddress().getCountryID(), "DE"); assertEquals(zi.getSellerTradePartyAddress().getCityName(), "Stadthausen"); - + + List li = zi.getLineItemList(); + assertEquals(zi.getLineItemList().get(0).getId().toString(), "1"); + assertEquals(zi.getLineItemList().get(0).product.getBuyerAssignedID(), ""); + assertEquals(zi.getLineItemList().get(0).product.getSellerAssignedID(), ""); + assertEquals(zi.getLineItemList().get(0).getLineTotalAmount().toString(), "160.00"); + assertEquals(zi.getLineItemList().get(0).getQuantity().toString(), "1.0000"); + assertEquals(zi.getLineItemList().get(0).getGrossPrice().toString(), "171.2000"); + assertEquals(zi.getLineItemList().get(0).product.getVATPercent().toString(), "7.00"); + assertEquals(zi.getLineItemList().get(0).product.getName(), "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung"); + assertEquals(zi.getLineItemList().get(0).product.getDescription(), ""); + try { assertEquals(zi.getVersion(), 2); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } - } }