implemented sepa direct debit as payment means

This commit is contained in:
Jochen Stärk
2019-09-14 18:47:35 +02:00
parent 3d8c1ad624
commit 211889b2f4
8 changed files with 304 additions and 138 deletions

View File

@@ -79,7 +79,8 @@ You will need a git client on the console, if that's available can e.g. be check
Change to the root of the repo. Change to the root of the repo.
Change to the project directory and run Change to the project directory and run
* `mvn clean install` . If that works you can * `mvn clean install` confirm javadoc is OK with
* `mvn javadoc:javadoc`. If that works you can
* clean the release with `mvn release:clean` and prepare the release with * clean the release with `mvn release:clean` and prepare the release with
* `mvn release:prepare -DignoreSnapshots=true` and enter the version numbers. * `mvn release:prepare -DignoreSnapshots=true` and enter the version numbers.
* After that is through you can create a new release via `mvn release:perform -Dmaven.java.skip=True`.This will also update the maven repo. * After that is through you can create a new release via `mvn release:perform -Dmaven.java.skip=True`.This will also update the maven repo.

View File

@@ -0,0 +1,43 @@
package org.mustangproject;
public class XMLTools {
public static String encodeXML(CharSequence s) {
StringBuilder sb = new StringBuilder();
int len = s.length();
for (int i=0;i<len;i++) {
int c = s.charAt(i);
if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode
}
if (c < 0x80) { // ASCII range: test most common case first
if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
switch(c) {
case '&': sb.append("&amp;"); break;
case '>': sb.append("&gt;"); break;
case '<': sb.append("&lt;"); break;
// Uncomment next two if encoding for an XML attribute
// case '\'' sb.append("&apos;"); break;
// case '\"' sb.append("&quot;"); break;
// Uncomment next three if you prefer, but not required
// case '\n' sb.append("&#10;"); break;
// case '\r' sb.append("&#13;"); break;
// case '\t' sb.append("&#9;"); break;
default: sb.append((char)c);
}
}
} else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
sb.append("&#x");
sb.append(Integer.toHexString(c));
sb.append(';');
}
}
return sb.toString();
}
}

View File

@@ -128,10 +128,20 @@ public interface IZUGFeRDExportableTransaction {
/** /**
* the creditors payment informations * the creditors payment informations
* * @deprecated use getTradeSettlement
* @return an array of IZUGFeRDTradeSettlementPayment * @return an array of IZUGFeRDTradeSettlementPayment
*/ */
IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment(); default IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment() {
return null;
}
/**
* the payment information for any payment means
*
* @return an array of IZUGFeRDTradeSettlement
*/
default IZUGFeRDTradeSettlement[] getTradeSettlement() {
return null;
}
/** /**

View File

@@ -0,0 +1,40 @@
/** **********************************************************************
*
* Copyright 2019 by ak on 12.04.19.
*
* 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;
public interface IZUGFeRDTradeSettlement {
/***
*
* @return zf2 xml for applicableHeaderTradeSettlement
*/
String getSettlementXML();
/***
*
* @return zf2 xml for applicableHeaderTradePayment
*/
default String getPaymentXML() {
return null;
}
}

View File

@@ -0,0 +1,57 @@
/** **********************************************************************
*
* Copyright 2019 by ak on 12.04.19.
*
* 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 org.mustangproject.XMLTools;
public interface IZUGFeRDTradeSettlementDebit extends IZUGFeRDTradeSettlement {
default String getSettlementXML() {
String xml = " <ram:SpecifiedTradeSettlementPaymentMeans>\n" //$NON-NLS-1$
+ " <ram:TypeCode>59</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:PayerPartyDebtorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:IBANID>"+XMLTools.encodeXML(getIBAN())+"</ram:IBANID>\n" //$NON-NLS-1$
+ " </ram:PayerPartyDebtorFinancialAccount>\n"; //$NON-NLS-1$
xml = xml + " </ram:SpecifiedTradeSettlementPaymentMeans>\n"; //$NON-NLS-1$
return xml;
}
default String getPaymentXML() {
return "<ram:DirectDebitMandateID>"+XMLTools.encodeXML(getMandate())+"</ram:DirectDebitMandateID>";
}
/***
* @return IBAN of the debtor (optional)
*/
String getIBAN();
/***
* @return sepa direct debit mandate reference
*/
String getMandate();
}

