diff --git a/History.md b/History.md index 6fdc6d49..193f7bf3 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,6 @@ +- upgraded en16931 validation to 1.3.8 +- Be able to embed Cross Industry Delivery Advice +- update xr to v. 2.2 2.5.0 ======= 2022-04-07 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 60eea2d4..5c9c2363 100755 --- a/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java +++ b/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java @@ -580,7 +580,7 @@ public class Main { if (format == null) { try { - format = getStringFromUser("Format (fx=Factur-X, zf=ZUGFeRD, ox=Order-X)", "zf", "fx|zf|ox|dx"); + format = getStringFromUser("Format (fx=Factur-X, zf=ZUGFeRD, ox=Order-X, da=Cross Industry Despatch Advice)", "zf", "fx|zf|ox|da"); } catch (Exception e) { LOGGER.error(e.getMessage(), e); } @@ -603,7 +603,7 @@ public class Main { try { if ((format.equals("zf") && (zfIntVersion == 1)) || (format.equals("ox") )) { zfProfile = getStringFromUser("Profile (b)asic, (c)omfort or ex(t)ended", "t", "B|b|C|c|T|t"); - } else if ( (format.equals("dx"))) { + } else if ( (format.equals("da"))) { zfProfile = getStringFromUser("Profile (p)ilot", "p", "P|p"); } else { zfProfile = getStringFromUser( @@ -630,8 +630,8 @@ public class Main { } EStandard standard=EStandard.facturx; - if (format.equals("dx")) { - standard=EStandard.deliverx; + if (format.equals("da")) { + standard=EStandard.deliveradvice; zfConformanceLevelProfile = Profiles.getByName(standard, "PILOT", 1); } @@ -682,7 +682,7 @@ public class Main { ((OXExporterFromA1)ze).ignorePDFAErrors(); } - } else if (format.equals("dx")) { + } else if (format.equals("da")) { ze = new DXExporterFromA1().setProducer("Mustang-cli") .setZUGFeRDVersion(zfIntVersion) .setCreator(System.getProperty("user.name")).setProfile(zfConformanceLevelProfile); diff --git a/library/src/main/java/org/mustangproject/EStandard.java b/library/src/main/java/org/mustangproject/EStandard.java index 0326f7c8..6cf07237 100644 --- a/library/src/main/java/org/mustangproject/EStandard.java +++ b/library/src/main/java/org/mustangproject/EStandard.java @@ -1,6 +1,6 @@ package org.mustangproject; public enum EStandard { - facturx, orderx, deliverx, zugferd, cii, ubl + facturx, orderx, deliveradvice, zugferd, cii, ubl } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java new file mode 100644 index 00000000..5ef1d409 --- /dev/null +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/DAPullProvider.java @@ -0,0 +1,358 @@ +/** + * ********************************************************************* + *

+ * Copyright 2018 Jochen Staerk + *

+ * Use is subject to license terms. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0. + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *

+ * See the License for the specific language governing permissions and + * limitations under the License. + *

+ * ********************************************************************** + */ +package org.mustangproject.ZUGFeRD; + +import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE; +import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE; +import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON; + +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.mustangproject.*; + +public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider { + + protected IExportableTransaction trans; + private String paymentTermsDescription; + protected Profile profile = Profiles.getByName(EStandard.deliveradvice,"pilot", 1); + + + @Override + public void generateXML(IExportableTransaction trans) { + this.trans = trans; + boolean hasDueDate = false; + final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); + + final String exemptionReason = ""; + + String senderReg = ""; + if (trans.getOwnOrganisationFullPlaintextInfo() != null) { + senderReg = "" + + XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + "" + + "REG"; + + } + + String subjectNote = ""; + if (trans.getSubjectNote() != null) { + subjectNote = "" + + XMLTools.encodeXML(trans.getSubjectNote()) + "" + + ""; + } + + final String typecode = "220"; + /*if (trans.getDocumentCode() != null) { + typecode = trans.getDocumentCode(); + }*/ + String notes = ""; + if (trans.getNotes() != null) { + for (final String currentNote : trans.getNotes()) { + notes += "" + XMLTools.encodeXML(currentNote) + ""; + + } + } + String testBooleanStr="true"; + String xml = "\n" + // + " + // xsi:schemaLocation=\"urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100 + // ../Schema/ZUGFeRD1p0.xsd\"" + + "" + // + " + +" "+testBooleanStr+"\n" + // + + "" + + "" + getProfile().getID() + "" + + "" + + "" + + "" + + "" + XMLTools.encodeXML(trans.getNumber()) + "" + // + " RECHNUNG" + // + "380" + + "" + typecode + "" + + "" + + DATE.udtFormat(trans.getIssueDate()) + "" // date + + notes + + subjectNote + + senderReg + + + "" + + ""; + int lineID = 0; + for (final IZUGFeRDExportableItem currentItem : trans.getZFItems()) { + lineID++; + if (currentItem.getProduct().getTaxExemptionReason() != null) { + // exemptionReason = "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; + } + notes = ""; + if (currentItem.getNotes() != null) { + for (final String currentNote : currentItem.getNotes()) { + notes = notes + "" + XMLTools.encodeXML(currentNote) + ""; + + } + } + final LineCalculator lc = new LineCalculator(currentItem); + xml += "" + + "" + + "" + lineID + "" + + notes + + "" + + + ""; + // + " 4012345001235" + if (currentItem.getProduct().getSellerAssignedID() != null) { + xml += "" + + XMLTools.encodeXML(currentItem.getProduct().getSellerAssignedID()) + ""; + } + if (currentItem.getProduct().getBuyerAssignedID() != null) { + xml += "" + + XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + ""; + } + String allowanceChargeStr = ""; + if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) { + for (final IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) { + allowanceChargeStr += getAllowanceChargeStr(allowance, currentItem); + } + } + if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) { + for (final IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) { + allowanceChargeStr += getAllowanceChargeStr(charge, currentItem); + + } + } + + + xml += "" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "" + + "" + XMLTools.encodeXML(currentItem.getProduct().getDescription()) + + "" + + "" + + + "" + + "" + + quantityFormat(currentItem.getQuantity()) + "" + + "" + + ""; + if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) { + xml += ""; + if (currentItem.getDetailedDeliveryPeriodFrom() != null) { + xml += "" + DATE.udtFormat(currentItem.getDetailedDeliveryPeriodFrom()) + ""; + } + if (currentItem.getDetailedDeliveryPeriodTo() != null) { + xml += "" + DATE.udtFormat(currentItem.getDetailedDeliveryPeriodTo()) + ""; + } + xml += ""; + + } + + xml += "" + + "" + currencyFormat(lc.getItemTotalNetAmount()) + + "" // currencyID=\"EUR\" + + ""; + /* if (currentItem.getAdditionalReferencedDocumentID() != null) { + xml += "" + currentItem.getAdditionalReferencedDocumentID() + "130"; + + }*/ + xml += "" + + ""; + + } + + xml += ""; + if (trans.getReferenceNumber() != null) { + xml += "" + XMLTools.encodeXML(trans.getReferenceNumber()) + ""; + + } + xml += "" + + getTradePartyAsXML(trans.getSender(), true, false) + + "" + + ""; + // + " GE2020211" + // + " 4000001987658" + + xml += getTradePartyAsXML(trans.getRecipient(), false, false); + xml += ""; + + if (trans.getSellerOrderReferencedDocumentID() != null) { + xml += "" + + "" + + XMLTools.encodeXML(trans.getSellerOrderReferencedDocumentID()) + "" + + ""; + } + if (trans.getBuyerOrderReferencedDocumentID() != null) { + xml += "" + + "" + + XMLTools.encodeXML(trans.getBuyerOrderReferencedDocumentID()) + "" + + ""; + } + if (trans.getContractReferencedDocument() != null) { + xml += "" + + "" + + XMLTools.encodeXML(trans.getContractReferencedDocument()) + "" + + ""; + } + + // Additional Documents of XRechnung (Rechnungsbegruendende Unterlagen - BG-24 XRechnung) + if (trans.getAdditionalReferencedDocuments() != null) { + for (final FileAttachment f : trans.getAdditionalReferencedDocuments()) { + final String documentContent = new String(Base64.getEncoder().encodeToString(f.getData())); + xml += "" + + "" + f.getFilename() + "" + + "916" + + "" + f.getDescription() + "" + + "" + documentContent + "" + + ""; + } + } + + if (trans.getSpecifiedProcuringProjectID() != null) { + xml += "" + + "" + + XMLTools.encodeXML(trans.getSpecifiedProcuringProjectID()) + ""; + if (trans.getSpecifiedProcuringProjectName() != null) { + xml += "" + XMLTools.encodeXML(trans.getSpecifiedProcuringProjectName()) + ""; + } + xml += ""; + } + xml += "" + + ""; + if (this.trans.getDeliveryAddress() != null) { + xml += "" + + getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) + + ""; + } + xml += " \n" + + " \n" + + " "+ DATE.udtFormat(trans.getDeliveryDate() )+"\n" + + " \n" + + " "; + +/* + xml += "" + + ""; + + if (trans.getDeliveryDate() != null) { + xml += DATE.udtFormat(trans.getDeliveryDate()); + } else { + throw new IllegalStateException("No delivery date provided"); + } + xml += "\n"; + xml += "\n" + + */ + /* + * + "\n" + + * "20130603\n" + + * "2013-51112\n" + + * "\n" + */ + xml+= "\n"; + // + " " + // + " " + // + " " + // + " Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr. + // 2013-51112 in Rechnung zu stellen:\n" + // + " \n" + // + " \n" + // + " \n"; + + xml += "" + + ""; + + final byte[] zugferdRaw; + try { + zugferdRaw = xml.getBytes("UTF-8"); + + zugferdData = XMLTools.removeBOM(zugferdRaw); + } catch (final UnsupportedEncodingException e) { + Logger.getLogger(OXPullProvider.class.getName()).log(Level.SEVERE, null, e); + } + } + + + @Override + public void setProfile(Profile p) { + profile = p; + } + + @Override + public Profile getProfile() { + return profile; + } + + private String buildPaymentTermsXml() { + + final IZUGFeRDPaymentTerms paymentTerms = trans.getPaymentTerms(); + if (paymentTerms == null) { + return ""; + } + String paymentTermsXml = ""; + + final IZUGFeRDPaymentDiscountTerms discountTerms = paymentTerms.getDiscountTerms(); + final Date dueDate = paymentTerms.getDueDate(); + if (dueDate != null && discountTerms != null && discountTerms.getBaseDate() != null) { + throw new IllegalStateException( + "if paymentTerms.dueDate is specified, paymentTerms.discountTerms.baseDate has not to be specified"); + } + paymentTermsXml += "" + paymentTerms.getDescription() + ""; + if (dueDate != null) { + paymentTermsXml += ""; + paymentTermsXml += DATE.udtFormat(dueDate); + paymentTermsXml += ""; + } + + if (discountTerms != null) { + paymentTermsXml += ""; + final String currency = trans.getCurrency(); + final String basisAmount = currencyFormat(calc.getGrandTotal()); + paymentTermsXml += "" + basisAmount + ""; + paymentTermsXml += "" + discountTerms.getCalculationPercentage().toString() + + ""; + + if (discountTerms.getBaseDate() != null) { + final Date baseDate = discountTerms.getBaseDate(); + paymentTermsXml += ""; + paymentTermsXml += DATE.udtFormat(baseDate); + paymentTermsXml += ""; + + paymentTermsXml += "" + + discountTerms.getBasePeriodMeasure() + ""; + } + + paymentTermsXml += ""; + } + + paymentTermsXml += ""; + return paymentTermsXml; + } + + +} diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java index 11fdfc15..7ba37681 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/DXExporterFromA3.java @@ -105,7 +105,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { * OrderX document type. As of version 1.0 it may be * ORDER, ORDER_RESPONSE, or ORDER_CHANGE */ - protected String deliverXDocumentType = "DESPATCHADVICE"; + protected String despatchAdviceDocumentType = "DESPATCHADVICE"; private HashMap additionalXMLs = new HashMap(); @@ -404,7 +404,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { */ public DXExporterFromA3 setDocumentType(String DocumentType) { - this.deliverXDocumentType = DocumentType; + this.despatchAdviceDocumentType = DocumentType; return this; } @@ -428,7 +428,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, 1, true, xmlProvider.getProfile(), "urn:facturx:pdfa:CrossIndustryDocument:despatchadvice:1p0#", "fx", "cida.xml"); - zf.setType(this.deliverXDocumentType); + zf.setType(this.despatchAdviceDocumentType); metadata.addSchema(zf); // also add the schema extensions... @@ -679,7 +679,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { @Override public DXExporterFromA3 setZUGFeRDVersion(int version) { - OXPullProvider z2p = new OXPullProvider(); + DAPullProvider z2p = new DAPullProvider(); setXMLProvider(z2p); return this; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/Profiles.java b/library/src/main/java/org/mustangproject/ZUGFeRD/Profiles.java index 2e81bb18..8f22805e 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/Profiles.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/Profiles.java @@ -33,7 +33,7 @@ public class Profiles { {"BASIC", new Profile("BASIC", "urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic")}, {"EN16931", new Profile("EN16931", "urn:cen.eu:en16931:2017")}, {"EXTENDED", new Profile("EXTENDED", "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended")}, - {"XRECHNUNG", new Profile("XRECHNUNG", "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.1")} + {"XRECHNUNG", new Profile("XRECHNUNG", "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.2")} }).collect(Collectors.toMap(data -> (String) data[0], data -> (Profile) data[1])); static Map zf1Map = Stream.of(new Object[][]{ @@ -51,7 +51,7 @@ public class Profiles { }).collect(Collectors.toMap(data -> (String) data[0], data -> (Profile) data[1])); static Map dx1Map = Stream.of(new Object[][]{ - {"PILOT", new Profile("PILOT", "urn:facturx:pdfa:CrossIndustryDocument:despatchadvice:1p0#")} + {"PILOT", new Profile("PILOT", "urn:awv-net.de:CIDA:1.0:pilot")} }).collect(Collectors.toMap(data -> (String) data[0], data -> (Profile) data[1])); @@ -63,7 +63,7 @@ public class Profiles { throw new RuntimeException("Profile not found"); } return result; - } else if (standard == EStandard.deliverx) { + } else if (standard == EStandard.deliveradvice) { Profile result = null; result = dx1Map.get(name.toUpperCase()); if (result == null) { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/DXTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/DXTest.java index 98db1729..ec4409b5 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/DXTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/DXTest.java @@ -23,9 +23,13 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import org.mustangproject.EStandard; import org.mustangproject.Invoice; +import org.mustangproject.TradeParty; import javax.xml.xpath.XPathExpressionException; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; @@ -38,6 +42,8 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; +import static org.xmlunit.assertj.XmlAssert.assertThat; + @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class DXTest extends MustangReaderTestCase implements IExportableTransaction { final String TARGET_PDF = "./target/testout-DX.pdf"; @@ -198,10 +204,10 @@ public class DXTest extends MustangReaderTestCase implements IExportableTransact EdgeProduct designProduct = new EdgeProduct("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung", "HUR"); EdgeProduct balloonProduct = new EdgeProduct("", "Bestellerweiterung für E&F Umbau", "C62");// test for issue - // 103 + // 103 EdgeProduct airProduct = new EdgeProduct("", "Heiße Luft pro Liter", "LTR"); - Item design=new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct); + Item design = new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct); design.setAddReference("1825"); allItems[0] = design; allItems[1] = new Item(new BigDecimal("0.79"), new BigDecimal("400"), balloonProduct); @@ -268,8 +274,8 @@ public class DXTest extends MustangReaderTestCase implements IExportableTransact .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); DXExporterFromA1 oe = new DXExporterFromA1().setProducer("My Application") - .setCreator(System.getProperty("user.name")).setZUGFeRDVersion(1).ignorePDFAErrors() - .load(SOURCE_PDF)) { + .setCreator(System.getProperty("user.name")).setZUGFeRDVersion(1).ignorePDFAErrors() + .load(SOURCE_PDF)) { oe.setTransaction(this); String theXML = new String(oe.getProvider().getXML(), StandardCharsets.UTF_8); assertTrue(theXML.contains("