- prevent nullpointerexception https://github.com/ZUGFeRD/mustangproject/pull/302 thanks to weclapp-dev
- lines no longer included in basic-wl and minimum - due date and delivery date no longer mandatory for credit notes - Allow foreign limited addresses, e.g. allow Parties withpout postal addresses/postcodes - Allow invoices in minimum profile e.g. dont use applicabletradetax, linetotalamount, paymentreference etc in Minimum profile
This commit is contained in:
@@ -6,6 +6,11 @@
|
||||
- Corrected forgotten --d CLI shortcut
|
||||
- No uber jar #297
|
||||
- automated server tests
|
||||
- prevent nullpointerexception https://github.com/ZUGFeRD/mustangproject/pull/302 thanks to weclapp-dev
|
||||
- lines no longer included in basic-wl and minimum
|
||||
- due date and delivery date no longer mandatory for credit notes
|
||||
- Allow foreign limited addresses, e.g. allow Parties withpout postal addresses/postcodes
|
||||
- Allow invoices in minimum profile e.g. dont use applicabletradetax, linetotalamount, paymentreference etc in Minimum profile
|
||||
|
||||
2.5.7
|
||||
=======
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
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.IOException;
|
||||
@@ -43,6 +42,7 @@ import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import org.mustangproject.FileAttachment;
|
||||
import org.mustangproject.XMLTools;
|
||||
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
|
||||
|
||||
public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
|
||||
@@ -166,9 +166,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
xml += "</ram:DefinedTradeContact>";
|
||||
|
||||
}
|
||||
xml += "<ram:PostalTradeAddress>"
|
||||
+ "<ram:PostcodeCode>" + XMLTools.encodeXML(party.getZIP())
|
||||
|
||||
xml += "<ram:PostalTradeAddress>";
|
||||
if (party.getZIP() != null) {
|
||||
xml += "<ram:PostcodeCode>" + XMLTools.encodeXML(party.getZIP())
|
||||
+ "</ram:PostcodeCode>";
|
||||
}
|
||||
if (party.getStreet() != null) {
|
||||
xml += "<ram:LineOne>" + XMLTools.encodeXML(party.getStreet())
|
||||
+ "</ram:LineOne>";
|
||||
@@ -181,11 +184,16 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
xml += "<ram:LineThree>" + XMLTools.encodeXML(party.getAdditionalAddressExtension())
|
||||
+ "</ram:LineThree>";
|
||||
}
|
||||
if (party.getLocation() != null) {
|
||||
xml += "<ram:CityName>" + XMLTools.encodeXML(party.getLocation())
|
||||
+ "</ram:CityName>"
|
||||
+ "<ram:CountryID>" + XMLTools.encodeXML(party.getCountry())
|
||||
+ "</ram:CityName>";
|
||||
}
|
||||
;
|
||||
//country IS mandatory
|
||||
xml += "<ram:CountryID>" + XMLTools.encodeXML(party.getCountry())
|
||||
+ "</ram:CountryID>"
|
||||
+ "</ram:PostalTradeAddress>";
|
||||
|
||||
if ((party.getVATID() != null) && (!isShipToTradeParty)) {
|
||||
xml += "<ram:SpecifiedTaxRegistration>"
|
||||
+ "<ram:ID schemeID=\"VA\">" + XMLTools.encodeXML(party.getVATID())
|
||||
@@ -273,7 +281,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
paymentTermsDescription = XMLTools.encodeXML(trans.getPaymentTermDescription());
|
||||
}
|
||||
|
||||
if ((paymentTermsDescription == null) && (trans.getDocumentCode() != CORRECTEDINVOICE)/* && (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE)*/) {
|
||||
if ((paymentTermsDescription == null) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CORRECTEDINVOICE) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE)) {
|
||||
paymentTermsDescription = "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate());
|
||||
|
||||
}
|
||||
@@ -357,6 +365,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
}
|
||||
}
|
||||
final LineCalculator lc = new LineCalculator(currentItem);
|
||||
if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) {
|
||||
|
||||
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
|
||||
"<ram:AssociatedDocumentLineDocument>"
|
||||
+ "<ram:LineID>" + lineID + "</ram:LineID>"
|
||||
@@ -474,6 +484,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
}
|
||||
xml += "</ram:SpecifiedLineTradeSettlement>"
|
||||
+ "</ram:IncludedSupplyChainTradeLineItem>";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -534,24 +545,28 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
}
|
||||
xml += " </ram:SpecifiedProcuringProject>";
|
||||
}
|
||||
xml += "</ram:ApplicableHeaderTradeAgreement>"
|
||||
+ "<ram:ApplicableHeaderTradeDelivery>";
|
||||
xml += "</ram:ApplicableHeaderTradeAgreement>";
|
||||
xml += "<ram:ApplicableHeaderTradeDelivery>";
|
||||
|
||||
if (this.trans.getDeliveryAddress() != null) {
|
||||
xml += "<ram:ShipToTradeParty>" +
|
||||
getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) +
|
||||
"</ram:ShipToTradeParty>";
|
||||
}
|
||||
|
||||
xml += "<ram:ActualDeliverySupplyChainEvent>"
|
||||
+ "<ram:OccurrenceDateTime>";
|
||||
|
||||
if (trans.getDeliveryDate() != null) {
|
||||
xml += "<ram:ActualDeliverySupplyChainEvent>"
|
||||
+ "<ram:OccurrenceDateTime>";
|
||||
xml += DATE.udtFormat(trans.getDeliveryDate());
|
||||
} else {
|
||||
throw new IllegalStateException("No delivery date provided");
|
||||
}
|
||||
xml += "</ram:OccurrenceDateTime>";
|
||||
xml += " </ram:ActualDeliverySupplyChainEvent>";
|
||||
|
||||
} else {
|
||||
if (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE) {
|
||||
throw new IllegalStateException("No delivery date provided");
|
||||
}
|
||||
}
|
||||
/*
|
||||
* + "<DeliveryNoteReferencedDocument>" +
|
||||
* "<IssueDateTime format=\"102\">20130603</IssueDateTime>" +
|
||||
@@ -564,29 +579,37 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
xml += "</ram:DespatchAdviceReferencedDocument>";
|
||||
|
||||
}
|
||||
xml += " </ram:ApplicableHeaderTradeDelivery> <ram:ApplicableHeaderTradeSettlement>"
|
||||
+ " <ram:PaymentReference>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:PaymentReference>"
|
||||
+ " <ram:InvoiceCurrencyCode>" + trans.getCurrency() + "</ram:InvoiceCurrencyCode>";
|
||||
|
||||
xml += "</ram:ApplicableHeaderTradeDelivery>";
|
||||
xml += "<ram:ApplicableHeaderTradeSettlement>";
|
||||
if ((trans.getNumber() != null) && (getProfile() != Profiles.getByName("Minimum"))) {
|
||||
xml += " <ram:PaymentReference>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:PaymentReference>";
|
||||
}
|
||||
xml += " <ram:InvoiceCurrencyCode>" + trans.getCurrency() + "</ram:InvoiceCurrencyCode>";
|
||||
|
||||
if (trans.getTradeSettlementPayment() != null) {
|
||||
for (final IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) {
|
||||
if (payment != null) {
|
||||
hasDueDate = true;
|
||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||
xml += payment.getSettlementXML();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (trans.getTradeSettlement() != null) {
|
||||
for (final IZUGFeRDTradeSettlement payment : trans.getTradeSettlement()) {
|
||||
if (payment != null) {
|
||||
if (payment instanceof IZUGFeRDTradeSettlementPayment) {
|
||||
hasDueDate = true;
|
||||
}
|
||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||
xml += payment.getSettlementXML();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((trans.getDocumentCode() == CORRECTEDINVOICE)/*||(trans.getDocumentCode() == DocumentCodeTypeConstants.CREDITNOTE)*/) {
|
||||
}
|
||||
if ((trans.getDocumentCode() == DocumentCodeTypeConstants.CORRECTEDINVOICE) || (trans.getDocumentCode() == DocumentCodeTypeConstants.CREDITNOTE)) {
|
||||
hasDueDate = false;
|
||||
}
|
||||
|
||||
@@ -596,6 +619,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
if (amount != null) {
|
||||
final String amountCategoryCode = amount.getCategoryCode();
|
||||
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
|
||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||
|
||||
xml += "<ram:ApplicableTradeTax>"
|
||||
+ "<ram:CalculatedAmount>" + currencyFormat(amount.getCalculated())
|
||||
+ "</ram:CalculatedAmount>" //currencyID=\"EUR\"
|
||||
@@ -607,6 +632,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
+ vatFormat(currentTaxPercent) + "</ram:RateApplicablePercent></ram:ApplicableTradeTax>";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((trans.getDetailedDeliveryPeriodFrom() != null) || (trans.getDetailedDeliveryPeriodTo() != null)) {
|
||||
xml += "<ram:BillingSpecifiedPeriod>";
|
||||
if (trans.getDetailedDeliveryPeriodFrom() != null) {
|
||||
@@ -660,7 +686,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
}
|
||||
|
||||
|
||||
if ((trans.getPaymentTerms() == null) && ((paymentTermsDescription != null) || (trans.getTradeSettlement() != null) || (hasDueDate))) {
|
||||
if ((trans.getPaymentTerms() == null) && (getProfile() != Profiles.getByName("Minimum")) && ((paymentTermsDescription != null) || (trans.getTradeSettlement() != null) || (hasDueDate))) {
|
||||
xml += "<ram:SpecifiedTradePaymentTerms>";
|
||||
|
||||
if (paymentTermsDescription != null) {
|
||||
@@ -691,20 +717,24 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
||||
|
||||
final String chargesTotalLine = "<ram:ChargeTotalAmount>" + currencyFormat(calc.getChargesForPercent(null)) + "</ram:ChargeTotalAmount>";
|
||||
|
||||
xml += "<ram:SpecifiedTradeSettlementHeaderMonetarySummation>"
|
||||
+ "<ram:LineTotalAmount>" + currencyFormat(calc.getTotal()) + "</ram:LineTotalAmount>"
|
||||
+ chargesTotalLine
|
||||
+ allowanceTotalLine
|
||||
+ "<ram:TaxBasisTotalAmount>" + currencyFormat(calc.getTaxBasis()) + "</ram:TaxBasisTotalAmount>"
|
||||
xml += "<ram:SpecifiedTradeSettlementHeaderMonetarySummation>";
|
||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||
xml += "<ram:LineTotalAmount>" + currencyFormat(calc.getTotal()) + "</ram:LineTotalAmount>";
|
||||
xml += chargesTotalLine
|
||||
+ allowanceTotalLine;
|
||||
}
|
||||
xml += "<ram:TaxBasisTotalAmount>" + currencyFormat(calc.getTaxBasis()) + "</ram:TaxBasisTotalAmount>"
|
||||
// //
|
||||
// currencyID=\"EUR\"
|
||||
+ "<ram:TaxTotalAmount currencyID=\"" + trans.getCurrency() + "\">"
|
||||
+ currencyFormat(calc.getGrandTotal().subtract(calc.getTaxBasis())) + "</ram:TaxTotalAmount>"
|
||||
+ "<ram:GrandTotalAmount>" + currencyFormat(calc.getGrandTotal()) + "</ram:GrandTotalAmount>"
|
||||
+ "<ram:GrandTotalAmount>" + currencyFormat(calc.getGrandTotal()) + "</ram:GrandTotalAmount>";
|
||||
// //
|
||||
// currencyID=\"EUR\"
|
||||
+ " <ram:TotalPrepaidAmount>" + currencyFormat(calc.getTotalPrepaid()) + "</ram:TotalPrepaidAmount>"
|
||||
+ "<ram:DuePayableAmount>" + currencyFormat(calc.getGrandTotal().subtract(calc.getTotalPrepaid())) + "</ram:DuePayableAmount>"
|
||||
if (getProfile() != Profiles.getByName("Minimum")) {
|
||||
xml+=" <ram:TotalPrepaidAmount>" + currencyFormat(calc.getTotalPrepaid()) + "</ram:TotalPrepaidAmount>";
|
||||
}
|
||||
xml+= "<ram:DuePayableAmount>" + currencyFormat(calc.getGrandTotal().subtract(calc.getTotalPrepaid())) + "</ram:DuePayableAmount>"
|
||||
+ "</ram:SpecifiedTradeSettlementHeaderMonetarySummation>";
|
||||
if (trans.getInvoiceReferencedDocumentID() != null) {
|
||||
xml += " <ram:InvoiceReferencedDocument>"
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* *********************************************************************
|
||||
* <p>
|
||||
* Copyright 2019 Jochen Staerk
|
||||
* <p>
|
||||
* Use is subject to license terms.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* <p>
|
||||
* **********************************************************************
|
||||
*/
|
||||
package org.mustangproject.ZUGFeRD;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.mustangproject.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/***
|
||||
* This is a test to confirm the minimum steps to implement a interface are still sufficient
|
||||
*
|
||||
* @author jstaerk
|
||||
*
|
||||
*/
|
||||
public class ProfilesMinimumBasicWLTest extends TestCase {
|
||||
|
||||
final String TARGET_PDF_FX_MINIMUM = "./target/testout-Minimum.pdf";
|
||||
|
||||
public void testMinimumExport() {
|
||||
String ownNumber = "NUMFACTURE";
|
||||
String ownBIC = "COBADEFFXXX";
|
||||
String ownIBAN = "DE88200800000970375700";
|
||||
String ownOrgName = "ME";
|
||||
|
||||
// the writing part
|
||||
try (InputStream SOURCE_PDF = this.getClass()
|
||||
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
|
||||
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setZUGFeRDVersion(2).setProfile("Minimum").load(SOURCE_PDF)) {
|
||||
//https://stackoverflow.com/questions/72450066/creating-a-min-basic-and-basic-wl-factur-x-using-mustang
|
||||
Invoice i = new Invoice()
|
||||
.setIssueDate(new Date())
|
||||
/* .setDueDate(new Date())
|
||||
.setDetailedDeliveryPeriod(new Date(), new Date())
|
||||
.setDeliveryDate(new Date())*/
|
||||
.setSender(
|
||||
new TradeParty().setName(ownOrgName).setCountry("FR")
|
||||
.addBankDetails(new BankDetails(ownIBAN, ownBIC)))
|
||||
.setRecipient(
|
||||
new TradeParty().setName("Client").setCountry("FR"))
|
||||
.setNumber(ownNumber)
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(123), new BigDecimal(1)))
|
||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(123), new BigDecimal(1)))
|
||||
.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);
|
||||
|
||||
// check for pdf-a schema extension
|
||||
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
|
||||
// assertFalse(pdfContent.indexOf("<pdfaSchema:prefix>zf</pdfaSchema:prefix>") == -1);
|
||||
// assertFalse(pdfContent.indexOf("urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#") == -1);
|
||||
|
||||
} catch (IOException e) {
|
||||
fail("IOException should not happen in testZExport");
|
||||
}
|
||||
|
||||
// now check the contents (like MustangReaderTest)
|
||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM);
|
||||
|
||||
// Reading ZUGFeRD
|
||||
assertEquals(zi.getAmount(), "439.11");
|
||||
// assertEquals(zi.getBIC(), ownBIC);
|
||||
// assertEquals(zi.getIBAN(), ownIBAN);
|
||||
assertEquals(zi.getHolder(), ownOrgName);
|
||||
// assertEquals(zi.getForeignReference(), ownNumber);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -248,9 +248,29 @@ public class LibraryTest extends ResourceCase {
|
||||
assertThat(res).valueByXPath("count(//notice)")
|
||||
.asInt()
|
||||
.isEqualTo(0);
|
||||
}
|
||||
|
||||
public void testMinimumProfileValidity() {
|
||||
File tempFile = new File("../library/target/testout-Minimum.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user