View File

@@ -18,7 +18,11 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
public interface IZUGFeRDTradeSettlementPayment { import java.text.SimpleDateFormat;
import org.mustangproject.XMLTools;
public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement {
/** /**
* get payment information text. e.g. Bank transfer * get payment information text. e.g. Bank transfer
@@ -78,4 +82,30 @@ public interface IZUGFeRDTradeSettlementPayment {
return null; return null;
} }
default String getSettlementXML() {
String xml = " <ram:SpecifiedTradeSettlementPaymentMeans>\n" //$NON-NLS-1$
+ " <ram:TypeCode>42</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:Information>Überweisung</ram:Information>\n" //$NON-NLS-1$
+ " <ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:IBANID>" + XMLTools.encodeXML(getOwnIBAN()) + "</ram:IBANID>\n"; //$NON-NLS-1$ //$NON-NLS-2$
if (getOwnKto()!=null) {
xml+= " <ram:ProprietaryID>" + XMLTools.encodeXML(getOwnKto()) + "</ram:ProprietaryID>\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
xml+= " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " <ram:BICID>" + XMLTools.encodeXML(getOwnBIC()) + "</ram:BICID>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <ram:Name>"+trans.getOwnBankName()+"</ram:Name>\n" //$NON-NLS-1$
// //$NON-NLS-2$
+ " </ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTradeSettlementPaymentMeans>\n"; //$NON-NLS-1$
return xml;
}
/* I'd love to implement getPaymentXML() and put <ram:DueDateDateTime> there because this is where it belongs
* unfortunately, the due date is part of the transaction which is not accessible here :-(
*/
} }

View File

