support item level allowances

This commit is contained in:
Jochen Stärk
2020-09-17 13:31:31 +02:00
parent e227608782
commit 060aa79958
9 changed files with 69 additions and 14 deletions

View File

@@ -594,9 +594,9 @@ public class Main {
System.out.println("ZUGFeRD XML source set to " + sourceName); System.out.println("ZUGFeRD XML source set to " + sourceName);
} }
if (outName == null) { if (outName == null) {
outName = getFilenameFromUser("ZUGFeRD 2.0 HTML target", "factur-x.html", "html", false, true); outName = getFilenameFromUser("HTML target file", "factur-x.html", "html", false, true);
} else { } else {
System.out.println("ZUGFeRD 1.0 XML source set to " + outName); System.out.println("HTML target set to " + outName);
} }
// Verify params // Verify params

View File

@@ -36,7 +36,8 @@ larger) and a commandline application using the latter
library. library.
The validator component embeds VeraPDF, a open-source The validator component embeds VeraPDF, a open-source
PDF/A-validator, via maven dependency and uses ph-schematron PDF/A-validator, via maven dependency and uses standard java
checks against schema and ph-schematron for checks against the schematron
to validate the XML part of the invoices. to validate the XML part of the invoices.
![architecture of the validator](ZUV-Architektur.svg "Graph of the architecture of the validator component") ![architecture of the validator](ZUV-Architektur.svg "Graph of the architecture of the validator component")

View File

@@ -13,4 +13,9 @@ public class Allowance extends Charge implements IZUGFeRDAllowanceCharge {
super(totalAmount, taxPercent, reason, categoryCode); super(totalAmount, taxPercent, reason, categoryCode);
} }
@Override
public boolean isCharge() {
return false;
}
} }

View File

@@ -62,6 +62,11 @@ public class Charge implements IZUGFeRDAllowanceCharge {
return taxPercent; return taxPercent;
} }
@Override
public boolean isCharge() {
return true;
}
public Charge setCategoryCode(String categoryCode) { public Charge setCategoryCode(String categoryCode) {
this.categoryCode = categoryCode; this.categoryCode = categoryCode;

View File

@@ -34,4 +34,5 @@ public interface IZUGFeRDAllowanceCharge {
return TaxCategoryCodeTypeConstants.STANDARDRATE; return TaxCategoryCodeTypeConstants.STANDARDRATE;
} }
public boolean isCharge();
} }

View File

@@ -4,19 +4,34 @@ import java.math.BigDecimal;
public class LineCalc { public class LineCalc {
private BigDecimal totalGross; private BigDecimal totalGross;
private BigDecimal price;
private BigDecimal priceGross; private BigDecimal priceGross;
private BigDecimal itemTotalNetAmount; private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount; private BigDecimal itemTotalVATAmount;
private BigDecimal allowance=new BigDecimal(0);
public LineCalc(IZUGFeRDExportableItem currentItem) { public LineCalc(IZUGFeRDExportableItem currentItem) {
if (currentItem.getItemAllowances()!=null && currentItem.getItemAllowances().length>0) {
for (IZUGFeRDAllowanceCharge allowance:currentItem.getItemAllowances()) {
addAllowance(allowance.getTotalAmount());
}
}
BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100)) BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100))
.add(new BigDecimal(1)); .add(new BigDecimal(1));
priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159 priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159
totalGross = currentItem.getQuantity().multiply(currentItem.getPrice()).divide(currentItem.getBasisQuantity()) price = priceGross.subtract(allowance);
totalGross = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity())
.multiply(multiplicator); .multiply(multiplicator);
itemTotalNetAmount = currentItem.getQuantity().multiply(currentItem.getPrice()).divide(currentItem.getBasisQuantity()) itemTotalNetAmount = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity())
.setScale(2, BigDecimal.ROUND_HALF_UP); .setScale(2, BigDecimal.ROUND_HALF_UP);
itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount); itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount);
}
public BigDecimal getPrice() {
return price;
} }
public BigDecimal getItemTotalNetAmount() { public BigDecimal getItemTotalNetAmount() {
@@ -28,12 +43,16 @@ public class LineCalc {
} }
public BigDecimal getItemTotalGrossAmount() { public BigDecimal getItemTotalGrossAmount() {
return itemTotalVATAmount; return itemTotalNetAmount;
} }
public BigDecimal getPriceGross() { public BigDecimal getPriceGross() {
return priceGross; return priceGross;
} }
public void addAllowance(BigDecimal b) {
allowance=b;
}
} }

View File

