working on cash discount abstraction (XR and reading is still missing)

This commit is contained in:
jstaerk
2024-05-09 08:59:20 +02:00
parent e8bbc6c008
commit 757910fff8
9 changed files with 309 additions and 213 deletions

View 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";
}
}

View File

@@ -26,12 +26,7 @@ import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.mustangproject.ZUGFeRD.IExportableTransaction;
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.*;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -47,20 +42,21 @@ 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 Date issueDate = null, dueDate = null, deliveryDate = 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<String> notes = null;
private List<IncludedNote> includedNotes = null;
protected String sellerOrderReferencedDocumentID;
private List<IncludedNote> includedNotes = null;
protected String sellerOrderReferencedDocumentID;
protected String contractReferencedDocument = null;
protected ArrayList<FileAttachment> xmlEmbeddedFiles=null;
protected ArrayList<FileAttachment> xmlEmbeddedFiles = null;
protected BigDecimal totalPrepaidAmount = null;
protected Date detailedDeliveryDateStart = null;
protected Date detailedDeliveryPeriodEnd = null;
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(),
Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>();
Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>();
protected IZUGFeRDPaymentTerms paymentTerms = null;
protected Date invoiceReferencedIssueDate;
protected String specifiedProcuringProjectID = null;
@@ -70,6 +66,7 @@ public class Invoice implements IExportableTransaction {
public Invoice() {
ZFItems = new ArrayList<>();
cashDiscounts = new ArrayList<>();
setCurrency("EUR");
}
@@ -79,7 +76,7 @@ public class Invoice implements IExportableTransaction {
}
@Override
public String getContractReferencedDocument() {
public String getContractReferencedDocument() {
return contractReferencedDocument;
}
@@ -100,14 +97,14 @@ public class Invoice implements IExportableTransaction {
public Invoice embedFileInXML(FileAttachment fa) {
if (xmlEmbeddedFiles == null) {
xmlEmbeddedFiles= new ArrayList<>();
xmlEmbeddedFiles = new ArrayList<>();
}
xmlEmbeddedFiles.add(fa);
return this;
}
@Override
public FileAttachment[] getAdditionalReferencedDocuments() {
public FileAttachment[] getAdditionalReferencedDocuments() {
if (xmlEmbeddedFiles == null) {
return null;
}
@@ -115,6 +112,10 @@ public class Invoice implements IExportableTransaction {
}
@Override
public IZUGFeRDCashDiscount[] getCashDiscounts() {
return cashDiscounts.toArray(new IZUGFeRDCashDiscount[0]);
}
@Override
public String getNumber() {
@@ -139,6 +140,7 @@ public class Invoice implements IExportableTransaction {
documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE;
return this;
}
public Invoice setCreditNote() {
documentCode = DocumentCodeTypeConstants.CREDITNOTE;
return this;
@@ -229,16 +231,18 @@ public class Invoice implements IExportableTransaction {
public String getBuyerOrderReferencedDocumentID() {
return buyerOrderReferencedDocumentID;
}
@Override
public String getSellerOrderReferencedDocumentID() {
return sellerOrderReferencedDocumentID;
}
public Invoice setSellerOrderReferencedDocumentID(String sellerOrderReferencedDocumentID) {
this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID;
return this;
}
public Invoice setSellerOrderReferencedDocumentID(String sellerOrderReferencedDocumentID) {
this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID;
return this;
}
/***
* usually the order number
* @param buyerOrderReferencedDocumentID string with number
@@ -258,22 +262,23 @@ public class Invoice implements IExportableTransaction {
this.invoiceReferencedDocumentID = invoiceReferencedDocumentID;
return this;
}
@Override
public String getInvoiceReferencedDocumentID() {
return invoiceReferencedDocumentID;
}
@Override
public Date getInvoiceReferencedIssueDate() {
return invoiceReferencedIssueDate;
}
@Override
public Date getInvoiceReferencedIssueDate() {
return invoiceReferencedIssueDate;
}
public Invoice setInvoiceReferencedIssueDate(Date issueDate) {
this.invoiceReferencedIssueDate = issueDate;
return this;
}
@Override
public Invoice setInvoiceReferencedIssueDate(Date issueDate) {
this.invoiceReferencedIssueDate = issueDate;
return this;
}
@Override
public String getBuyerOrderReferencedDocumentIssueDateTime() {
return buyerOrderReferencedDocumentIssueDateTime;
}
@@ -285,7 +290,7 @@ public class Invoice implements IExportableTransaction {
* @return fluent setter
*/
public Invoice setTotalPrepaidAmount(BigDecimal prepaid) {
totalPrepaidAmount=prepaid;
totalPrepaidAmount = prepaid;
return this;
}
@@ -380,13 +385,13 @@ public class Invoice implements IExportableTransaction {
@Override
public String getOwnLocation() {
public String getOwnLocation() {
return sender.getLocation();
}
@Override
public String getOwnCountry() {
public String getOwnCountry() {
return sender.getCountry();
}
@@ -399,12 +404,12 @@ public class Invoice implements IExportableTransaction {
return notes.toArray(new String[0]);
}
@Override
public List<IncludedNote> getNotesWithSubjectCode() {
return includedNotes;
}
@Override
public List<IncludedNote> getNotesWithSubjectCode() {
return includedNotes;
}
@Override
@Override
public String getCurrency() {
return currency;
}
@@ -473,13 +478,14 @@ public class Invoice implements IExportableTransaction {
}
@Override
public TradeParty getRecipient() {
public TradeParty getRecipient() {
return recipient;
}
/**
* required.
* sets the invoice receiving institution = invoicee
*
* @param recipient the invoicee organisation
* @return fluent setter
*/
@@ -491,6 +497,7 @@ public class Invoice implements IExportableTransaction {
/**
* required.
* sets the invoicing institution = invoicer
*
* @param sender the invoicer
* @return fluent setter
*/
@@ -508,8 +515,8 @@ public class Invoice implements IExportableTransaction {
if (Allowances.isEmpty()) {
return null;
} else {
return Allowances.toArray(new IZUGFeRDAllowanceCharge[0]);
}
return Allowances.toArray(new IZUGFeRDAllowanceCharge[0]);
}
}
@@ -518,8 +525,8 @@ public class Invoice implements IExportableTransaction {
if (Charges.isEmpty()) {
return null;
} else {
return Charges.toArray(new IZUGFeRDAllowanceCharge[0]);
}
return Charges.toArray(new IZUGFeRDAllowanceCharge[0]);
}
}
@@ -528,8 +535,8 @@ public class Invoice implements IExportableTransaction {
if (LogisticsServiceCharges.isEmpty()) {
return null;
} else {
return LogisticsServiceCharges.toArray(new IZUGFeRDAllowanceCharge[0]);
}
return LogisticsServiceCharges.toArray(new IZUGFeRDAllowanceCharge[0]);
}
}
@@ -569,22 +576,33 @@ public class Invoice implements IExportableTransaction {
this.deliveryAddress = deliveryAddress;
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
public IZUGFeRDExportableItem[] getZFItems() {
return ZFItems.toArray(new IZUGFeRDExportableItem[0]);
}
public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) {
ZFItems=ims;
public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) {
ZFItems = ims;
}
/**
* required
* adds invoice "lines" :-)
* @see Item
*
* @param item the invoice line
* @return fluent setter
* @see Item
*/
public Invoice addItem(IZUGFeRDExportableItem item) {
ZFItems.add(item);
@@ -669,6 +687,7 @@ public class Invoice implements IExportableTransaction {
/**
* adds a free text paragraph, which will become an includedNote element
*
* @param text freeform UTF8 plain text
* @return fluent setter
*/
@@ -679,129 +698,138 @@ public class Invoice implements IExportableTransaction {
notes.add(text);
return this;
}
public Invoice addNotes(Collection<IncludedNote> notes) {
if (notes == null) {
return this;
}
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.addAll(notes);
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAI}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addGeneralNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.generalNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#REG}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addRegulatoryNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.regulatoryNote(content));
return this;
}
public Invoice addNotes(Collection<IncludedNote> notes) {
if (notes == null) {
return this;
}
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.addAll(notes);
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ABL}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addLegalNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.legalNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAI}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addGeneralNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.generalNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#CUS}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addCustomsNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.customsNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#REG}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addRegulatoryNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.regulatoryNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#SUR}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addSellerNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.sellerNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ABL}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addLegalNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.legalNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#CUS}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addCustomsNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.customsNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#SUR}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addSellerNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.sellerNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#TXD}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addTaxNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.taxNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ACY}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addIntroductionNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.introductionNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAK}
*
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addDiscountBonusNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.discountBonusNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#TXD}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addTaxNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.taxNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ACY}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addIntroductionNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.introductionNote(content));
return this;
}
/**
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAK}
* @param content freeform UTF8 plain text
* @return fluent setter
*/
public Invoice addDiscountBonusNote(String content) {
if (includedNotes == null) {
includedNotes = new ArrayList<>();
}
includedNotes.add(IncludedNote.discountBonusNote(content));
return this;
}
@Override
public String getSpecifiedProcuringProjectID() {
return specifiedProcuringProjectID;
@@ -841,6 +869,7 @@ public class Invoice implements IExportableTransaction {
/**
* Decide when the VAT should be collected.
*
* @param vatDueDateTypeCode use EventTimeCodeTypeConstants
* @return fluent setter
*/

View File

@@ -120,36 +120,6 @@ public class XMLTools extends XMLWriter {
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
* @param zugferdRaw the CII XML

View File

@@ -140,6 +140,11 @@ public interface IExportableTransaction {
return null;
}
default IZUGFeRDCashDiscount[] getCashDiscounts() { return null; }
/***
* @return the invoice line items with the positions
*/
IZUGFeRDExportableItem[] getZFItems();
/**

View File

@@ -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();
}

View File

@@ -689,6 +689,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} else {
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>";