@@ -18,17 +18,12 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -38,6 +33,7 @@ import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.mustangproject.XMLTools;
import org.mustangproject.toecount.Toecount; import org.mustangproject.toecount.Toecount;
public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
@@ -72,6 +68,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData; protected byte[] zugferdData;
private IZUGFeRDExportableTransaction trans; private IZUGFeRDExportableTransaction trans;
private ZUGFeRDConformanceLevel level; private ZUGFeRDConformanceLevel level;
private String paymentTermsDescription;
public void setProfile(ZUGFeRDConformanceLevel level) { public void setProfile(ZUGFeRDConformanceLevel level) {
this.level = level; this.level = level;
@@ -202,55 +199,28 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
return "urn:cen.eu:en16931:2017"; return "urn:cen.eu:en16931:2017";
} }
public static String encodeXML(CharSequence s) {
StringBuilder sb = new StringBuilder();
int len = s.length();
for (int i=0;i<len;i++) {
int c = s.charAt(i);
if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode
}
if (c < 0x80) { // ASCII range: test most common case first
if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
switch(c) {
case '&': sb.append("&amp;"); break;
case '>': sb.append("&gt;"); break;
case '<': sb.append("&lt;"); break;
// Uncomment next two if encoding for an XML attribute
// case '\'' sb.append("&apos;"); break;
// case '\"' sb.append("&quot;"); break;
// Uncomment next three if you prefer, but not required
// case '\n' sb.append("&#10;"); break;
// case '\r' sb.append("&#13;"); break;
// case '\t' sb.append("&#9;"); break;
default: sb.append((char)c);
}
}
} else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
// Illegal XML character, even encoded. Skip or substitute
sb.append("&#xfffd;"); // Unicode replacement character
} else {
sb.append("&#x");
sb.append(Integer.toHexString(c));
sb.append(';');
}
}
return sb.toString();
}
@Override @Override
public void generateXML(IZUGFeRDExportableTransaction trans) { public void generateXML(IZUGFeRDExportableTransaction trans) {
this.trans = trans; this.trans = trans;
boolean hasDueDate=false;
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); //$NON-NLS-1$ SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); //$NON-NLS-1$
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$ SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
if (paymentTermsDescription==null) {
paymentTermsDescription= "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate());
}
if (trans.getPaymentTermDescription()!=null) {
paymentTermsDescription=trans.getPaymentTermDescription();
}
String senderReg = ""; String senderReg = "";
if (trans.getOwnOrganisationFullPlaintextInfo() != null) { if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
senderReg = "" + "<ram:IncludedNote>\n" + " <ram:Content>\n" senderReg = "" + "<ram:IncludedNote>\n" + " <ram:Content>\n"
+ encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + " </ram:Content>\n" + XMLTools.encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + " </ram:Content>\n"
+ "<ram:SubjectCode>REG</ram:SubjectCode>\n" + "</ram:IncludedNote>\n"; + "<ram:SubjectCode>REG</ram:SubjectCode>\n" + "</ram:IncludedNote>\n";
} }
@@ -272,7 +242,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " </ram:GuidelineSpecifiedDocumentContextParameter>\n" //$NON-NLS-1$ + " </ram:GuidelineSpecifiedDocumentContextParameter>\n" //$NON-NLS-1$
+ " </rsm:ExchangedDocumentContext>\n" //$NON-NLS-1$ + " </rsm:ExchangedDocumentContext>\n" //$NON-NLS-1$
+ " <rsm:ExchangedDocument>\n" //$NON-NLS-1$ + " <rsm:ExchangedDocument>\n" //$NON-NLS-1$
+ " <ram:ID>" + encodeXML(trans.getNumber()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:ID>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <ram:Name>RECHNUNG</ram:Name>\n" //$NON-NLS-1$ // + " <ram:Name>RECHNUNG</ram:Name>\n" //$NON-NLS-1$
+ " <ram:TypeCode>380</ram:TypeCode>\n" //$NON-NLS-1$ + " <ram:TypeCode>380</ram:TypeCode>\n" //$NON-NLS-1$
+ " <ram:IssueDateTime><udt:DateTimeString format=\"102\">" //$NON-NLS-1$ + " <ram:IssueDateTime><udt:DateTimeString format=\"102\">" //$NON-NLS-1$
@@ -308,8 +278,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
// + " <GlobalID schemeID=\"0160\">4012345001235</GlobalID>\n" // + " <GlobalID schemeID=\"0160\">4012345001235</GlobalID>\n"
// + " <SellerAssignedID>KR3M</SellerAssignedID>\n" // + " <SellerAssignedID>KR3M</SellerAssignedID>\n"
// + " <BuyerAssignedID>55T01</BuyerAssignedID>\n" // + " <BuyerAssignedID>55T01</BuyerAssignedID>\n"
+ " <ram:Name>" + encodeXML(currentItem.getProduct().getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:Description>" + encodeXML(currentItem.getProduct().getDescription()) //$NON-NLS-1$ + " <ram:Description>" + XMLTools.encodeXML(currentItem.getProduct().getDescription()) //$NON-NLS-1$
+ "</ram:Description>\n" //$NON-NLS-1$ + "</ram:Description>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTradeProduct>\n" //$NON-NLS-1$ + " </ram:SpecifiedTradeProduct>\n" //$NON-NLS-1$
@@ -317,7 +287,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " <ram:GrossPriceProductTradePrice>\n" //$NON-NLS-1$ + " <ram:GrossPriceProductTradePrice>\n" //$NON-NLS-1$
+ " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) //$NON-NLS-1$ + " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) //$NON-NLS-1$
+ "</ram:ChargeAmount>\n" //$NON-NLS-1$ //currencyID=\"EUR\" + "</ram:ChargeAmount>\n" //$NON-NLS-1$ //currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$ + " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$
+ "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$ + "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$
// + " <AppliedTradeAllowanceCharge>\n" // + " <AppliedTradeAllowanceCharge>\n"
// + " <ChargeIndicator>false</ChargeIndicator>\n" // + " <ChargeIndicator>false</ChargeIndicator>\n"
@@ -328,13 +298,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " <ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$ + " <ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$
+ " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) //$NON-NLS-1$ + " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) //$NON-NLS-1$
+ "</ram:ChargeAmount>\n" //$NON-NLS-1$ // currencyID=\"EUR\" + "</ram:ChargeAmount>\n" //$NON-NLS-1$ // currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$ + " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) //$NON-NLS-1$
+ "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$ + "\">1.0000</ram:BasisQuantity>\n" //$NON-NLS-1$
+ " </ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$ + " </ram:NetPriceProductTradePrice>\n" //$NON-NLS-1$
+ " </ram:SpecifiedLineTradeAgreement>\n" //$NON-NLS-1$ + " </ram:SpecifiedLineTradeAgreement>\n" //$NON-NLS-1$
+ " <ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$ + " <ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$
+ " <ram:BilledQuantity unitCode=\"" + encodeXML(currentItem.getProduct().getUnit()) + "\">" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:BilledQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + "\">" //$NON-NLS-1$ //$NON-NLS-2$
+ quantityFormat(currentItem.getQuantity()) + "</ram:BilledQuantity>\n" //$NON-NLS-1$ + quantityFormat(currentItem.getQuantity()) + "</ram:BilledQuantity>\n" //$NON-NLS-1$
+ " </ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$ + " </ram:SpecifiedLineTradeDelivery>\n" //$NON-NLS-1$
+ " <ram:SpecifiedLineTradeSettlement>\n" //$NON-NLS-1$ + " <ram:SpecifiedLineTradeSettlement>\n" //$NON-NLS-1$
@@ -355,34 +325,34 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
xml = xml + " <ram:ApplicableHeaderTradeAgreement>\n"; //$NON-NLS-1$ xml = xml + " <ram:ApplicableHeaderTradeAgreement>\n"; //$NON-NLS-1$
if (trans.getReferenceNumber() != null) { if (trans.getReferenceNumber() != null) {
xml = xml + " <ram:BuyerReference>" + encodeXML(trans.getReferenceNumber()) + "</ram:BuyerReference>\n"; xml = xml + " <ram:BuyerReference>" + XMLTools.encodeXML(trans.getReferenceNumber()) + "</ram:BuyerReference>\n";
} }
xml = xml + " <ram:SellerTradeParty>\n" //$NON-NLS-1$ xml = xml + " <ram:SellerTradeParty>\n" //$NON-NLS-1$
// + " <GlobalID schemeID=\"0088\">4000001123452</GlobalID>\n" // + " <GlobalID schemeID=\"0088\">4000001123452</GlobalID>\n"
+ " <ram:Name>" + encodeXML(trans.getOwnOrganisationName()) + "</ram:Name>\n"; //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:Name>" + XMLTools.encodeXML(trans.getOwnOrganisationName()) + "</ram:Name>\n"; //$NON-NLS-1$ //$NON-NLS-2$
if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) { if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) {
xml = xml + " <ram:SpecifiedLegalOrganization>\n" + " <ram:ID schemeID='9930'>" xml = xml + " <ram:SpecifiedLegalOrganization>\n" + " <ram:ID schemeID='9930'>"
+ encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" + " <ram:TradingBusinessName>" + XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" + " <ram:TradingBusinessName>"
+ encodeXML(trans.getOwnOrganisationName()) + "</ram:TradingBusinessName>\n" + XMLTools.encodeXML(trans.getOwnOrganisationName()) + "</ram:TradingBusinessName>\n"
+ " </ram:SpecifiedLegalOrganization>"; + " </ram:SpecifiedLegalOrganization>";
} }
if (trans.getOwnContact() != null) { if (trans.getOwnContact() != null) {
xml = xml + "<ram:DefinedTradeContact>\n" + " <ram:PersonName>" + encodeXML(trans.getOwnContact().getName()) xml = xml + "<ram:DefinedTradeContact>\n" + " <ram:PersonName>" + XMLTools.encodeXML(trans.getOwnContact().getName())
+ "</ram:PersonName>\n"; + "</ram:PersonName>\n";
if (trans.getOwnContact().getPhone() != null) { if (trans.getOwnContact().getPhone() != null) {
xml = xml + " <ram:TelephoneUniversalCommunication>\n" + " <ram:CompleteNumber>" xml = xml + " <ram:TelephoneUniversalCommunication>\n" + " <ram:CompleteNumber>"
+ encodeXML(trans.getOwnContact().getPhone()) + "</ram:CompleteNumber>\n" + XMLTools.encodeXML(trans.getOwnContact().getPhone()) + "</ram:CompleteNumber>\n"
+ " </ram:TelephoneUniversalCommunication>\n"; + " </ram:TelephoneUniversalCommunication>\n";
} }
if (trans.getOwnContact().getEMail() != null) { if (trans.getOwnContact().getEMail() != null) {
xml = xml + " <ram:EmailURIUniversalCommunication>\n" + " <ram:URIID>" xml = xml + " <ram:EmailURIUniversalCommunication>\n" + " <ram:URIID>"
+ encodeXML(trans.getOwnContact().getEMail()) + "</ram:URIID>\n" + XMLTools.encodeXML(trans.getOwnContact().getEMail()) + "</ram:URIID>\n"
+ " </ram:EmailURIUniversalCommunication>\n"; + " </ram:EmailURIUniversalCommunication>\n";
} }
xml = xml + " </ram:DefinedTradeContact>"; xml = xml + " </ram:DefinedTradeContact>";
@@ -390,33 +360,33 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
} }
xml = xml + " <ram:PostalTradeAddress>\n" + " <ram:PostcodeCode>" xml = xml + " <ram:PostalTradeAddress>\n" + " <ram:PostcodeCode>"
+ encodeXML(trans.getOwnZIP()) + "</ram:PostcodeCode>\n" + " <ram:LineOne>" + XMLTools.encodeXML(trans.getOwnZIP()) + "</ram:PostcodeCode>\n" + " <ram:LineOne>"
+ encodeXML(trans.getOwnStreet()) + "</ram:LineOne>\n" + " <ram:CityName>" + encodeXML(trans.getOwnLocation()) + XMLTools.encodeXML(trans.getOwnStreet()) + "</ram:LineOne>\n" + " <ram:CityName>" + XMLTools.encodeXML(trans.getOwnLocation())
+ "</ram:CityName>\n" + " <ram:CountryID>" + encodeXML(trans.getOwnCountry()) + "</ram:CityName>\n" + " <ram:CountryID>" + XMLTools.encodeXML(trans.getOwnCountry())
+ "</ram:CountryID>\n" + " </ram:PostalTradeAddress>\n" + "</ram:CountryID>\n" + " </ram:PostalTradeAddress>\n"
+ " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ + " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"FC\">" + encodeXML(trans.getOwnTaxID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:ID schemeID=\"FC\">" + XMLTools.encodeXML(trans.getOwnTaxID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ + " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ + " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"VA\">" + encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(trans.getOwnVATID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ + " </ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " </ram:SellerTradeParty>\n" //$NON-NLS-1$ + " </ram:SellerTradeParty>\n" //$NON-NLS-1$
+ " <ram:BuyerTradeParty>\n" //$NON-NLS-1$ + " <ram:BuyerTradeParty>\n" //$NON-NLS-1$
// + " <ID>GE2020211</ID>\n" // + " <ID>GE2020211</ID>\n"
// + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n" // + " <GlobalID schemeID=\"0088\">4000001987658</GlobalID>\n"
+ " <ram:Name>" + encodeXML(trans.getRecipient().getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:Name>" + XMLTools.encodeXML(trans.getRecipient().getName()) + "</ram:Name>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <DefinedTradeContact>\n" // + " <DefinedTradeContact>\n"
// + " <PersonName>xxx</PersonName>\n" // + " <PersonName>xxx</PersonName>\n"
// + " </DefinedTradeContact>\n" // + " </DefinedTradeContact>\n"
+ " <ram:PostalTradeAddress>\n" //$NON-NLS-1$ + " <ram:PostalTradeAddress>\n" //$NON-NLS-1$
+ " <ram:PostcodeCode>" + encodeXML(trans.getRecipient().getZIP()) + "</ram:PostcodeCode>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:PostcodeCode>" + XMLTools.encodeXML(trans.getRecipient().getZIP()) + "</ram:PostcodeCode>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:LineOne>" + encodeXML(trans.getRecipient().getStreet()) + "</ram:LineOne>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:LineOne>" + XMLTools.encodeXML(trans.getRecipient().getStreet()) + "</ram:LineOne>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:CityName>" + encodeXML(trans.getRecipient().getLocation()) + "</ram:CityName>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:CityName>" + XMLTools.encodeXML(trans.getRecipient().getLocation()) + "</ram:CityName>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:CountryID>" + encodeXML(trans.getRecipient().getCountry()) + "</ram:CountryID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:CountryID>" + XMLTools.encodeXML(trans.getRecipient().getCountry()) + "</ram:CountryID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:PostalTradeAddress>\n"; //$NON-NLS-1$ + " </ram:PostalTradeAddress>\n"; //$NON-NLS-1$
if (trans.getRecipient().getVATID() != null) { if (trans.getRecipient().getVATID() != null) {
xml += " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$ xml += " <ram:SpecifiedTaxRegistration>\n" //$NON-NLS-1$
+ " <ram:ID schemeID=\"VA\">" + encodeXML(trans.getRecipient().getVATID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(trans.getRecipient().getVATID()) + "</ram:ID>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " </ram:SpecifiedTaxRegistration>\n"; //$NON-NLS-1$ + " </ram:SpecifiedTaxRegistration>\n"; //$NON-NLS-1$
} }
xml += " </ram:BuyerTradeParty>\n" //$NON-NLS-1$ xml += " </ram:BuyerTradeParty>\n" //$NON-NLS-1$
@@ -436,26 +406,26 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
* " </DeliveryNoteReferencedDocument>\n" * " </DeliveryNoteReferencedDocument>\n"
*/ */
+ " </ram:ApplicableHeaderTradeDelivery>\n" + " <ram:ApplicableHeaderTradeSettlement>\n" //$NON-NLS-2$ + " </ram:ApplicableHeaderTradeDelivery>\n" + " <ram:ApplicableHeaderTradeSettlement>\n" //$NON-NLS-2$
+ " <ram:PaymentReference>" + encodeXML(trans.getNumber()) + "</ram:PaymentReference>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:PaymentReference>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:PaymentReference>\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>\n"; //$NON-NLS-1$ + " <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>\n"; //$NON-NLS-1$
if (trans.getTradeSettlementPayment()!=null) {
for (IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) { for (IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) {
xml += " <ram:SpecifiedTradeSettlementPaymentMeans>\n" //$NON-NLS-1$ if(payment!=null) {
+ " <ram:TypeCode>42</ram:TypeCode>\n" //$NON-NLS-1$ hasDueDate=true;
+ " <ram:Information>Überweisung</ram:Information>\n" //$NON-NLS-1$ xml+=payment.getSettlementXML();
+ " <ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$ }
+ " <ram:IBANID>" + encodeXML(payment.getOwnIBAN()) + "</ram:IBANID>\n"; //$NON-NLS-1$ //$NON-NLS-2$ }
if (payment.getOwnKto()!=null) { }
xml+= " <ram:ProprietaryID>" + encodeXML(payment.getOwnKto()) + "</ram:ProprietaryID>\n"; //$NON-NLS-1$ //$NON-NLS-2$ if (trans.getTradeSettlement()!=null) {
for (IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
if(payment!=null) {
if (payment instanceof IZUGFeRDTradeSettlementPayment) {
hasDueDate=true;
}
xml+=payment.getSettlementXML();
}
} }
xml+= " </ram:PayeePartyCreditorFinancialAccount>\n" //$NON-NLS-1$
+ " <ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " <ram:BICID>" + encodeXML(payment.getOwnBIC()) + "</ram:BICID>\n" //$NON-NLS-1$ //$NON-NLS-2$
// + " <ram:Name>"+trans.getOwnBankName()+"</ram:Name>\n" //$NON-NLS-1$
// //$NON-NLS-2$
+ " </ram:PayeeSpecifiedCreditorFinancialInstitution>\n" //$NON-NLS-1$
+ " </ram:SpecifiedTradeSettlementPaymentMeans>\n"; //$NON-NLS-1$
} }
HashMap<BigDecimal, VATAmount> VATPercentAmountMap = getVATPercentAmountMap(); HashMap<BigDecimal, VATAmount> VATPercentAmountMap = getVATPercentAmountMap();
@@ -473,45 +443,29 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
} }
} }
/*
* xml+= " + " <SpecifiedTradeAllowanceCharge>\n" +
* " <ChargeIndicator>false</ChargeIndicator>\n" +
* " <BasisAmount currencyID=\"EUR\">10</BasisAmount>\n" +
* " <ActualAmount>1.00</ActualAmount>\n" +
* " <Reason>Sondernachlass</Reason>\n" +
* " <CategoryTradeTax>\n" +
* " <TypeCode>VAT</TypeCode>\n" +
* " <CategoryCode>S</CategoryCode>\n" +
* " <ApplicablePercent>19</ApplicablePercent>\n" +
* " </CategoryTradeTax>\n" +
* " </SpecifiedTradeAllowanceCharge>\n" +
* " <SpecifiedTradeAllowanceCharge>\n" +
* " <ChargeIndicator>false</ChargeIndicator>\n" +
* " <BasisAmount currencyID=\"EUR\">137.30</BasisAmount>\n" +
* " <ActualAmount>13.73</ActualAmount>\n" +
* " <Reason>Sondernachlass</Reason>\n" +
* " <CategoryTradeTax>\n" +
* " <TypeCode>VAT</TypeCode>\n" +
* " <CategoryCode>S</CategoryCode>\n" +
* " <ApplicablePercent>7</ApplicablePercent>\n" +
* " </CategoryTradeTax>\n" +
* " </SpecifiedTradeAllowanceCharge>\n" +
* " <SpecifiedLogisticsServiceCharge>\n" +
* " <Description>Versandkosten</Description>\n" +
* " <AppliedAmount>5.80</AppliedAmount>\n" +
* " <AppliedTradeTax>\n" +
* " <TypeCode>VAT</TypeCode>\n" +
* " <CategoryCode>S</CategoryCode>\n" +
* " <ApplicablePercent>7</ApplicablePercent>\n" +
* " </AppliedTradeTax>\n" +
* " </SpecifiedLogisticsServiceCharge>\n"
*/
xml = xml + " <ram:SpecifiedTradePaymentTerms>\n" //$NON-NLS-1$ xml = xml + " <ram:SpecifiedTradePaymentTerms>\n" //$NON-NLS-1$
+ " <ram:Description>Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate()) + " <ram:Description>"+
+ "</ram:Description>\n" + " <ram:DueDateDateTime><udt:DateTimeString format=\"102\">" //$NON-NLS-2$ paymentTermsDescription
+ zugferdDateFormat.format(trans.getDueDate()) + "</udt:DateTimeString></ram:DueDateDateTime>\n"// 20130704 //$NON-NLS-1$ + "</ram:Description>\n";
+ " </ram:SpecifiedTradePaymentTerms>\n" //$NON-NLS-1$
if (trans.getTradeSettlement()!=null) {
for (IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
if(payment!=null) {
xml+=payment.getPaymentXML();
}
}
}
if(hasDueDate) {
xml=xml+" <ram:DueDateDateTime><udt:DateTimeString format=\"102\">" //$NON-NLS-2$
+ zugferdDateFormat.format(trans.getDueDate()) + "</udt:DateTimeString></ram:DueDateDateTime>\n";// 20130704 //$NON-NLS-1$
}
xml = xml + " </ram:SpecifiedTradePaymentTerms>\n" //$NON-NLS-1$
+ " <ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$ + " <ram:SpecifiedTradeSettlementHeaderMonetarySummation>\n" //$NON-NLS-1$
+ " <ram:LineTotalAmount>" + currencyFormat(getTotal()) + "</ram:LineTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$ + " <ram:LineTotalAmount>" + currencyFormat(getTotal()) + "</ram:LineTotalAmount>\n" //$NON-NLS-1$ //$NON-NLS-2$
// currencyID=\"EUR\" // currencyID=\"EUR\"

View File

@@ -18,24 +18,53 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import junit.framework.Test;
import junit.framework.TestSuite;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ZF2EdgeTest extends MustangReaderTestCase { public class ZF2EdgeTest extends MustangReaderTestCase {
final String TARGET_PDF = "./target/testout-ZF2newEdge.pdf"; final String TARGET_PDF = "./target/testout-ZF2newEdge.pdf";
protected class DebitPayment implements IZUGFeRDTradeSettlementDebit {
@Override
public String getIBAN() {
return "DE540815";
}
@Override
public String getMandate() {
return "DE99XX12345";
}
}
@Override
public IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment() {
return null;
}
@Override
public IZUGFeRDTradeSettlement[] getTradeSettlement() {
IZUGFeRDTradeSettlement[] payments = new DebitPayment[1];
payments[0] = new DebitPayment();
return payments;
}
@Override @Override
public Date getDeliveryDate() { public Date getDeliveryDate() {
return new GregorianCalendar(2017, Calendar.MAY, 7).getTime(); return new GregorianCalendar(2017, Calendar.MAY, 7).getTime();
@@ -129,8 +158,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase {
@Override @Override
public String getPaymentTermDescription() { public String getPaymentTermDescription() {
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); return "Zahlbar sofort";
return "Zahlbar ohne Abzug bis zum " + germanDateFormat.format(getDueDate());
} }
@Override @Override
@@ -191,6 +219,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase {
ze.PDFattachZugferdFile(this); ze.PDFattachZugferdFile(this);
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertTrue(theXML.contains("Zahlbar sofort"));
ze.export(TARGET_PDF); ze.export(TARGET_PDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised in testEdgeExport");
@@ -199,10 +228,12 @@ public class ZF2EdgeTest extends MustangReaderTestCase {
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF); ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF);
assertTrue(zi.getUTF8().contains("<ram:TypeCode>59</ram:TypeCode>"));
assertTrue(zi.getUTF8().contains("<ram:IBANID>DE540815</ram:IBANID>"));
assertTrue(zi.getUTF8().contains("<ram:DirectDebitMandateID>DE99XX12345</ram:DirectDebitMandateID>"));
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals(zi.getAmount(), "571.04"); assertEquals(zi.getAmount(), "571.04");
assertEquals(zi.getBIC(), getTradeSettlementPayment()[0].getOwnBIC());
assertEquals(zi.getIBAN(), getTradeSettlementPayment()[0].getOwnIBAN());
assertEquals(zi.getHolder(), getOwnOrganisationName()); assertEquals(zi.getHolder(), getOwnOrganisationName());
assertEquals(zi.getForeignReference(), getNumber()); assertEquals(zi.getForeignReference(), getNumber());
try { try {