@@ -33,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.Charge;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider { public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
@@ -121,7 +122,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
BigDecimal res = new BigDecimal(0); BigDecimal res = new BigDecimal(0);
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) { for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
LineCalc lc = new LineCalc(currentItem); LineCalc lc = new LineCalc(currentItem);
res = res.add(lc.getItemTotalNetAmount()); res = res.add(lc.getItemTotalGrossAmount());
} }
return res; return res;
} }
@@ -286,6 +287,19 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
xml = xml + " <ram:BuyerAssignedID>" xml = xml + " <ram:BuyerAssignedID>"
+ XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + "</ram:BuyerAssignedID>\n"; + XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + "</ram:BuyerAssignedID>\n";
} }
String allowanceStr=" <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit())
+ "\">" + quantityFormat(currentItem.getBasisQuantity()) +"</ram:BasisQuantity>\n";
if (currentItem.getItemAllowances()!=null && currentItem.getItemAllowances().length>0) {
for (IZUGFeRDAllowanceCharge allowance:currentItem.getItemAllowances()) {
if (allowance.isCharge()) {
throw new RuntimeException("Item level charges are not yet implemented");
} else {
allowanceStr= "<ram:AppliedTradeAllowanceCharge><ram:ChargeIndicator><udt:Indicator>false</udt:Indicator></ram:ChargeIndicator><ram:ActualAmount>"+priceFormat(allowance.getTotalAmount())+"</ram:ActualAmount></ram:AppliedTradeAllowanceCharge>";
}
}
}
xml = xml + " <ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>\n" //$NON-NLS-2$ xml = xml + " <ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>\n" //$NON-NLS-2$
+ " <ram:Description>" + XMLTools.encodeXML(currentItem.getProduct().getDescription()) + " <ram:Description>" + XMLTools.encodeXML(currentItem.getProduct().getDescription())
+ "</ram:Description>\n" + "</ram:Description>\n"
@@ -295,8 +309,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
+ " <ram:GrossPriceProductTradePrice>\n" + " <ram:GrossPriceProductTradePrice>\n"
+ " <ram:ChargeAmount>" + priceFormat(lc.getPriceGross()) + " <ram:ChargeAmount>" + priceFormat(lc.getPriceGross())
+ "</ram:ChargeAmount>\n" //currencyID=\"EUR\" + "</ram:ChargeAmount>\n" //currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + allowanceStr
+ "\">" + quantityFormat(currentItem.getBasisQuantity()) +"</ram:BasisQuantity>\n"
// + " <AppliedTradeAllowanceCharge>\n" // + " <AppliedTradeAllowanceCharge>\n"
// + " <ChargeIndicator>false</ChargeIndicator>\n" // + " <ChargeIndicator>false</ChargeIndicator>\n"
// + " <ActualAmount currencyID=\"EUR\">0.6667</ActualAmount>\n" // + " <ActualAmount currencyID=\"EUR\">0.6667</ActualAmount>\n"
@@ -304,7 +317,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
// + " </AppliedTradeAllowanceCharge>\n" // + " </AppliedTradeAllowanceCharge>\n"
+ " </ram:GrossPriceProductTradePrice>\n" + " </ram:GrossPriceProductTradePrice>\n"
+ " <ram:NetPriceProductTradePrice>\n" + " <ram:NetPriceProductTradePrice>\n"
+ " <ram:ChargeAmount>" + priceFormat(currentItem.getPrice()) + " <ram:ChargeAmount>" + priceFormat(lc.getPrice())
+ "</ram:ChargeAmount>\n" // currencyID=\"EUR\" + "</ram:ChargeAmount>\n" // currencyID=\"EUR\"
+ " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + " <ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit())
+ "\">" + quantityFormat(currentItem.getBasisQuantity()) +"</ram:BasisQuantity>\n" + "\">" + quantityFormat(currentItem.getBasisQuantity()) +"</ram:BasisQuantity>\n"

View File

@@ -24,6 +24,7 @@ public class IZUGFeRDAllowanceChargeImpl implements IZUGFeRDAllowanceCharge {
private BigDecimal totalAmount; private BigDecimal totalAmount;
private String reason; private String reason;
private BigDecimal taxPercent; private BigDecimal taxPercent;
boolean isCharge=true;
@Override @Override
public BigDecimal getTotalAmount() { public BigDecimal getTotalAmount() {
@@ -40,6 +41,16 @@ public class IZUGFeRDAllowanceChargeImpl implements IZUGFeRDAllowanceCharge {
return taxPercent; return taxPercent;
} }
@Override
public boolean isCharge() {
return isCharge;
}
public IZUGFeRDAllowanceChargeImpl setAllowance() {
isCharge = false;
return this;
}
public IZUGFeRDAllowanceChargeImpl setTotalAmount(BigDecimal totalAmount) { public IZUGFeRDAllowanceChargeImpl setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount; this.totalAmount = totalAmount;
return this; return this;

View File

@@ -136,9 +136,9 @@ public class ZF2PushTest extends TestCase {
.load(SOURCE_PDF)) { .load(SOURCE_PDF)) {
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(0.1),new BigDecimal(0), "","K"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal(0.1),new BigDecimal(0), "","K")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal(0.07),new BigDecimal(0), "","K"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal(0.07),new BigDecimal(0), "","K")).addCharge(new Charge(new BigDecimal(0.1),new BigDecimal(0), "","K"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
@@ -154,7 +154,7 @@ public class ZF2PushTest extends TestCase {
assertTrue(zi.getUTF8().contains("EUR")); assertTrue(zi.getUTF8().contains("EUR"));
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals("10.71", zi.getAmount()); assertEquals("10.59", zi.getAmount());
assertEquals(zi.getHolder(), orgname); assertEquals(zi.getHolder(), orgname);
assertEquals(zi.getForeignReference(), number); assertEquals(zi.getForeignReference(), number);
try { try {