write support for referenceddocument

This commit is contained in:
jstaerk
2021-10-21 13:58:10 +02:00
parent d0e3fb1876
commit cfee4b3c31
6 changed files with 184 additions and 73 deletions

View File

@@ -1,5 +1,6 @@
package org.mustangproject; package org.mustangproject;
import org.mustangproject.ZUGFeRD.IReferencedDocument;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
@@ -17,6 +18,7 @@ public class Item implements IZUGFeRDExportableItem {
protected String referencedLineID=null; protected String referencedLineID=null;
protected Product product; protected Product product;
protected ArrayList<String> notes = null; protected ArrayList<String> notes = null;
protected ArrayList<ReferencedDocument> referencedDocuments = null;
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<IZUGFeRDAllowanceCharge>(), protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<IZUGFeRDAllowanceCharge>(),
Charges = new ArrayList<IZUGFeRDAllowanceCharge>(); Charges = new ArrayList<IZUGFeRDAllowanceCharge>();
@@ -189,6 +191,26 @@ public class Item implements IZUGFeRDExportableItem {
return this; return this;
} }
/***
* adds item level Referenced documents along with their typecodes and issuerassignedIDs
* @param doc the ReferencedDocument to add
* @return fluent setter
*/
public Item addReferencedDocument(ReferencedDocument doc) {
if (referencedDocuments==null) {
referencedDocuments=new ArrayList<ReferencedDocument>();
}
referencedDocuments.add(doc);
return this;
}
@Override
public IReferencedDocument[] getReferencedDocuments() {
if (referencedDocuments==null) {
return null;
}
return referencedDocuments.toArray(new IReferencedDocument[0]);
}
/*** /***
* specify a item level delivery period * specify a item level delivery period
* (apart from the document level delivery period, and the document level * (apart from the document level delivery period, and the document level

View File

@@ -0,0 +1,38 @@
package org.mustangproject;
import org.mustangproject.ZUGFeRD.IReferencedDocument;
public class ReferencedDocument implements IReferencedDocument {
String issuerAssignedID;
String typeCode;
String referenceTypeCode;
public void setIssuerAssignedID(String issuerAssignedID) {
this.issuerAssignedID = issuerAssignedID;
}
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
public void setReferenceTypeCode(String referenceTypeCode) {
this.referenceTypeCode = referenceTypeCode;
}
@Override
public String getIssuerAssignedID() {
return issuerAssignedID;
}
@Override
public String getTypeCode() {
return typeCode;
}
@Override
public String getReferenceTypeCode() {
return referenceTypeCode;
}
}

View File

@@ -0,0 +1,8 @@
package org.mustangproject.ZUGFeRD;
public interface IReferencedDocument {
String getIssuerAssignedID();
String getTypeCode();
String getReferenceTypeCode();
}

View File

@@ -94,6 +94,14 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
return null; return null;
} }
/***
* allows to specify multiple(!) referenced documents along with e.g. their typecodes
* @return
*/
default IReferencedDocument[] getReferencedDocuments() {
return null;
}
/*** /***
* descriptive texts * descriptive texts
* @return an array of strings of item specific "includedNotes", text values * @return an array of strings of item specific "includedNotes", text values

View File

@@ -351,9 +351,21 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " </ram:SpecifiedTradeProduct>\n" + " </ram:SpecifiedTradeProduct>\n"
+ " <ram:SpecifiedLineTradeAgreement>\n"; + " <ram:SpecifiedLineTradeAgreement>\n";
if (currentItem.getReferencedDocuments() != null) {
for (IReferencedDocument currentReferencedDocument : currentItem.getReferencedDocuments()) {
xml = xml + "<ram:AdditionalReferencedDocument>\n" +
"<ram:IssuerAssignedID>" + XMLTools.encodeXML(currentReferencedDocument.getIssuerAssignedID()) + "</ram:IssuerAssignedID>\n" +
"<ram:TypeCode>" + XMLTools.encodeXML(currentReferencedDocument.getTypeCode()) + "</ram:TypeCode>\n" +
"<ram:ReferenceTypeCode>" + XMLTools.encodeXML(currentReferencedDocument.getReferenceTypeCode()) + "</ram:ReferenceTypeCode>\n" +
"</ram:AdditionalReferencedDocument>\n";
}
}
if (currentItem.getBuyerOrderReferencedDocumentLineID() != null) { if (currentItem.getBuyerOrderReferencedDocumentLineID() != null) {
xml = xml + " <ram:BuyerOrderReferencedDocument> \n" xml = xml + " <ram:BuyerOrderReferencedDocument> \n"
+ " <ram:LineID>"+XMLTools.encodeXML(currentItem.getBuyerOrderReferencedDocumentLineID())+"</ram:LineID>\n" + " <ram:LineID>" + XMLTools.encodeXML(currentItem.getBuyerOrderReferencedDocumentLineID()) + "</ram:LineID>\n"
+ " </ram:BuyerOrderReferencedDocument>\n"; + " </ram:BuyerOrderReferencedDocument>\n";
} }

View File

@@ -18,6 +18,8 @@ import java.util.Date;
public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDInvoiceImporter.class.getCanonicalName()); // log private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDInvoiceImporter.class.getCanonicalName()); // log
private boolean recalcPrice = false;
private boolean ignoreCalculationErrors = false;
public ZUGFeRDInvoiceImporter() { public ZUGFeRDInvoiceImporter() {
super(); super();
@@ -84,7 +86,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if ((exchangedDocumentChilds.item(documentChildIndex).getLocalName() != null) && (exchangedDocumentChilds.item(documentChildIndex).getLocalName().equals("IssueDateTime"))) { if ((exchangedDocumentChilds.item(documentChildIndex).getLocalName() != null) && (exchangedDocumentChilds.item(documentChildIndex).getLocalName().equals("IssueDateTime"))) {
NodeList issueDateTimeChilds = exchangedDocumentChilds.item(documentChildIndex).getChildNodes(); NodeList issueDateTimeChilds = exchangedDocumentChilds.item(documentChildIndex).getChildNodes();
for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) { for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) {
if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName()!=null)&&(issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) { if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null) && (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) {
issueDate = new SimpleDateFormat("yyyyMMdd").parse(issueDateTimeChilds.item(issueDateChildIndex).getTextContent()); issueDate = new SimpleDateFormat("yyyyMMdd").parse(issueDateTimeChilds.item(issueDateChildIndex).getTextContent());
} }
} }
@@ -160,6 +162,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
String description = ""; String description = "";
String quantity = "0"; String quantity = "0";
String vatPercent = "0"; String vatPercent = "0";
String lineTotal = "0";
String unitCode = "0"; String unitCode = "0";
//nodes.item(i).getTextContent())) { //nodes.item(i).getTextContent())) {
@@ -169,7 +172,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedLineTradeAgreement"))) { if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedLineTradeAgreement"))) {
NodeList tradeLineChilds = itemChilds.item(itemChildIndex).getChildNodes(); NodeList tradeLineChilds = itemChilds.item(itemChildIndex).getChildNodes();
for (int tradeLineChildIndex = 0; tradeLineChildIndex < tradeLineChilds.getLength(); tradeLineChildIndex++) { for (int tradeLineChildIndex = 0; tradeLineChildIndex < tradeLineChilds.getLength(); tradeLineChildIndex++) {
if ((tradeLineChilds.item(tradeLineChildIndex).getLocalName()!=null)&&tradeLineChilds.item(tradeLineChildIndex).getLocalName().equals("NetPriceProductTradePrice")) { if ((tradeLineChilds.item(tradeLineChildIndex).getLocalName() != null) && tradeLineChilds.item(tradeLineChildIndex).getLocalName().equals("NetPriceProductTradePrice")) {
NodeList netChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes(); NodeList netChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes();
for (int netIndex = 0; netIndex < netChilds.getLength(); netIndex++) { for (int netIndex = 0; netIndex < netChilds.getLength(); netIndex++) {
if ((netChilds.item(netIndex).getLocalName() != null) && (netChilds.item(netIndex).getLocalName().equals("ChargeAmount"))) { if ((netChilds.item(netIndex).getLocalName() != null) && (netChilds.item(netIndex).getLocalName().equals("ChargeAmount"))) {
@@ -192,8 +195,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedTradeProduct"))) { if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedTradeProduct"))) {
NodeList tradeProductChilds = itemChilds.item(itemChildIndex).getChildNodes(); NodeList tradeProductChilds = itemChilds.item(itemChildIndex).getChildNodes();
for (int tradeProductChildIndex = 0; tradeProductChildIndex < tradeProductChilds.getLength(); tradeProductChildIndex++) { for (int tradeProductChildIndex = 0; tradeProductChildIndex < tradeProductChilds.getLength(); tradeProductChildIndex++) {
if ((tradeProductChilds.item(tradeProductChildIndex).getLocalName() != null) && (tradeProductChilds.item(tradeProductChildIndex).getLocalName().equals("Name"))) if ((tradeProductChilds.item(tradeProductChildIndex).getLocalName() != null) && (tradeProductChilds.item(tradeProductChildIndex).getLocalName().equals("Name"))) {
{
name = tradeProductChilds.item(tradeProductChildIndex).getTextContent(); name = tradeProductChilds.item(tradeProductChildIndex).getTextContent();
} }
} }
@@ -201,95 +203,116 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedLineTradeSettlement"))) { if ((itemChilds.item(itemChildIndex).getLocalName() != null) && (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedLineTradeSettlement"))) {
NodeList tradeSettlementChilds = itemChilds.item(itemChildIndex).getChildNodes(); NodeList tradeSettlementChilds = itemChilds.item(itemChildIndex).getChildNodes();
for (int tradeSettlementChildIndex = 0; tradeSettlementChildIndex < tradeSettlementChilds.getLength(); tradeSettlementChildIndex++) { for (int tradeSettlementChildIndex = 0; tradeSettlementChildIndex < tradeSettlementChilds.getLength(); tradeSettlementChildIndex++) {
if ((tradeSettlementChilds.item(tradeSettlementChildIndex).getLocalName() != null) && (tradeSettlementChilds.item(tradeSettlementChildIndex).getLocalName().equals("ApplicableTradeTax"))) {
NodeList taxChilds = tradeSettlementChilds.item(tradeSettlementChildIndex).getChildNodes(); if (tradeSettlementChilds.item(tradeSettlementChildIndex).getLocalName() != null) {
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { if (tradeSettlementChilds.item(tradeSettlementChildIndex).getLocalName().equals("ApplicableTradeTax")) {
if ((taxChilds.item(taxChildIndex).getLocalName() != null) && (taxChilds.item(taxChildIndex).getLocalName().equals("RateApplicablePercent"))) { NodeList taxChilds = tradeSettlementChilds.item(tradeSettlementChildIndex).getChildNodes();
vatPercent = taxChilds.item(taxChildIndex).getTextContent(); for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
if ((taxChilds.item(taxChildIndex).getLocalName() != null) && (taxChilds.item(taxChildIndex).getLocalName().equals("RateApplicablePercent"))) {
vatPercent = taxChilds.item(taxChildIndex).getTextContent();
}
}
}
if (tradeSettlementChilds.item(tradeSettlementChildIndex).getLocalName().equals("SpecifiedTradeSettlementLineMonetarySummation")) {
NodeList totalChilds = tradeSettlementChilds.item(tradeSettlementChildIndex).getChildNodes();
for (int totalChildIndex = 0; totalChildIndex < totalChilds.getLength(); totalChildIndex++) {
if ((totalChilds.item(totalChildIndex).getLocalName() != null) && (totalChilds.item(totalChildIndex).getLocalName().equals("LineTotalAmount"))) {
lineTotal = totalChilds.item(totalChildIndex).getTextContent();
}
} }
} }
} }
} }
} }
} }
zpp.addItem(new Item(new Product(name, description, unitCode, new BigDecimal(vatPercent.trim())), new BigDecimal(price.trim()), new BigDecimal(quantity.trim()))); BigDecimal prc = new BigDecimal(price.trim());
BigDecimal qty = new BigDecimal(quantity.trim());
if ((recalcPrice) && (!qty.equals(BigDecimal.ZERO))) {
prc = new BigDecimal(lineTotal.trim()).divide(qty,4, RoundingMode.HALF_UP);
}
zpp.addItem(new Item(new Product(name, description, unitCode, new BigDecimal(vatPercent.trim())), prc, qty));
} }
} // item level charges+allowances are not yet handled but a lower item price will be read,
// so the invoice remains arithmetically correct
// -> parse document level charges+allowances
xpr = xpath.compile(
"//*[local-name()=\"SpecifiedTradeAllowanceCharge\"]");
NodeList chargeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
for (int i = 0; i < chargeNodes.getLength(); i++) {
NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes();
boolean isCharge = true;
String chargeAmount = null;
String reason = null;
String taxPercent = null;
for (int chargeChildIndex = 0; chargeChildIndex < chargeNodeChilds.getLength(); chargeChildIndex++) {
if (chargeNodeChilds.item(chargeChildIndex).getLocalName() != null) {
// item level charges+allowances are not yet handled but a lower item price will be read, if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("ChargeIndicator")) {
// so the invoice remains arithmetically correct NodeList indicatorChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes();
// -> parse document level charges+allowances for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) {
xpr = xpath.compile( if (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator")) {
"//*[local-name()=\"SpecifiedTradeAllowanceCharge\"]"); isCharge = indicatorChilds.item(indicatorChildIndex).getTextContent().equalsIgnoreCase("true");
NodeList chargeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); }
for (int i = 0; i < chargeNodes.getLength(); i++) {
NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes();
boolean isCharge = true;
String chargeAmount = null;
String reason = null;
String taxPercent = null;
for (int chargeChildIndex = 0; chargeChildIndex < chargeNodeChilds.getLength(); chargeChildIndex++) {
if (chargeNodeChilds.item(chargeChildIndex).getLocalName() != null) {
if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("ChargeIndicator")) {
NodeList indicatorChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes();
for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) {
if (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator")) {
isCharge = indicatorChilds.item(indicatorChildIndex).getTextContent().equalsIgnoreCase("true");
} }
} } else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("ActualAmount")) {
} else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("ActualAmount")) { chargeAmount = chargeNodeChilds.item(chargeChildIndex).getTextContent();
chargeAmount = chargeNodeChilds.item(chargeChildIndex).getTextContent(); } else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("Reason")) {
} else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("Reason")) { reason = chargeNodeChilds.item(chargeChildIndex).getTextContent();
reason = chargeNodeChilds.item(chargeChildIndex).getTextContent(); } else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("CategoryTradeTax")) {
} else if (chargeNodeChilds.item(chargeChildIndex).getLocalName().equals("CategoryTradeTax")) { NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes();
NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { if ((taxChilds.item(taxChildIndex).getLocalName() != null) && (taxChilds.item(taxChildIndex).getLocalName().equals("RateApplicablePercent"))) {
if ((taxChilds.item(taxChildIndex).getLocalName() != null) && (taxChilds.item(taxChildIndex).getLocalName().equals("RateApplicablePercent"))) taxPercent = taxChilds.item(taxChildIndex).getTextContent();
{ }
taxPercent = taxChilds.item(taxChildIndex).getTextContent();
} }
} }
} }
} }
if (isCharge) {
Charge c = new Charge(new BigDecimal(chargeAmount));
if (reason != null) {
c.setReason(reason);
}
if (taxPercent != null) {
c.setTaxPercent(new BigDecimal(taxPercent));
}
zpp.addCharge(c);
} else {
Allowance a = new Allowance(new BigDecimal(chargeAmount));
if (reason != null) {
a.setReason(reason);
}
if (taxPercent != null) {
a.setTaxPercent(new BigDecimal(taxPercent));
}
zpp.addAllowance(a);
}
} }
TransactionCalculator tc = new TransactionCalculator(zpp);
if (isCharge) { String expectedStringTotalGross = tc.getGrandTotal().toPlainString();
Charge c = new Charge(new BigDecimal(chargeAmount)); if ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2)))&&(!ignoreCalculationErrors)) {
if (reason != null) { throw new ParseException("Could not reproduce the invoice, this could mean that it could not be read properly", 0);
c.setReason(reason);
}
if (taxPercent != null) {
c.setTaxPercent(new BigDecimal(taxPercent));
}
zpp.addCharge(c);
} else {
Allowance a = new Allowance(new BigDecimal(chargeAmount));
if (reason != null) {
a.setReason(reason);
}
if (taxPercent != null) {
a.setTaxPercent(new BigDecimal(taxPercent));
}
zpp.addAllowance(a);
} }
} }
TransactionCalculator tc = new TransactionCalculator(zpp);
String expectedStringTotalGross = tc.getGrandTotal().toPlainString();
if (!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) {
throw new ParseException("Could not reproduce the invoice, this could mean that it could not be read properly", 0);
}
return zpp; return zpp;
} }
public void doRecalculateItemPricesFromLineTotals() {
recalcPrice = true;
}
public void doIgnoreCalculationErrors() {
ignoreCalculationErrors = true;
}
} }