working on cash discount abstraction (XR and reading is still missing)
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
- UBL importer to also parse contacts
|
- UBL importer to also parse contacts
|
||||||
- https://github.com/ZUGFeRD/mustangproject/pull/369
|
- https://github.com/ZUGFeRD/mustangproject/pull/369
|
||||||
- upgrade ph-schematron from 6.3.3 to 8
|
- upgrade ph-schematron from 6.3.3 to 8
|
||||||
|
- support inputstreams https://github.com/ZUGFeRD/mustangproject/pull/379
|
||||||
|
- #314 ZUGFeRDInvoiceImporter additional constructur
|
||||||
|
|
||||||
2.10.0
|
2.10.0
|
||||||
=======
|
=======
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
## General approach
|
## Typical process
|
||||||
|
|
||||||
1. build
|
1. build
|
||||||
|
|
||||||
@@ -29,6 +29,18 @@ If you do a pull request, please do a feature branch, e.g. if you are working on
|
|||||||
Most of mustang is a library, adding (autmated junit) test cases is often not only the most sustainable but also the fastest way to see if new/changed functionality works. If something is changed so that old test cases break on purpose please do not just remove them but take the time to fix the test cases
|
Most of mustang is a library, adding (autmated junit) test cases is often not only the most sustainable but also the fastest way to see if new/changed functionality works. If something is changed so that old test cases break on purpose please do not just remove them but take the time to fix the test cases
|
||||||
|
|
||||||
|
|
||||||
|
## Typical workflow
|
||||||
|
|
||||||
|
If e.g. new elements or attributes are added, they are often added
|
||||||
|
* in the object so that a developer can use them
|
||||||
|
* in the interface so that a old fashioned developer could use them as well
|
||||||
|
* in the pullprovider so that it actually finds it's way into the XML
|
||||||
|
* in at least one test, after the test has been run this should at least once be
|
||||||
|
* validated. If that works one can start implementing the
|
||||||
|
* reading part (along with tests), then it needs to be
|
||||||
|
* documented e.g. on the homepage and
|
||||||
|
* communicated, at the very least by mentioning it in the history.md
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
Mustang contains a library to read/write e-invoices,
|
Mustang contains a library to read/write e-invoices,
|
||||||
|
|||||||
50
library/src/main/java/org/mustangproject/CashDiscount.java
Normal file
50
library/src/main/java/org/mustangproject/CashDiscount.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package org.mustangproject;
|
||||||
|
|
||||||
|
import org.mustangproject.ZUGFeRD.IZUGFeRDCashDiscount;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class CashDiscount implements IZUGFeRDCashDiscount {
|
||||||
|
|
||||||
|
protected BigDecimal percent;
|
||||||
|
protected Integer days=null;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a cash discount (skonto) with the specified height in the specified period.
|
||||||
|
* Should someone add more period types than just "days" there
|
||||||
|
* is be space for a (optional) third parameter
|
||||||
|
*
|
||||||
|
* @param percent max 3 decimals "behind the dot", more precision is currently ignored
|
||||||
|
* @param days
|
||||||
|
*/
|
||||||
|
public CashDiscount(BigDecimal percent, int days) {
|
||||||
|
this.percent = percent;
|
||||||
|
this.days = days;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @return this particular cash discount as cross industry invoice XML
|
||||||
|
*/
|
||||||
|
public String getAsCII() {
|
||||||
|
return "<ram:SpecifiedTradePaymentTerms>"+
|
||||||
|
"<ram:Description>Cash Discount</ram:Description>"+
|
||||||
|
" <ram:ApplicableTradePaymentDiscountTerms>"+
|
||||||
|
" <ram:BasisPeriodMeasure unitCode=\"DAY\">"+days+"</ram:BasisPeriodMeasure>"+
|
||||||
|
" <ram:CalculationPercent>"+XMLTools.nDigitFormat(percent,3)+"</ram:CalculationPercent>"+
|
||||||
|
" </ram:ApplicableTradePaymentDiscountTerms>"+
|
||||||
|
"</ram:SpecifiedTradePaymentTerms>";
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* since EN16931 voted not to have (or even allow) cash discounts in their core invoice the german
|
||||||
|
* XRechnung CIUS defined it's own proprietary format for a freetext field
|
||||||
|
* @return this particular cash discount in proprietary xrechnung format
|
||||||
|
*/
|
||||||
|
public String getAsXRechnung() {
|
||||||
|
return "#SKONTO#TAGE="+days+"#PROZENT="+XMLTools.nDigitFormat(percent,3)+"#\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,12 +26,7 @@ import java.util.Collection;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mustangproject.ZUGFeRD.IExportableTransaction;
|
import org.mustangproject.ZUGFeRD.*;
|
||||||
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
|
|
||||||
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
|
|
||||||
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
|
|
||||||
import org.mustangproject.ZUGFeRD.IZUGFeRDPaymentTerms;
|
|
||||||
import org.mustangproject.ZUGFeRD.IZUGFeRDTradeSettlement;
|
|
||||||
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
|
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
@@ -47,13 +42,14 @@ public class Invoice implements IExportableTransaction {
|
|||||||
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
|
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
|
||||||
protected Date issueDate = null, dueDate = null, deliveryDate = null;
|
protected Date issueDate = null, dueDate = null, deliveryDate = null;
|
||||||
protected TradeParty sender = null, recipient = null, deliveryAddress = null;
|
protected TradeParty sender = null, recipient = null, deliveryAddress = null;
|
||||||
@JsonDeserialize(contentAs=Item.class)
|
protected ArrayList<CashDiscount> cashDiscounts = null;
|
||||||
|
@JsonDeserialize(contentAs = Item.class)
|
||||||
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
|
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
|
||||||
protected ArrayList<String> notes = null;
|
protected ArrayList<String> notes = null;
|
||||||
private List<IncludedNote> includedNotes = null;
|
private List<IncludedNote> includedNotes = null;
|
||||||
protected String sellerOrderReferencedDocumentID;
|
protected String sellerOrderReferencedDocumentID;
|
||||||
protected String contractReferencedDocument = null;
|
protected String contractReferencedDocument = null;
|
||||||
protected ArrayList<FileAttachment> xmlEmbeddedFiles=null;
|
protected ArrayList<FileAttachment> xmlEmbeddedFiles = null;
|
||||||
|
|
||||||
protected BigDecimal totalPrepaidAmount = null;
|
protected BigDecimal totalPrepaidAmount = null;
|
||||||
protected Date detailedDeliveryDateStart = null;
|
protected Date detailedDeliveryDateStart = null;
|
||||||
@@ -70,6 +66,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
|
|
||||||
public Invoice() {
|
public Invoice() {
|
||||||
ZFItems = new ArrayList<>();
|
ZFItems = new ArrayList<>();
|
||||||
|
cashDiscounts = new ArrayList<>();
|
||||||
setCurrency("EUR");
|
setCurrency("EUR");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +97,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
|
|
||||||
public Invoice embedFileInXML(FileAttachment fa) {
|
public Invoice embedFileInXML(FileAttachment fa) {
|
||||||
if (xmlEmbeddedFiles == null) {
|
if (xmlEmbeddedFiles == null) {
|
||||||
xmlEmbeddedFiles= new ArrayList<>();
|
xmlEmbeddedFiles = new ArrayList<>();
|
||||||
}
|
}
|
||||||
xmlEmbeddedFiles.add(fa);
|
xmlEmbeddedFiles.add(fa);
|
||||||
return this;
|
return this;
|
||||||
@@ -115,6 +112,10 @@ public class Invoice implements IExportableTransaction {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IZUGFeRDCashDiscount[] getCashDiscounts() {
|
||||||
|
return cashDiscounts.toArray(new IZUGFeRDCashDiscount[0]);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNumber() {
|
public String getNumber() {
|
||||||
@@ -139,6 +140,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE;
|
documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Invoice setCreditNote() {
|
public Invoice setCreditNote() {
|
||||||
documentCode = DocumentCodeTypeConstants.CREDITNOTE;
|
documentCode = DocumentCodeTypeConstants.CREDITNOTE;
|
||||||
return this;
|
return this;
|
||||||
@@ -229,6 +231,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
public String getBuyerOrderReferencedDocumentID() {
|
public String getBuyerOrderReferencedDocumentID() {
|
||||||
return buyerOrderReferencedDocumentID;
|
return buyerOrderReferencedDocumentID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSellerOrderReferencedDocumentID() {
|
public String getSellerOrderReferencedDocumentID() {
|
||||||
return sellerOrderReferencedDocumentID;
|
return sellerOrderReferencedDocumentID;
|
||||||
@@ -239,6 +242,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID;
|
this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* usually the order number
|
* usually the order number
|
||||||
* @param buyerOrderReferencedDocumentID string with number
|
* @param buyerOrderReferencedDocumentID string with number
|
||||||
@@ -258,6 +262,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
this.invoiceReferencedDocumentID = invoiceReferencedDocumentID;
|
this.invoiceReferencedDocumentID = invoiceReferencedDocumentID;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getInvoiceReferencedDocumentID() {
|
public String getInvoiceReferencedDocumentID() {
|
||||||
return invoiceReferencedDocumentID;
|
return invoiceReferencedDocumentID;
|
||||||
@@ -285,7 +290,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
public Invoice setTotalPrepaidAmount(BigDecimal prepaid) {
|
public Invoice setTotalPrepaidAmount(BigDecimal prepaid) {
|
||||||
totalPrepaidAmount=prepaid;
|
totalPrepaidAmount = prepaid;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,6 +485,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* required.
|
* required.
|
||||||
* sets the invoice receiving institution = invoicee
|
* sets the invoice receiving institution = invoicee
|
||||||
|
*
|
||||||
* @param recipient the invoicee organisation
|
* @param recipient the invoicee organisation
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -491,6 +497,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* required.
|
* required.
|
||||||
* sets the invoicing institution = invoicer
|
* sets the invoicing institution = invoicer
|
||||||
|
*
|
||||||
* @param sender the invoicer
|
* @param sender the invoicer
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -569,6 +576,16 @@ public class Invoice implements IExportableTransaction {
|
|||||||
this.deliveryAddress = deliveryAddress;
|
this.deliveryAddress = deliveryAddress;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/***
|
||||||
|
* Adds a cash discount (skonto)
|
||||||
|
* @param CashDiscount the percent/period combination
|
||||||
|
* @return fluent setter
|
||||||
|
*/
|
||||||
|
public Invoice addCashDiscount(CashDiscount c) {
|
||||||
|
this.cashDiscounts.add(c);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IZUGFeRDExportableItem[] getZFItems() {
|
public IZUGFeRDExportableItem[] getZFItems() {
|
||||||
@@ -576,15 +593,16 @@ public class Invoice implements IExportableTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) {
|
public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) {
|
||||||
ZFItems=ims;
|
ZFItems = ims;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* required
|
* required
|
||||||
* adds invoice "lines" :-)
|
* adds invoice "lines" :-)
|
||||||
* @see Item
|
*
|
||||||
* @param item the invoice line
|
* @param item the invoice line
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
|
* @see Item
|
||||||
*/
|
*/
|
||||||
public Invoice addItem(IZUGFeRDExportableItem item) {
|
public Invoice addItem(IZUGFeRDExportableItem item) {
|
||||||
ZFItems.add(item);
|
ZFItems.add(item);
|
||||||
@@ -669,6 +687,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element
|
* adds a free text paragraph, which will become an includedNote element
|
||||||
|
*
|
||||||
* @param text freeform UTF8 plain text
|
* @param text freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -694,6 +713,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#AAI}
|
* subjectCode {@link SubjectCode#AAI}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -708,6 +728,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#REG}
|
* subjectCode {@link SubjectCode#REG}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -722,6 +743,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#ABL}
|
* subjectCode {@link SubjectCode#ABL}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -736,6 +758,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#CUS}
|
* subjectCode {@link SubjectCode#CUS}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -750,6 +773,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#SUR}
|
* subjectCode {@link SubjectCode#SUR}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -764,6 +788,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#TXD}
|
* subjectCode {@link SubjectCode#TXD}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -778,6 +803,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#ACY}
|
* subjectCode {@link SubjectCode#ACY}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -788,9 +814,11 @@ public class Invoice implements IExportableTransaction {
|
|||||||
includedNotes.add(IncludedNote.introductionNote(content));
|
includedNotes.add(IncludedNote.introductionNote(content));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adds a free text paragraph, which will become an includedNote element with explicit
|
* adds a free text paragraph, which will become an includedNote element with explicit
|
||||||
* subjectCode {@link SubjectCode#AAK}
|
* subjectCode {@link SubjectCode#AAK}
|
||||||
|
*
|
||||||
* @param content freeform UTF8 plain text
|
* @param content freeform UTF8 plain text
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -841,6 +869,7 @@ public class Invoice implements IExportableTransaction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide when the VAT should be collected.
|
* Decide when the VAT should be collected.
|
||||||
|
*
|
||||||
* @param vatDueDateTypeCode use EventTimeCodeTypeConstants
|
* @param vatDueDateTypeCode use EventTimeCodeTypeConstants
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -120,36 +120,6 @@ public class XMLTools extends XMLWriter {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Byte Order Mark size and thus allows to skips over a BOM
|
|
||||||
* at the beginning of the given ByteArrayInputStream, if one exists.
|
|
||||||
*
|
|
||||||
* @param is the ByteArrayInputStream used
|
|
||||||
* @throws IOException if can not be read from is
|
|
||||||
* @see <a href="https://www.w3.org/TR/xml/#sec-guessing">Autodetection of Character Encodings</a>
|
|
||||||
*
|
|
||||||
public static int guessBOMSize(ByteArrayInputStream is) throws IOException {
|
|
||||||
byte[] pad = new byte[4];
|
|
||||||
is.read(pad);
|
|
||||||
is.reset();
|
|
||||||
int test2 = ((pad[0] & 0xFF) << 8) | (pad[1] & 0xFF);
|
|
||||||
int test3 = ((test2 & 0xFFFF) << 8) | (pad[2] & 0xFF);
|
|
||||||
int test4 = ((test3 & 0xFFFFFF) << 8) | (pad[3] & 0xFF);
|
|
||||||
//
|
|
||||||
if (test4 == 0x0000FEFF || test4 == 0xFFFE0000 || test4 == 0x0000FFFE || test4 == 0xFEFF0000) {
|
|
||||||
// UCS-4: BOM takes 4 bytes
|
|
||||||
return 4;
|
|
||||||
} else if (test3 == 0xEFBBFF) {
|
|
||||||
// UTF-8: BOM takes 3 bytes
|
|
||||||
return 3;
|
|
||||||
} else if (test2 == 0xFEFF || test2 == 0xFFFE) {
|
|
||||||
// UTF-16: BOM takes 2 bytes
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* removes utf8 byte order marks from byte arrays, in case one is there
|
* removes utf8 byte order marks from byte arrays, in case one is there
|
||||||
* @param zugferdRaw the CII XML
|
* @param zugferdRaw the CII XML
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ public interface IExportableTransaction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default IZUGFeRDCashDiscount[] getCashDiscounts() { return null; }
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @return the invoice line items with the positions
|
||||||
|
*/
|
||||||
IZUGFeRDExportableItem[] getZFItems();
|
IZUGFeRDExportableItem[] getZFItems();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
|
import org.mustangproject.XMLTools;
|
||||||
|
|
||||||
|
public interface IZUGFeRDCashDiscount {
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* @return this particular cash discount as cross industry invoice XML
|
||||||
|
*/
|
||||||
|
public String getAsCII();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* since EN16931 voted not to have (or even allow) cash discounts in their core invoice the german
|
||||||
|
* XRechnung CIUS defined it's own proprietary format for a freetext field
|
||||||
|
* @return this particular cash discount in proprietary xrechnung format
|
||||||
|
*/
|
||||||
|
public String getAsXRechnung();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -689,6 +689,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
|
|||||||
} else {
|
} else {
|
||||||
xml += buildPaymentTermsXml();
|
xml += buildPaymentTermsXml();
|
||||||
}
|
}
|
||||||
|
if ((profile == Profiles.getByName("Extended"))&&(trans.getCashDiscounts()!=null)&&(trans.getCashDiscounts().length>0)) {
|
||||||
|
for (IZUGFeRDCashDiscount discount:trans.getCashDiscounts()
|
||||||
|
) {
|
||||||
|
xml += discount.getAsCII();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
final String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowancesForPercent(null)) + "</ram:AllowanceTotalAmount>";
|
final String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowancesForPercent(null)) + "</ram:AllowanceTotalAmount>";
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
|
|
||||||
ze.export(TARGET_PDF);
|
ze.export(TARGET_PDF);
|
||||||
} catch (IOException | ParseException e) {
|
} catch (IOException | ParseException e) {
|
||||||
fail("Exception should not be raised in testPushExport");
|
fail("Exception should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -142,7 +142,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_ATTACHMENTSPDF);
|
ze.export(TARGET_ATTACHMENTSPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -192,11 +192,11 @@ public class ZF2PushTest extends TestCase {
|
|||||||
//assertEquals(IBAN,read.getSender().getBankDetails().get(0).getIBAN());
|
//assertEquals(IBAN,read.getSender().getBankDetails().get(0).getIBAN());
|
||||||
ze.export(TARGET_BANKPDF);
|
ze.export(TARGET_BANKPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
} catch (XPathExpressionException e) {
|
} catch (XPathExpressionException e) {
|
||||||
fail("XPathException should not be raised in testEdgeExport");
|
fail("XPathException should not be raised");
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
fail("ParseException should not be raised in testEdgeExport");
|
fail("ParseException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_ITEMCHARGESALLOWANCESPDF);
|
ze.export(TARGET_ITEMCHARGESALLOWANCESPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -284,7 +284,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF);
|
ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -333,7 +333,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_REVERSECHARGEPDF);
|
ze.export(TARGET_REVERSECHARGEPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -379,7 +379,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_CHARGESALLOWANCESPDF);
|
ze.export(TARGET_CHARGESALLOWANCESPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -417,7 +417,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
|
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
|
||||||
|
|
||||||
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
|
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
|
||||||
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors()
|
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("extended")).ignorePDFAErrors()
|
||||||
.load(SOURCE_PDF)) {
|
.load(SOURCE_PDF)) {
|
||||||
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
@@ -435,6 +435,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
|
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
|
||||||
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
|
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
|
||||||
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
|
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
|
||||||
|
.addCashDiscount(new CashDiscount(new BigDecimal(2),14))
|
||||||
.setDeliveryDate(sdf.parse("2020-11-02")).setOwnVATID("DE0815").setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
|
.setDeliveryDate(sdf.parse("2020-11-02")).setOwnVATID("DE0815").setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
|
||||||
);
|
);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
@@ -445,7 +446,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_PUSHEDGE);
|
ze.export(TARGET_PUSHEDGE);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -473,7 +474,8 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(zi.getUTF8().contains("DE4711")); // the VAT ID should be there...
|
assertTrue(zi.getUTF8().contains("DE4711")); // the VAT ID should be there...
|
||||||
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
|
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
|
||||||
assertTrue(zi.getUTF8().contains("document level 2/2"));
|
assertTrue(zi.getUTF8().contains("document level 2/2"));
|
||||||
assertFalse(zi.getUTF8().contains("++49555123456")); // in profile EN16931 contact fax number is not allowed
|
assertTrue(zi.getUTF8().contains("++49555123456"));
|
||||||
|
assertTrue(zi.getUTF8().contains("Cash Discount")); // default description for cash discounts
|
||||||
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString()
|
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString()
|
||||||
.isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE);
|
.isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE);
|
||||||
|
|
||||||
@@ -486,9 +488,9 @@ public class ZF2PushTest extends TestCase {
|
|||||||
|
|
||||||
|
|
||||||
} catch (XPathExpressionException e) {
|
} catch (XPathExpressionException e) {
|
||||||
fail("XPathExpressionException should not be raised in testEdgeExport");
|
fail("XPathExpressionException should not be raised");
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
fail("ParseException should not be raised in testEdgeExport");
|
fail("ParseException should not be raised");
|
||||||
/* a parseException would also be fired if the calculated grand total does not
|
/* a parseException would also be fired if the calculated grand total does not
|
||||||
match the read grand total */
|
match the read grand total */
|
||||||
}
|
}
|
||||||
@@ -518,7 +520,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_ALLOWANCESPDF);
|
ze.export(TARGET_ALLOWANCESPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -563,7 +565,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_RELATIVECHARGESALLOWANCESPDF);
|
ze.export(TARGET_RELATIVECHARGESALLOWANCESPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -613,7 +615,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_CORRECTIONPDF);
|
ze.export(TARGET_CORRECTIONPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
@@ -662,7 +664,7 @@ public class ZF2PushTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
ze.export(TARGET_CREDITNOTEPDF);
|
ze.export(TARGET_CREDITNOTEPDF);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
fail("IOException should not be raised in testEdgeExport");
|
fail("IOException should not be raised");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now check the contents (like MustangReaderTest)
|
// now check the contents (like MustangReaderTest)
|
||||||
|
|||||||
Reference in New Issue
Block a user