diff --git a/History.md b/History.md index 36878c7e..c37b08ec 100644 --- a/History.md +++ b/History.md @@ -1,14 +1,28 @@ -- #420 -- #441 +- 461 +- 463 + +2.13.0 +======= +2024-08-28 + +- Item Attributes and Country of Origin missing on Product. #420 +- Avoid NullPointerException if dueDate is not set. #441 +- support reasoncodes #431 - Enhance Charges/Allowances with reasonCode. #432 - Fix build warnings from editing and building. #415 - ZUGFeRDVisualizer.toPDF(): generate PDF/A-3b. #400 - allow access to invoice attachments via ZUGFeRDInvoiceImporter zii.getFileAttachmentsPDF() and XML (zii.getFileAttachmentsXML) -- #436 and #370. (langfr) +- No interface for required field CreditorReferenceID #436 and +- X-Rechnung direct-debit missing mandatory field BT-90 #370. (langfr) - refactor(ZUGFeRDVisualizer): improve PDF visualization performance #438 - - +- product creation without description now possible empty description +- filename of embedded file was not xrechnung.xml when using profile xrechnung #452 +- allow legalorganisation to have a tradingbusinessname #447 +- JSon deserialization does not work with BankDetails #455 +- Fix ClassCastException in CLI (Main.java). #451 +- changed additional references by line from String to List and implemented it on Item #454 + 2.12.0 ======= 2024-07-20 diff --git a/Mustang-CLI/pom.xml b/Mustang-CLI/pom.xml index 6d3b12ad..5147b159 100644 --- a/Mustang-CLI/pom.xml +++ b/Mustang-CLI/pom.xml @@ -4,7 +4,7 @@ org.mustangproject core - 2.13.1-SNAPSHOT + 2.14.0-SNAPSHOT Mustang-CLI diff --git a/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java b/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java index c7ac50af..e33b5879 100755 --- a/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java +++ b/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java @@ -722,9 +722,6 @@ public class Main { ((ZUGFeRDExporterFromPDFA) ze).ignorePDFAErrors(); } } - for (FileAttachment attachment : attachments) { - ((ZUGFeRDExporterFromA3) ze).attachFile(attachment.getFilename(), attachment.getData(), attachment.getMimetype(), attachment.getRelation()); - } ze.load(pdfName); ze.setProducer("Mustang-cli") @@ -737,6 +734,10 @@ public class Main { ze.setXML(Files.readAllBytes(Paths.get(xmlName))); + for (FileAttachment attachment : attachments) { + ze.attachFile(attachment.getFilename(), attachment.getData(), attachment.getMimetype(), attachment.getRelation()); + } + ze.export(outName); System.out.println("Written to " + outName); diff --git a/library/pom.xml b/library/pom.xml index e62636d5..663003d3 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -5,7 +5,7 @@ org.mustangproject core - 2.13.1-SNAPSHOT + 2.14.0-SNAPSHOT library jar @@ -207,7 +207,7 @@ package - + diff --git a/library/src/main/java/org/mustangproject/BankDetails.java b/library/src/main/java/org/mustangproject/BankDetails.java index b72817ca..c6edcc86 100644 --- a/library/src/main/java/org/mustangproject/BankDetails.java +++ b/library/src/main/java/org/mustangproject/BankDetails.java @@ -1,5 +1,7 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IZUGFeRDTradeSettlementPayment; /** @@ -18,6 +20,10 @@ public class BankDetails implements IZUGFeRDTradeSettlementPayment { * the "name" of the bank account (holder) */ protected String accountName=null; + /*** + * bean constructor + */ + public BankDetails() { } /*** * constructor for IBAN only :-) @@ -80,12 +86,14 @@ public class BankDetails implements IZUGFeRDTradeSettlementPayment { * */ @Override @Deprecated + @JsonIgnore public String getOwnBIC() { return getBIC(); } @Override @Deprecated + @JsonIgnore public String getOwnIBAN() { return getIBAN(); } diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index c1810c57..a16f5e66 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -116,12 +116,20 @@ public class Charge implements IZUGFeRDAllowanceCharge { @Override public BigDecimal getTotalAmount(IAbsoluteValueProvider currentItem) { + if (percent!=null) { + return currentItem.getValue().multiply(getPercent().divide(new BigDecimal(100))); + } else if(totalAmount != null) { + return totalAmount; + } else { + throw new RuntimeException("percent must be set"); + } + } + + public BigDecimal getTotalAmount() { if (totalAmount!=null) { return totalAmount; - } else if (percent!=null) { - return currentItem.getValue().multiply(getPercent().divide(new BigDecimal(100))); } else { - throw new RuntimeException("Either totalAmount or percent must be set"); + throw new RuntimeException("totalAmount must be set"); } } diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 321b4723..d4d417c6 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -26,6 +26,7 @@ public class Item implements IZUGFeRDExportableItem { protected Product product; protected ArrayList notes = null; protected ArrayList referencedDocuments = null; + protected ArrayList additionalReference = null; protected ArrayList Allowances = new ArrayList<>(), Charges = new ArrayList<>(); @@ -63,6 +64,7 @@ public class Item implements IZUGFeRDExportableItem { String referencedLineID = null; ArrayList rdocs = null; + ArrayList addRefs = null; // nodes.item(i).getTextContent())) { @@ -248,6 +250,35 @@ public class Item implements IZUGFeRDExportableItem { } } } + + if (tradeSettlementName.equals("AdditionalReferencedDocument")) { + String IssuerAssignedID = ""; + String TypeCode = ""; + String ReferenceTypeCode = ""; + + NodeList refDocChilds = tradeSettlementChilds.item(tradeSettlementChildIndex).getChildNodes(); + for (int refDocIndex = 0; refDocIndex < refDocChilds.getLength(); refDocIndex++) { + String localName = refDocChilds.item(refDocIndex).getLocalName(); + if ((localName != null) && (localName.equals("IssuerAssignedID"))) { + IssuerAssignedID = refDocChilds.item(refDocIndex).getTextContent(); + } + if ((localName != null) && (localName.equals("TypeCode"))) { + TypeCode = refDocChilds.item(refDocIndex).getTextContent(); + } + if ((localName != null) && (localName.equals("ReferenceTypeCode"))) { + ReferenceTypeCode = refDocChilds.item(refDocIndex).getTextContent(); + } + } + + ReferencedDocument rd = new ReferencedDocument(IssuerAssignedID, TypeCode, + ReferenceTypeCode); + if (addRefs == null) { + addRefs = new ArrayList<>(); + } + addRefs.add(rd); + + } + } } } @@ -274,6 +305,11 @@ public class Item implements IZUGFeRDExportableItem { addReferencedDocument(rdoc); } } + if (addRefs != null) { + for (ReferencedDocument rdoc : addRefs) { + addAdditionalReference(rdoc); + } + } addReferencedLineID( referencedLineID ); } @@ -465,6 +501,29 @@ public class Item implements IZUGFeRDExportableItem { return referencedDocuments.toArray(new IReferencedDocument[0]); } + + /*** + * adds item level references along with their typecodes and issuerassignedIDs (contract ID, cost centre, ...) + * @param doc the ReferencedDocument to add + * @return fluent setter + */ + public Item addAdditionalReference(ReferencedDocument doc) { + if (additionalReference == null) { + additionalReference = new ArrayList<>(); + } + additionalReference.add(doc); + return this; + } + + @Override + public IReferencedDocument[] getAdditionalReferences() { + if (additionalReference == null) { + return null; + } + return additionalReference.toArray(new IReferencedDocument[0]); + } + + /*** * specify a item level delivery period * (apart from the document level delivery period, and the document level diff --git a/library/src/main/java/org/mustangproject/LegalOrganisation.java b/library/src/main/java/org/mustangproject/LegalOrganisation.java index 9ed0aa62..320d7412 100644 --- a/library/src/main/java/org/mustangproject/LegalOrganisation.java +++ b/library/src/main/java/org/mustangproject/LegalOrganisation.java @@ -2,6 +2,8 @@ package org.mustangproject; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.*; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /*** * A organisation, i.e. usually a company @@ -9,35 +11,68 @@ import org.mustangproject.ZUGFeRD.*; @JsonIgnoreProperties(ignoreUnknown = true) public class LegalOrganisation implements IZUGFeRDLegalOrganisation { - protected String ID = null; - protected String SchemeID = null; + protected SchemedID schemedID = null; + protected String tradingBusinessName = null; public LegalOrganisation() { } public LegalOrganisation(String ID, String scheme) { - this.ID=ID; - this.SchemeID=scheme; + this.schemedID = new SchemedID(ID, scheme); + } + + public LegalOrganisation(SchemedID schemedID, String tradingBusinessName) { + this.schemedID = schemedID; + this.tradingBusinessName=tradingBusinessName; + } + + /*** + * XML parsing constructor + * @param nodes the nodelist returned e.g. from xpath + */ + public LegalOrganisation(NodeList nodes) { + if (nodes.getLength() > 0) { + /* + will parse sth like + + 4711 + Test GmbH & Co.KG + + */ + for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) { + Node currentItemNode = nodes.item(nodeIndex); + if (currentItemNode.getLocalName() != null) { + if (currentItemNode.getLocalName().equals("GlobalID")) { + if (currentItemNode.getAttributes().getNamedItem("schemeID") != null) { + SchemedID gid = new SchemedID().setScheme(currentItemNode.getAttributes().getNamedItem("schemeID").getNodeValue()).setId(currentItemNode.getTextContent()); + this.setSchemedID(gid); + } + } + if (currentItemNode.getLocalName().equals("TradingBusinessName")) { + setTradingBusinessName(currentItemNode.getFirstChild().getNodeValue()); + } + } + } + } } @Override - public String getID() { - return ID; + public SchemedID getSchemedID() { + return this.schemedID; } @Override - public String getSchemeID() { - return SchemeID; + public String getTradingBusinessName() { + return this.tradingBusinessName; } - public LegalOrganisation setID(String id) { - this.ID=id; + public LegalOrganisation setSchemedID(SchemedID schemedID) { + this.schemedID = schemedID; return this; } - public LegalOrganisation setSchemeID(String scheme) { - SchemeID=scheme; + public LegalOrganisation setTradingBusinessName(String tradingBusinessName) { + this.tradingBusinessName = tradingBusinessName; return this; } - } diff --git a/library/src/main/java/org/mustangproject/Product.java b/library/src/main/java/org/mustangproject/Product.java index b90a70cd..73e18fd1 100644 --- a/library/src/main/java/org/mustangproject/Product.java +++ b/library/src/main/java/org/mustangproject/Product.java @@ -11,7 +11,10 @@ import java.util.HashMap; */ @JsonIgnoreProperties(ignoreUnknown = true) public class Product implements IZUGFeRDExportableProduct { - protected String unit, name, description, sellerAssignedID, buyerAssignedID; + protected String unit, name, sellerAssignedID, buyerAssignedID; + protected String description=""; + protected String taxExemptionReason=null; + protected String taxCategoryCode=null; protected BigDecimal VATPercent; protected boolean isReverseCharge = false; protected boolean isIntraCommunitySupply = false; @@ -65,6 +68,44 @@ public class Product implements IZUGFeRDExportableProduct { return this; } + /*** + * + * @return e.g. intra-commnunity supply or small business + */ + @Override + public String getTaxExemptionReason() { + return taxExemptionReason; + } + + /*** + * + * @param taxExemptionReasonText String e.g. Kleinunternehmer gemäß §19 UStG https://github.com/ZUGFeRD/mustangproject/issues/463 + * @return + */ + public Product setTaxExemptionReason(String taxExemptionReasonText) { + taxExemptionReason = taxExemptionReasonText; + return this; + } + + /*** + * + * @return e.g. S (normal tax), Z=zero rated, E (e.g. small business) or K (intrra community supply) + */ + @Override + public String getTaxCategoryCode() { + return taxCategoryCode; + } + + /*** + * + * @param code e.g. S (normal tax), Z=zero rated, E (e.g. small business) or K (intrra community supply) see also https://github.com/ZUGFeRD/mustangproject/issues/463 + * @return + */ + public Product setTaxCategoryCode(String code) { + taxCategoryCode = code; + return this; + } + @Override public String getSellerAssignedID() { diff --git a/library/src/main/java/org/mustangproject/TradeParty.java b/library/src/main/java/org/mustangproject/TradeParty.java index 2580fbef..b953008c 100644 --- a/library/src/main/java/org/mustangproject/TradeParty.java +++ b/library/src/main/java/org/mustangproject/TradeParty.java @@ -23,6 +23,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { protected String name, zip, street, location, country; protected String taxID = null, vatID = null; protected String ID = null; + protected String description = null; protected String additionalAddress = null; protected String additionalAddressExtension = null; protected List bankDetails = new ArrayList<>(); @@ -66,14 +67,14 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { Node currentItemNode = nodes.item(nodeIndex); if (currentItemNode.getLocalName() != null) { - String debcurrentChild = currentItemNode.getLocalName(); - if (debcurrentChild.equals("Party")) { + String currentUBLChild = currentItemNode.getLocalName(); + if (currentUBLChild.equals("Party")) { NodeList party = currentItemNode.getChildNodes(); for (int partyIndex = 0; partyIndex < party.getLength(); partyIndex++) { if (party.item(partyIndex).getLocalName() != null) { - String debCN = party.item(partyIndex).getLocalName(); - if (debCN.equals("PartyName")) { + String currentTopElementName = party.item(partyIndex).getLocalName(); + if (currentTopElementName.equals("PartyName")) { NodeList partyName = party.item(partyIndex).getChildNodes(); for (int partyNameIndex = 0; partyNameIndex < partyName.getLength(); partyNameIndex++) { @@ -86,7 +87,20 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { } } } - if (debCN.equals("PostalAddress")) { + + // UBL only: formally it can have a name as well but BT27 party name *should* be stored in + // so overwrite if one exists + if (currentTopElementName.equals("PartyLegalEntity")) { + NodeList legal = party.item(partyIndex).getChildNodes(); + for (int legalChildIndex = 0; legalChildIndex < legal.getLength(); legalChildIndex++) { + if (legal.item(legalChildIndex).getLocalName() != null) { + if (legal.item(legalChildIndex).getLocalName().equals("RegistrationName")) { + setName(legal.item(legalChildIndex).getTextContent()); + } + } + } + } + if (currentTopElementName.equals("PostalAddress")) { NodeList postal = party.item(partyIndex).getChildNodes(); for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) { @@ -144,7 +158,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { } } - if (debCN.equals("Contact")) { + if (currentTopElementName.equals("Contact")) { NodeList contact = party.item(partyIndex).getChildNodes(); setContact(new Contact(contact)); @@ -155,19 +169,19 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { } - if (debcurrentChild.equals("GlobalID")) { + if (currentUBLChild.equals("GlobalID")) { if (nodes.item(nodeIndex).getAttributes().getNamedItem("schemeID") != null) { SchemedID gid = new SchemedID().setScheme(nodes.item(nodeIndex).getAttributes().getNamedItem("schemeID").getNodeValue()).setId(nodes.item(nodeIndex).getTextContent()); addGlobalID(gid); } } - if (debcurrentChild.equals("DefinedTradeContact")) { + if (currentUBLChild.equals("DefinedTradeContact")) { NodeList contact = nodes.item(nodeIndex).getChildNodes(); setContact(new Contact(contact)); } - if (debcurrentChild.equals("PostalTradeAddress")) { + if (currentUBLChild.equals("PostalTradeAddress")) { NodeList postal = nodes.item(nodeIndex).getChildNodes(); for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) { if (postal.item(postalChildIndex).getLocalName() != null) { @@ -195,7 +209,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { } - if (debcurrentChild.equals("SpecifiedTaxRegistration")) { + if (currentUBLChild.equals("SpecifiedTaxRegistration")) { NodeList taxChilds = nodes.item(nodeIndex).getChildNodes(); for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { if (taxChilds.item(taxChildIndex).getLocalName() != null) { @@ -286,8 +300,8 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) { //nodes.item(i).getTextContent())) { - String debLN = nodes.item(nodeIndex).getLocalName(); - if (debLN.equals("Party")) { + String topElementName = nodes.item(nodeIndex).getLocalName(); + if (topElementName.equals("Party")) { // take one step back and parse from top parseFromUBL(nodes); return; @@ -302,12 +316,20 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { if (itemChilds.item(itemChildIndex).getLocalName().equals("Name")) { setName(itemChilds.item(itemChildIndex).getTextContent()); } + + if (itemChilds.item(itemChildIndex).getLocalName().equals("Description")) { + setDescription(itemChilds.item(itemChildIndex).getTextContent()); + } if (itemChilds.item(itemChildIndex).getLocalName().equals("GlobalID")) { if (itemChilds.item(itemChildIndex).getAttributes().getNamedItem("schemeID") != null) { SchemedID gid = new SchemedID().setScheme(itemChilds.item(itemChildIndex).getAttributes().getNamedItem("schemeID").getNodeValue()).setId(itemChilds.item(itemChildIndex).getTextContent()); addGlobalID(gid); } } + if (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedLegalOrganization")) { + NodeList organization = itemChilds.item(itemChildIndex).getChildNodes(); + setLegalOrganisation(new LegalOrganisation(organization)); + } if (itemChilds.item(itemChildIndex).getLocalName().equals("DefinedTradeContact")) { NodeList contact = itemChilds.item(itemChildIndex).getChildNodes(); setContact(new Contact(contact)); @@ -556,6 +578,26 @@ public class TradeParty implements IZUGFeRDExportableTradeParty { } + /*** + * + * @return String the description, e.g. if it's a vat exempt company + */ + public String getDescription() { + return description; + } + + + /*** + * required, usually done in the constructor: the complete name of the organisation + * @param description human readable description + * @return fluent setter + */ + public TradeParty setDescription(String description) { + this.description = description; + return this; + } + + public String getZIP() { return zip; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java index f6e03242..e5e25cae 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java @@ -92,12 +92,22 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{ /*** * the ID of an additionally referenced document for this item + * @deprecated use {@link #getAdditionalReferences()} instead. * @return the id as string */ + @Deprecated default String getAdditionalReferencedDocumentID() { return null; } + /*** + * allows to specify multiple references (billing information) + * @return the referenced documents + */ + default IReferencedDocument[] getAdditionalReferences() { + return null; + } + /*** * allows to specify multiple(!) referenced documents along with e.g. their typecodes diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTradeParty.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTradeParty.java index 8af10dbb..a9977f01 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTradeParty.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTradeParty.java @@ -112,12 +112,17 @@ public interface IZUGFeRDExportableTradeParty { } /** - * First and last name of the recipient + * e.g. first and last name of the owner * - * @return First and last name of the recipient + * @return full name of the party */ String getName(); + /** + * @return description, e.g. if it's a small company + */ + default String getDescription() { return null; } + /** * Postal code of the recipient * diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExporter.java index f056da5c..fd4ffb4c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExporter.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import jakarta.activation.DataSource; +import org.mustangproject.FileAttachment; public interface IZUGFeRDExporter extends Closeable, IExporter { /** @@ -64,6 +65,8 @@ public interface IZUGFeRDExporter extends Closeable, IExporter { public String getNamespaceForVersion(int ver); public String getPrefixForVersion(int ver) ; public IZUGFeRDExporter disableAutoClose(boolean disableAutoClose); + public void attachFile(FileAttachment file); + public void attachFile(String filename, byte[] data, String mimetype, String relation); public IXMLProvider getProvider(); } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDLegalOrganisation.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDLegalOrganisation.java index 9de017cf..09bf14fc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDLegalOrganisation.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDLegalOrganisation.java @@ -18,17 +18,19 @@ *********************************************************************** */ package org.mustangproject.ZUGFeRD; -public interface IZUGFeRDLegalOrganisation { +import org.mustangproject.SchemedID; - /*** - * - * @return the ID of the legal organisation - */ - public String getID(); +public interface IZUGFeRDLegalOrganisation { /** * - * @return the scheme attribute of the legal organization=the type of the identification, e.g. 0002=Siren + * @return the scheme attribute of the legal organization=the type of the identification, e.g. 0002=Siren, and its value */ - public String getSchemeID(); + public SchemedID getSchemedID(); + + /*** + * + * @return the TradingBusinessName of the legal organisation + */ + public String getTradingBusinessName(); } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlement.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlement.java index 038c5d36..3dd18d74 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlement.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlement.java @@ -18,12 +18,15 @@ *********************************************************************** */ package org.mustangproject.ZUGFeRD; +import com.fasterxml.jackson.annotation.JsonIgnore; + public interface IZUGFeRDTradeSettlement { /*** * gets the applicableHeaderTradeSettlement * @return zf2 xml */ + @JsonIgnore String getSettlementXML(); @@ -31,6 +34,7 @@ public interface IZUGFeRDTradeSettlement { * gets the applicableHeaderTradePayment * @return zf2 xml */ + @JsonIgnore default String getPaymentXML() { return null; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java index 2247ae06..9731a9a5 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java @@ -18,6 +18,7 @@ *********************************************************************** */ package org.mustangproject.ZUGFeRD; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.mustangproject.XMLTools; public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement { @@ -27,6 +28,7 @@ public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement * * @return payment information text */ + @JsonIgnore default String getOwnPaymentInfoText() { return null; } @@ -59,6 +61,7 @@ public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement @Override + @JsonIgnore default String getSettlementXML() { String accountNameStr=""; if (getAccountName()!=null) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/TransactionCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/TransactionCalculator.java index b67e7598..4d9382ea 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/TransactionCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/TransactionCalculator.java @@ -172,6 +172,10 @@ public class TransactionCalculator implements IAbsoluteValueProvider { LineCalculator lc = new LineCalculator(currentItem); VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode); + String reasonText=currentItem.getProduct().getTaxExemptionReason(); + if (reasonText!=null) { + itemVATAmount.setVatExemptionReasonText(reasonText); + } VATAmount current = hm.get(percent.stripTrailingZeros()); if (current == null) { hm.put(percent.stripTrailingZeros(), itemVATAmount); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java b/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java index a7a82f61..2a7a7920 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java @@ -32,6 +32,16 @@ import java.math.RoundingMode; */ public class VATAmount { + + protected BigDecimal basis, calculated, applicablePercent; + + protected String categoryCode; + + protected String vatExemptionReasonText; + + protected String dueDateTypeCode; + + public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) { super(); this.basis = basis; @@ -48,35 +58,41 @@ public class VATAmount { this.dueDateTypeCode = dueDateTypeCode; } - BigDecimal basis, calculated, applicablePercent; - - String categoryCode; - - String dueDateTypeCode; - public BigDecimal getApplicablePercent() { return applicablePercent; } - public void setApplicablePercent(BigDecimal applicablePercent) { + public VATAmount setApplicablePercent(BigDecimal applicablePercent) { + this.applicablePercent = applicablePercent; + return this; } public BigDecimal getBasis() { return basis; } - public void setBasis(BigDecimal basis) { + public VATAmount setBasis(BigDecimal basis) { this.basis = basis.setScale(2, RoundingMode.HALF_UP); - + return this; } public BigDecimal getCalculated() { return calculated; } - public void setCalculated(BigDecimal calculated) { + public VATAmount setCalculated(BigDecimal calculated) { this.calculated = calculated; + return this; + } + + public String getVatExemptionReasonText() { + return vatExemptionReasonText; + } + + public VATAmount setVatExemptionReasonText(String theText) { + this.vatExemptionReasonText = theText; + return this; } /** @@ -94,24 +110,27 @@ public class VATAmount { * @deprecated Use {@link #setCategoryCode(String)} instead */ @Deprecated - public void setDocumentCode(String documentCode) { + public VATAmount setDocumentCode(String documentCode) { this.categoryCode = documentCode; + return this; } public String getCategoryCode() { return categoryCode; } - public void setCategoryCode(String categoryCode) { + public VATAmount setCategoryCode(String categoryCode) { this.categoryCode = categoryCode; + return this; } public String getDueDateTypeCode() { return dueDateTypeCode; } - public void setDueDateTypeCode(String dueDateTypeCode) { + public VATAmount setDueDateTypeCode(String dueDateTypeCode) { this.dueDateTypeCode = dueDateTypeCode; + return this; } public VATAmount add(VATAmount v) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java index ba43dd94..b98abebc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java @@ -387,9 +387,10 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider { + "" + currencyFormat(lc.getItemTotalNetAmount()) + "" + ""; - if (currentItem.getAdditionalReferencedDocumentID() != null) { + if (currentItem.getAdditionalReferences() != null) { + xml += "" + currentItem.getAdditionalReferences()[0].getIssuerAssignedID() + "130"; + } else if (currentItem.getAdditionalReferencedDocumentID() != null) { xml += "" + currentItem.getAdditionalReferencedDocumentID() + "130"; - } xml += "" + ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 8577e5a0..1b5b0242 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -51,7 +51,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZUGFeRD2PullProvider implements IXMLProvider { - private static final Logger LOGGER = LoggerFactory.getLogger (ZUGFeRD2PullProvider.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRD2PullProvider.class); protected byte[] zugferdData; protected IExportableTransaction trans; @@ -93,7 +93,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { try { document = DocumentHelper.parseText(new String(zugferdData)); } catch (final DocumentException e1) { - LOGGER.error ("Failed to parse ZUGFeRD data", e1); + LOGGER.error("Failed to parse ZUGFeRD data", e1); } try { final OutputFormat format = OutputFormat.createPrettyPrint(); @@ -103,7 +103,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { res = sw.toString().getBytes(StandardCharsets.UTF_8); } catch (final IOException e) { - LOGGER.error ("Failed to write ZUGFeRD data", e); + LOGGER.error("Failed to write ZUGFeRD data", e); } return res; @@ -138,14 +138,21 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + XMLTools.encodeXML(party.getGlobalID()) + ""; } xml += "" + XMLTools.encodeXML(party.getName()) + ""; - + if (party.getDescription() != null) { + xml += "" + XMLTools.encodeXML(party.getDescription()) + ""; + } if (party.getLegalOrganisation() != null) { xml += " "; - xml += "" + XMLTools.encodeXML(party.getLegalOrganisation().getID()) + ""; + if (party.getLegalOrganisation().getSchemedID() != null) { + xml += "" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getID()) + ""; + } + if (party.getLegalOrganisation().getTradingBusinessName() != null) { + xml += "" + XMLTools.encodeXML(party.getLegalOrganisation().getTradingBusinessName()) + ""; + } xml += ""; } - if ((party.getContact() != null) && (isSender || profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { + if ((party.getContact() != null) && (isSender || profile == Profiles.getByName("EN16931") || profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { xml += ""; if (party.getContact().getName() != null) { xml += "" + XMLTools.encodeXML(party.getContact().getName()) @@ -318,7 +325,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { paymentTermsDescription += discount.getAsXRechnung(); } } else if ((paymentTermsDescription == null) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CORRECTEDINVOICE) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE)) { - if ( trans.getDueDate() != null ) { + if (trans.getDueDate() != null) { paymentTermsDescription = "Please remit until " + germanDateFormat.format(trans.getDueDate()); } } @@ -417,7 +424,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { ""; } if (currentItem.getProduct().getAttributes() != null) { - for ( Entry entry : currentItem.getProduct().getAttributes().entrySet() ) { + for (Entry entry : currentItem.getProduct().getAttributes().entrySet()) { xml += "" + "" + XMLTools.encodeXML(entry.getKey()) + "" + "" + XMLTools.encodeXML(entry.getValue()) + "" + @@ -426,8 +433,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } if (currentItem.getProduct().getCountryOfOrigin() != null) { xml += "" + - XMLTools.encodeXML(currentItem.getProduct().getCountryOfOrigin()) + - ""; + XMLTools.encodeXML(currentItem.getProduct().getCountryOfOrigin()) + + ""; } xml += "" @@ -498,9 +505,16 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + "" + currencyFormat(lc.getItemTotalNetAmount()) + "" // currencyID=\"EUR\" + ""; - if (currentItem.getAdditionalReferencedDocumentID() != null) { + if (currentItem.getAdditionalReferences() != null) { + for (final IReferencedDocument currentReference : currentItem.getAdditionalReferences()) { + xml += "" + + "" + XMLTools.encodeXML(currentReference.getIssuerAssignedID()) + "" + + "130" + + "" + XMLTools.encodeXML(currentReference.getReferenceTypeCode()) + "" + + ""; + } + } else if (currentItem.getAdditionalReferencedDocumentID() != null) { xml += "" + currentItem.getAdditionalReferencedDocumentID() + "130"; - } xml += "" + ""; @@ -640,12 +654,17 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { final String amountDueDateTypeCode = amount.getDueDateTypeCode(); final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode); if (getProfile() != Profiles.getByName("Minimum")) { + String exemptionReasonTextXML = ""; + if ((displayExemptionReason) && (amount.getVatExemptionReasonText() != null)) { + exemptionReasonTextXML = "" + XMLTools.encodeXML(amount.getVatExemptionReasonText()) + ""; + + } xml += "" + "" + currencyFormat(amount.getCalculated()) + "" //currencyID=\"EUR\" + "VAT" - + (displayExemptionReason ? exemptionReason : "") + + exemptionReasonTextXML + "" + currencyFormat(amount.getBasis()) + "" // currencyID=\"EUR\" + "" + amountCategoryCode + "" + (amountDueDateTypeCode != null ? "" + amountDueDateTypeCode + "" : "") @@ -667,7 +686,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if ((trans.getZFCharges() != null) && (trans.getZFCharges().length > 0)) { if (profile == Profiles.getByName("XRechnung")) { - for(IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) { + for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) { xml += "" + "" + "true" + @@ -710,7 +729,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if ((trans.getZFAllowances() != null) && (trans.getZFAllowances().length > 0)) { if (profile == Profiles.getByName("XRechnung")) { - for(IZUGFeRDAllowanceCharge allowance : trans.getZFAllowances()) { + for (IZUGFeRDAllowanceCharge allowance : trans.getZFAllowances()) { xml += "" + "" + "false" + diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java index 0dd548e5..dea69a6f 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java @@ -85,6 +85,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte this.ignorePDFAErrors = true; return this; } + protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE; protected ArrayList fileAttachments = new ArrayList<>(); @@ -223,17 +224,16 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * @return the filename of the file to be embedded */ public String getFilenameForVersion(int ver, Profile profile) { + if (profile.getName().equals("XRECHNUNG")) { + return "xrechnung.xml"; + } if (isFacturX) { return "factur-x.xml"; } else { if (ver == 1) { return "ZUGFeRD-invoice.xml"; } else { - if (profile.getName().equals("XRECHNUNG")) { - return "xrechnung.xml"; - } else { - return "zugferd-invoice.xml"; - } + return "zugferd-invoice.xml"; } } } @@ -247,7 +247,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * @deprecated It's now the default anyway */ @Deprecated - public ZUGFeRDExporterFromA3 setFacturX() { + public ZUGFeRDExporterFromA3 setFacturX() { isFacturX = true; return this; } @@ -260,10 +260,9 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * * @param XRechnungVersion the XRechnung version */ - public void setXRechnungSpecificVersion(String XRechnungVersion) - { - this.XRechnungVersion = XRechnungVersion; - } + public void setXRechnungSpecificVersion(String XRechnungVersion) { + this.XRechnungVersion = XRechnungVersion; + } /*** @@ -308,13 +307,13 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * @throws IOException if anything is wrong in the target location */ @Override - public void export(String ZUGFeRDfilename) throws IOException { + public void export(String ZUGFeRDfilename) throws IOException { if (!documentPrepared) { prepareDocument(); } if ((!fileAttached) && (attachZUGFeRDHeaders)) { throw new IOException( - "File must be attached (usually with setTransaction) before perfoming this operation"); + "File must be attached (usually with setTransaction) before perfoming this operation"); } doc.save(ZUGFeRDfilename, CompressParameters.NO_COMPRESSION); if (!disableAutoClose) { @@ -336,13 +335,13 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * @throws IOException if anything is wrong in the OutputStream */ @Override - public void export(OutputStream output) throws IOException { + public void export(OutputStream output) throws IOException { if (!documentPrepared) { prepareDocument(); } if ((!fileAttached) && (attachZUGFeRDHeaders)) { throw new IOException( - "File must be attached (usually with setTransaction) before perfoming this operation"); + "File must be attached (usually with setTransaction) before perfoming this operation"); } doc.save(output, CompressParameters.NO_COMPRESSION); if (!disableAutoClose) { @@ -440,7 +439,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte cosArray.add(fs); doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray); } else if ((AFEntry instanceof COSObject) && - ((COSObject) AFEntry).getObject() instanceof COSArray) { + ((COSObject) AFEntry).getObject() instanceof COSArray) { COSArray cosArray = (COSArray) ((COSObject) AFEntry).getObject(); cosArray.add(fs); } else { @@ -534,19 +533,18 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte */ protected void addXMP(XMPMetadata metadata) { - String metaDataVersion = null; // default will be used + String metaDataVersion = null; // default will be used - // The XRechnung version may be settable from outside. - if ((this.XRechnungVersion != null) && (this.profile != null) && - this.profile.getName().equalsIgnoreCase(Profiles.getByName("XRECHNUNG").getName())) - { - metaDataVersion = this.XRechnungVersion; - } + // The XRechnung version may be settable from outside. + if ((this.XRechnungVersion != null) && (this.profile != null) && + this.profile.getName().equalsIgnoreCase(Profiles.getByName("XRECHNUNG").getName())) { + metaDataVersion = this.XRechnungVersion; + } if (attachZUGFeRDHeaders) { XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, ZFVersion, isFacturX, xmlProvider.getProfile(), - getNamespaceForVersion(ZFVersion), getPrefixForVersion(ZFVersion), - getFilenameForVersion(ZFVersion, xmlProvider.getProfile()), metaDataVersion); + getNamespaceForVersion(ZFVersion), getPrefixForVersion(ZFVersion), + getFilenameForVersion(ZFVersion, xmlProvider.getProfile()), metaDataVersion); metadata.addSchema(zf); } @@ -557,8 +555,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte } private void removeCidSet(PDDocument doc) - throws IOException - { + throws IOException { // https://github.com/ZUGFeRD/mustangproject/issues/249 COSName cidSet = COSName.getPDFName("CIDSet"); @@ -577,8 +574,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte PDType0Font typedFont = (PDType0Font) pdFont; if (typedFont.getDescendantFont() instanceof PDCIDFontType2) { - @SuppressWarnings ("unused") - PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont(); + @SuppressWarnings("unused") + PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont(); PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor(); fontDescriptor.getCOSObject().removeItem(cidSet); @@ -637,7 +634,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte * @throws IOException if anything is wrong with already loaded PDF */ @Override - public IExporter setTransaction(IExportableTransaction trans) throws IOException { + public IExporter setTransaction(IExportableTransaction trans) throws IOException { this.trans = trans; return prepare(); } @@ -647,22 +644,20 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte xmlProvider.generateXML(trans); String filename = getFilenameForVersion(ZFVersion, xmlProvider.getProfile()); - String relationship = "Alternative"; - // ZUGFeRD 2.1.1 Technical Supplement | Part A | 2.2.2. Data Relationship - // See documentation ZUGFeRD211_EN/Documentation/ZUGFeRD-2.1.1 - Specification_TA_Part-A.pdf - // https://www.ferd-net.de/standards/zugferd-2.1.1/index.html - if ((this.profile != null) && (ZFVersion >= 2)) - { - if (this.profile.getName().equalsIgnoreCase(Profiles.getByName("MINIMUM").getName()) || - this.profile.getName().equalsIgnoreCase(Profiles.getByName("BASICWL").getName())) - { - relationship = "Data"; - } - } + String relationship = "Alternative"; + // ZUGFeRD 2.1.1 Technical Supplement | Part A | 2.2.2. Data Relationship + // See documentation ZUGFeRD211_EN/Documentation/ZUGFeRD-2.1.1 - Specification_TA_Part-A.pdf + // https://www.ferd-net.de/standards/zugferd-2.1.1/index.html + if ((this.profile != null) && (ZFVersion >= 2)) { + if (this.profile.getName().equalsIgnoreCase(Profiles.getByName("MINIMUM").getName()) || + this.profile.getName().equalsIgnoreCase(Profiles.getByName("BASICWL").getName())) { + relationship = "Data"; + } + } PDFAttachGenericFile(doc, filename, relationship, - "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)", - "text/xml", xmlProvider.getXML()); + "Invoice metadata conforming to ZUGFeRD standard (http://www.ferd-net.de/front_content.php?idcat=231&lang=4)", + "text/xml", xmlProvider.getXML()); for (FileAttachment attachment : fileAttachments) { PDFAttachGenericFile(doc, attachment.getFilename(), attachment.getRelation(), attachment.getDescription(), attachment.getMimetype(), attachment.getData()); @@ -674,12 +669,12 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte /** * Reads the XMPMetadata from the PDDocument, if it exists. * Otherwise creates XMPMetadata. + * * @return the finished XMPMetadata object * @throws IOException when e.g. XmpParsingException */ protected XMPMetadata getXmpMetadata() - throws IOException - { + throws IOException { PDMetadata meta = doc.getDocumentCatalog().getMetadata(); if ((meta != null) && (meta.getLength() > 0)) { try { @@ -701,6 +696,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte /** * Sets the producer if the overwrite flag is set or the producer is not already set. * Sets the PDFVersion to 1.4 if the field is empty. + * * @param xmp the metadata as XML */ protected void writeAdobePDFSchema(XMPMetadata xmp) { @@ -823,11 +819,11 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte /** * Adds an OutputIntent and the sRGB color profile if no OutputIntent exist + * * @throws IOException if the ICC file cannot be read or attached to doc */ protected void addSRGBOutputIntend() - throws IOException - { + throws IOException { if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) { return; } @@ -892,12 +888,12 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte public ZUGFeRDExporterFromA3 setZUGFeRDVersion(EStandard est, int version) { this.ZFVersion = version; - if ((version<1) || (version>2)) { + if ((version < 1) || (version > 2)) { throw new IllegalArgumentException("Version not supported"); } - int generation=version; - if ((est==EStandard.facturx)&&(version==1)) { - generation=2; + int generation = version; + if ((est == EStandard.facturx) && (version == 1)) { + generation = 2; } if (generation == 1) { ZUGFeRD1PullProvider z1p = new ZUGFeRD1PullProvider(); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java index 9a4f2c2f..498ae7dc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java @@ -34,6 +34,7 @@ import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.schema.PDFAIdentificationSchema; import org.apache.xmpbox.xml.DomXmpParser; import org.apache.xmpbox.xml.XmpParsingException; +import org.mustangproject.FileAttachment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,7 +73,8 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter { } - protected IZUGFeRDExporter getExporter() { + + public IZUGFeRDExporter getExporter() { if (theExporter==null) { throw new RuntimeException("In ZUGFeRDExporterFromPDFA, source must always be loaded before other operations are performed."); } @@ -247,5 +249,14 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter { public void export(OutputStream output) throws IOException { getExporter().export(output); } + + public void attachFile(FileAttachment file) { + theExporter.attachFile(file); + } + + public void attachFile(String filename, byte[] data, String mimetype, String relation) { + theExporter.attachFile(filename, data, mimetype, relation); + } + } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java index 2029e854..4016e2a1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java @@ -104,7 +104,7 @@ public class ZUGFeRDImporter { /*** * return the file names of all files embedded into the PDF - * @see for XML embedded files please use ZUGFeRDInvoiceImporter.getFileAttachmentsXML + * for XML embedded files please use ZUGFeRDInvoiceImporter.getFileAttachmentsXML * @return a ArrayList of FileAttachments, empty if none */ public List getFileAttachmentsPDF() { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 4a28fd44..117254fb 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -423,7 +423,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { /*** * * @return the file attachments embedded in XML (using base64) decoded as byte array, - * @see for PDF embedded files in FX use getFileAttachmentsPDF() + * for PDF embedded files in FX use getFileAttachmentsPDF() */ public List getFileAttachmentsXML() { return fileAttachments; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java index 185e8ba8..5223b31b 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java @@ -96,6 +96,7 @@ public class ZUGFeRDVisualizer { private TransformerFactory mFactory = null; private Templates mXsltXRTemplate = null; private Templates mXsltUBLTemplate = null; + private Templates mXsltCIOTemplate = null; private Templates mXsltHTMLTemplate = null; private Templates mXsltPDFTemplate = null; private Templates mXsltZF1HTMLTemplate = null; @@ -151,6 +152,8 @@ public class ZUGFeRDVisualizer { String zf2Signature = "CrossIndustryInvoice"; String ublSignature = "Invoice"; String ublCreditNoteSignature = "CreditNote"; + String cioSignature = "SCRDMCCBDACIOMessageStructure" + + ""; boolean doPostProcessing = false; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -173,6 +176,10 @@ public class ZUGFeRDVisualizer { //zf2 or fx applyUBLCreditNote2XSLT(fis, iaos); doPostProcessing = true; + } else if (root.getLocalName().equals(cioSignature)) { + //zf2 or fx + applyCIO2XSLT(fis, iaos); + doPostProcessing = true; } else { throw new IllegalArgumentException("File does not look like CII or UBL"); } @@ -277,20 +284,6 @@ public class ZUGFeRDVisualizer { } catch (FileNotFoundException | TransformerException e) { LOGGER.error("Failed to apply FOP", e); } - /* - FopConfParser parser = null; - try { - //parsing configuration - parser = new FopConfParser(CLASS_LOADER.getResourceAsStream("fop-config.xconf"), new URI("file:///")); - - } catch (SAXException e) { - throw new UncheckedIOException(new IOException(e)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (URISyntaxException e) { - Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e); - }*/ -// FopFactoryBuilder builder = parser.getFopFactoryBuilder(); DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); Configuration cfg = null; @@ -351,6 +344,17 @@ public class ZUGFeRDVisualizer { transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); } + protected void applyCIO2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) + throws TransformerException { + if (mXsltCIOTemplate == null) { + mXsltCIOTemplate = mFactory.newTemplates( + new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cio-xr.xsl"))); + } + Transformer transformer = mXsltCIOTemplate.newTransformer(); + + transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); + } + protected void applyUBL2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) throws TransformerException { if (mXsltUBLTemplate == null) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/model/TaxCategoryCodeTypeConstants.java b/library/src/main/java/org/mustangproject/ZUGFeRD/model/TaxCategoryCodeTypeConstants.java index 62badd8c..c400cbbc 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/model/TaxCategoryCodeTypeConstants.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/model/TaxCategoryCodeTypeConstants.java @@ -30,5 +30,5 @@ public class TaxCategoryCodeTypeConstants { public static final String UNTAXEDSERVICE = "O"; public static final String INTRACOMMUNITY = "K"; - public static Set CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE).collect(Collectors.toSet()); + public static Set CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT).collect(Collectors.toSet()); } diff --git a/library/src/main/resources/stylesheets/cio-xr.xsl b/library/src/main/resources/stylesheets/cio-xr.xsl new file mode 100644 index 00000000..415e2dfe --- /dev/null +++ b/library/src/main/resources/stylesheets/cio-xr.xsl @@ -0,0 +1,2225 @@ + + + + + + Author: KoSIT Bremen (kosit@finanzen.bremen.de) + Fassung vom: 2020-06-30+02:00 + Überführt eine zur EN 16931 konforme elektronische Rechnung in der konkreten Syntax UNCEFACT.CII.D16B in eine Instanz gemäß des Schemas für den Namensraum urn:ce.eu:en16931:2017:xoev-de:kosit:standard:xrechnung-1. + Das Skript setzt voraus, dass das zu verarbeitende Dokument valide bzgl. des XML Schemas und der Schematron-Regeln der Quelle ist. Für nicht valide Dokumente ist das Ergebnis nicht definiert. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ; + + + + + + + + + + + + + + ; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ILLEGAL DATE FORMAT: <para>Mit diesem Datentyp wird ein kalendarisches Datum abgebildet, wie es in der ISO 8601 Spezifikation <quote>Calendar date complete representation</quote> beschrieben ist (siehe ISO 8601:2004, Abschnitt 5.2.1.1). Das Datum beinhaltet keine Zeitangabe. Das konkret zu verwendende Format ist abhängig von der genutzten Syntax.</para> +<para>Der Datentyp basiert auf dem Typ <quote>Date Time. Type</quote>, wie in ISO 15000-5:2014 Anhang B definiert.</para> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Liefert einen XPath-Pfad, welches $n eindeutig identifiziert. + + + + + + + + + + + Liefert einen XPath-Pfad, welches $n eindeutig identifiziert. + + + + + / + + + [ + + ] + + + + /@ + + + diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java index 4b9bbb34..d6a425d4 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java @@ -36,7 +36,7 @@ public class DeSerializationTest extends TestCase { public void testJackson() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); - Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty("some org", "teststr", "55232", "teststadt", "DE").addTaxID("taxID")).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber("0185").addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal("1"), new BigDecimal(1.0))); + Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty("some org", "teststr", "55232", "teststadt", "DE").addTaxID("taxID").addBankDetails(new BankDetails("DE3600000123456", "ABCDEFG1001").setAccountName("Donald Duck"))).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber("0185").addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal("1"), new BigDecimal(1.0))); String jsonArray = mapper.writeValueAsString(i); // [{"stringValue":"a","intValue":1,"booleanValue":true}, diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ProfilesMinimumBasicWLTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ProfilesMinimumBasicWLTest.java index 016dd9f2..af75615f 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ProfilesMinimumBasicWLTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ProfilesMinimumBasicWLTest.java @@ -36,7 +36,8 @@ import java.util.Date; */ public class ProfilesMinimumBasicWLTest extends TestCase { - final String TARGET_PDF_FX_MINIMUM = "./target/testout-Minimum.pdf"; + final String TARGET_PDF_FX_MINIMUM_INV = "./target/testout-Minimum-INV.pdf"; + final String TARGET_PDF_FX_MINIMUM_CN = "./target/testout-Minimum-CN.pdf"; public void testMinimumCreditNote() { String ownNumber = "NUMFACTURE"; @@ -67,7 +68,7 @@ public class ProfilesMinimumBasicWLTest extends TestCase { .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(123), new BigDecimal(1))) .setCreditNote(); ze.setTransaction(i); - ze.export(TARGET_PDF_FX_MINIMUM); + ze.export(TARGET_PDF_FX_MINIMUM_CN); // check for pdf-a schema extension // assertFalse(pdfContent.indexOf("EN 16931") == -1); @@ -79,7 +80,7 @@ public class ProfilesMinimumBasicWLTest extends TestCase { } // now check the contents (like MustangReaderTest) - ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM); + ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM_CN); // Reading ZUGFeRD assertEquals("146.37",zi.getAmount()); @@ -121,7 +122,7 @@ public class ProfilesMinimumBasicWLTest extends TestCase { .setNumber(ownNumber).setTotalPrepaidAmount(new BigDecimal("1")) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(123), new BigDecimal(1))); ze.setTransaction(i); - ze.export(TARGET_PDF_FX_MINIMUM); + ze.export(TARGET_PDF_FX_MINIMUM_INV); // check for pdf-a schema extension // assertFalse(pdfContent.indexOf("EN 16931") == -1); @@ -133,7 +134,7 @@ public class ProfilesMinimumBasicWLTest extends TestCase { } // now check the contents (like MustangReaderTest) - ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM); + ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM_INV); // Reading ZUGFeRD assertEquals("145.37",zi.getAmount()); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 4235d8df..227383e6 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -60,7 +60,9 @@ public class ZF2PushTest extends TestCase { final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf"; public void testPushExport() { - + /*** + * This writes to a filename like an official sample, please consider when changing (probably better not?) + */ // the writing part String orgname = "Bei Spiel GmbH"; String number = "RE-20201121/508"; @@ -92,10 +94,14 @@ public class ZF2PushTest extends TestCase { fail("Exception should not be raised"); } + + // now check the contents (like MustangReaderTest) ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF); assertTrue(zi.getUTF8().contains("DE88200800000970375700")); //the iban assertTrue(zi.getUTF8().contains("Max Mustermann")); //account holder + assertTrue(zi.getUTF8().contains("DueDateDateTime")); //account holder + assertTrue(zi.getUTF8().contains("20201212")); //account holder assertTrue(zi.getUTF8().contains(" + + + + false + + + A1 + + + urn:order-x.eu:1p0:comfort + + + + PO123456789 + Doc Name + 220 + + 202003311232 + + + false + + 9 + AC + + Content of Note + AAI + + + + 20200331 + + + 20200630 + + + + + + + 1 + + WEEE Tax of 0,50 euros per item included + TXD + + + + 1234567890123 + 987654321 + 654987321 + Product Name + Product Description + Product Batch ID (lot ID) + Product Brand Name + + Characteristic_Code + Characteristic Description + 5 meters + + + Class_code + Name Class Codification + + + Product Instances Batch ID + Product Instances Supplier Serial ID + + + 7B + + 5 + 3 + 1 + + + + FR + + + ADD_REF_PROD_ID + ADD_REF_PROD_URIID + 6 + ADD_REF_PROD_Desc + + + + + 1 + + + QUOT_125487 + 3 + + + ADD_REF_DOC_ID + ADD_REF_DOC_URIID + 5 + 916 + ADD_REF_DOC_Desc + + + OBJECT_125487 + 130 + AWV + + + 10.50 + 1.00 + + + false + + 1.00 + 95 + DISCOUNT + + + + true + + 0.50 + AEW + WEEE + + + + 10.00 + 1.00 + + + CATALOG_REF_ID + 2 + + + 2 + + + + + true + + 6 + 3 + 2 + + + + 202004150900 + + + 202004301800 + + + + + + + VAT + S + 20.00 + + + + false + + 10.00 + 60.00 + 6.00 + 64 + SPECIAL AGREEMENT + + + + true + + 10.00 + 60.00 + 6.00 + FC + FREIGHT SERVICES + + + 60.00 + + + BUYER_ACCOUNTING_REF + + + + + + 2 + + WEEE Tax of 0,50 euros per item included + TXD + + + + 548796523 + 598632147 + 698569856 + Product Name + Product Description + Product Batch ID (lot ID) + Product Brand Name + + Characteristic_Code + Characteristic Description + 3 meters + + + Class_code + Name Class Codification + + + Product Instances Batch ID + Product Instances Supplier Serial ID + + + 7B + + 2 + 1 + 3 + + + + FR + + + ADD_REF_PROD_ID + ADD_REF_PROD_URIID + 6 + ADD_REF_PROD_Desc + + + + + 3 + + + QUOT_125487 + 2 + + + ADD_REF_DOC_ID + ADD_REF_DOC_URIID + 5 + 916 + ADD_REF_DOC_Desc + + + OBJECT_125487 + 130 + AWV + + + 19.50 + 2 + + + true + + 0.50 + AEW + WEEE TAX + + + + 20.00 + 2 + + + CATALOG_REF_ID + 2 + + + 3 + + + + + true + + 10.00 + 5 + 2 + + + 20200415 + + + + + + VAT + S + 20.00 + + + + false + + 1.00 + 100.00 + 1.00 + 64 + SPECIAL AGREEMENT + + + + true + + 1.00 + 100.00 + 1.00 + FC + FREIGHT SERVICES + + + 100.00 + + + BUYER_ACCOUNTING_REF + + + + + + 3 + + Content of Note + AAI + + + + 854721548 + 698325417 + 598674321 + Product Name + Product Description + Product Batch ID (lot ID) + Product Brand Name + + Characteristic_Code + Characteristic Description + 3 meters + + + Class_code + Name Class Codification + + + Product Instances Batch ID + Product Instances Supplier Serial ID + + + 7B + + 2 + 1 + 3 + + + + FR + + + ADD_REF_PROD_ID + ADD_REF_PROD_URIID + 6 + ADD_REF_PROD_Desc + + + + + 4 + + + QUOT_125487 + 1 + + + ADD_REF_DOC_ID + ADD_REF_DOC_URIID + 5 + 916 + ADD_REF_DOC_Desc + + + OBJECT_125487 + 130 + AWV + + + 30 + 1 + + + false + + 5 + + + + 25 + 1 + + + CATALOG_REF_ID + 5 + + + 4 + + + + + true + + 6 + 3 + 2 + + + + 20200415 + + + 20200430 + + + + + + + VAT + S + 20.00 + + + + false + + 10.00 + 150.00 + 15.00 + 64 + SPECIAL AGREEMENT + + + + true + + 10.00 + 150.00 + 15.00 + FC + FREIGHT SERVICES + + + 150.00 + + + BUYER_ACCOUNTING_REF + + + + + BUYER_REF_BU123 + + SUPPLIER_ID_321654 + 123654879 + SELLER_NAME + SELLER_ADD_LEGAL_INFORMATION + + 123456789 + SELLER_TRADING_NAME + + + SELLER_CONTACT_NAME + SELLER_CONTACT_DEP + SR + + +33 6 25 64 98 75 + + + contact@seller.com + + + + 75001 + SELLER_ADDR_1 + SELLER_ADDR_2 + SELLER_ADDR_3 + SELLER_CITY + FR + + + sales@seller.com + + + FR 32 123 456 789 + + + SELLER_TAX_ID + + + + BY_ID_9587456 + 98765432179 + BUYER_NAME + + 987654321 + BUYER_TRADING_NAME + + + BUYER_CONTACT_NAME + BUYER_CONTACT_DEP + LB + + +33 6 65 98 75 32 + + + contact@buyer.com + + + + 69001 + BUYER_ADDR_1 + BUYER_ADDR_2 + BUYER_ADDR_3 + BUYER_CITY + FR + + + operation@buyer.com + + + FR 05 987 654 321 + + + BUYER_TAX_ID + + + + BUYER_REQ_ID_25987 + 654987321 + BUYER_REQ_NAME + + 654987321 + BUYER_REQ_TRADING_NAME + + + BUYER_REQ_CONTACT_NAME + BUYER_REQ_CONTACT_DEP + PD + + +33 6 54 98 65 32 + + + requisitioner@buyer.com + + + + 69001 + BUYER_REQ_ADDR_1 + BUYER_REQ_ADDR_2 + BUYER_REQ_ADDR_3 + BUYER_REQ_CITY + FR + + + purchase@buyer.com + + + FR 92 654 987 321 + + + + FCA + Free Carrier + 7 + + DEL_TERMS_LOC_ID + DEL_TERMS_LOC_Name + + + + SALES_REF_ID_459875 + + + PO123456789 + + + QUOT_125487 + + + CONTRACT_2020-25987 + + + REQ_875498 + + + ADD_REF_DOC_ID + ADD_REF_DOC_URIID + 916 + ADD_REF_DOC_Desc + + + TENDER_ID + 50 + + + OBJECT_ID + 130 + AWV + + + CATALOG_ID + + + BLANKET_ORDER_ID + + + PREV_ORDER_C_ID + + + PREV_ORDER_R_ID + + + PROJECT_ID + Project Reference + + + + + SHIP_TO_ID + 5897546912 + SHIP_TO_NAME + + 951632874 + SHIP_TO_TRADING_NAME + + + SHIP_TO_CONTACT_NAME + SHIP_TO_CONTACT_DEP + SD + + +33 6 85 96 32 41 + + + shipto@customer.com + + + + 69003 + SHIP_TO_ADDR_1 + SHIP_TO_ADDR_2 + SHIP_TO_ADDR_3 + SHIP_TO_CITY + FR + + + delivery@buyer.com + + + FR 66 951 632 874 + + + + SHIP_FROM_ID + 875496123 + SHIP_FROM_NAME + + 548963127 + SHIP_FROM_TRADING_NAME + + + SHIP_FROM_CONTACT_NAME + SHIP_FROM_CONTACT_DEP + SD + + +33 6 85 96 32 41 + + + shipfrom@seller.com + + + + 75003 + SHIP_FROM_ADDR_1 + SHIP_FROM_ADDR_2 + SHIP_FROM_ADDR_3 + SHIP_FROM_CITY + FR + + + warehouse@seller.com + + + FR 16 548 963 127 + + + + + 20200415 + + + + 202004150900 + + + 202004301800 + + + + + + EUR + + INVOICEE_ID_9587456 + 98765432179 + INVOICEE_NAME + + 987654321 + INVOICEE_TRADING_NAME + + + INVOICEE_CONTACT_NAME + INVOICEE_CONTACT_DEP + LB + + +33 6 65 98 75 32 + + + invoicee@buyer.com + + + + 69001 + INVOICEE_ADDR_1 + INVOICEE_ADDR_2 + INVOICEE_ADDR_3 + INVOICEE_CITY + FR + + + invoicee@buyer.com + + + FR 05 987 654 321 + + + INVOICEE_TAX_ID + + + + 30 + Credit Transfer + + + + false + + 10.00 + 310 + 31.00 + 64 + SPECIAL AGREEMENT + + VAT + S + 20.00 + + + + + true + + 10.00 + 210.00 + 21.00 + FC + FREIGHT SERVICES + + VAT + S + 20.00 + + + + PAYMENT_TERMS_DESC + + + 310.00 + 21.00 + 31.00 + 300.00 + 60.00 + 360.00 + + + BUYER_ACCOUNT_REF + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index eab363ea..d1d680f8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.mustangproject core - 2.13.1-SNAPSHOT + 2.14.0-SNAPSHOT pom Mustang @@ -83,8 +83,7 @@ scm:git:https://github.com/ZUGFeRD/mustangproject.git scm:git:https://github.com/ZUGFeRD/mustangproject.git https://github.com/ZUGFeRD/mustangproject - HEAD - + @@ -248,7 +247,7 @@ package - + diff --git a/validator/pom.xml b/validator/pom.xml index 7b0152b3..8f2bdcf4 100644 --- a/validator/pom.xml +++ b/validator/pom.xml @@ -4,7 +4,7 @@ org.mustangproject core - 2.13.1-SNAPSHOT + 2.14.0-SNAPSHOT validator diff --git a/validator/src/test/java/org/mustangproject/validator/LibraryTest.java b/validator/src/test/java/org/mustangproject/validator/LibraryTest.java index 2e1b1a17..03b7c782 100644 --- a/validator/src/test/java/org/mustangproject/validator/LibraryTest.java +++ b/validator/src/test/java/org/mustangproject/validator/LibraryTest.java @@ -244,8 +244,31 @@ public class LibraryTest extends ResourceCase { .isEqualTo(0); } - public void testMinimumProfileValidity() { - File tempFile = new File("../library/target/testout-Minimum.pdf"); + public void testMinimumProfileValidityInvoice() { + File tempFile = new File("../library/target/testout-Minimum-INV.pdf"); + ZUGFeRDValidator zfv = new ZUGFeRDValidator(); + + String res = zfv.validate(tempFile.getAbsolutePath()); + + assertThat(res).valueByXPath("count(//error)") + .asInt() + .isEqualTo(0); + assertThat(res).valueByXPath("/validation/summary/@status") + .asString() + .isEqualTo("valid");// expect to be valid because XR notices are, well, only notices + assertThat(res).valueByXPath("/validation/xml/summary/@status") + .asString() + .isEqualTo("valid"); + /** end of errors due to version mismatch*/ + + + assertThat(res).valueByXPath("count(//notice)") + .asInt() + .isEqualTo(0); + } + + public void testMinimumProfileValidityCreditNote() { + File tempFile = new File("../library/target/testout-Minimum-CN.pdf"); ZUGFeRDValidator zfv = new ZUGFeRDValidator(); String res = zfv.validate(tempFile.getAbsolutePath());