Merge branch 'issues/764'

# Conflicts:
#	library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java
#	library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java
This commit is contained in:
jstaerk
2025-06-11 08:20:08 +02:00
15 changed files with 372 additions and 119 deletions

View File

@@ -3,8 +3,10 @@ package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import java.math.BigDecimal;
import java.math.RoundingMode;
/***
* (absolute) allowances on item and document level
@@ -29,6 +31,22 @@ public class Allowance extends Charge {
}
@Override
public BigDecimal getTotalAmount(IAbsoluteValueProvider currentItem) {
if(totalAmount != null) {
return totalAmount;
} else if (percent!=null) {
BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100))), 18, RoundingMode.HALF_UP);
// BigDecimal singlePrice=currentItem.getValue().multiply(BigDecimal.ONE.subtract(getPercent().divide(new BigDecimal(100))));
BigDecimal singlePriceDiff=currentItem.getValue().subtract(singlePrice);
return singlePriceDiff;
} else {
throw new RuntimeException("percent must be set");
}
}
/***
* Always to return false for IZUGFeRDAllowanceCharge
* @return false since its not supposed to be calculated negatively

View File

@@ -7,6 +7,7 @@ import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import java.math.BigDecimal;
import java.math.RoundingMode;
/***
* Absolute and relative charges for document and item level
@@ -23,6 +24,10 @@ public class Charge implements IZUGFeRDAllowanceCharge {
* the absolute value if not percentage
*/
protected BigDecimal totalAmount;
/**
* the value the percentage is applied upon
*/
protected BigDecimal basisAmount;
/**
* the tax rate the charge belongs to
*/
@@ -103,6 +108,22 @@ public class Charge implements IZUGFeRDAllowanceCharge {
}
@Override
public BigDecimal getBasisAmount() {
return basisAmount;
}
/***
* sets a potential basis for the potential percentage
* @param basis the basis amount
* @return fluid setter
*/
public Charge setBasisAmount(BigDecimal basis) {
this.basisAmount = basis;
return this;
}
@Override
public String getReasonCode() {
return reasonCode;
@@ -121,10 +142,13 @@ public class Charge implements IZUGFeRDAllowanceCharge {
@Override
public BigDecimal getTotalAmount(IAbsoluteValueProvider currentItem) {
if (percent!=null) {
return currentItem.getValue().multiply(getPercent().divide(new BigDecimal(100)));
} else if(totalAmount != null) {
if(totalAmount != null) {
return totalAmount;
} else if (percent!=null) {
BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100))), 18, RoundingMode.HALF_UP);
// BigDecimal singlePrice=currentItem.getValue().multiply(BigDecimal.ONE.subtract(getPercent().divide(new BigDecimal(100))));
BigDecimal singlePriceDiff=currentItem.getValue().add(singlePrice);
return singlePriceDiff;
} else {
throw new RuntimeException("percent must be set");
}

View File

@@ -72,8 +72,6 @@ public class Item implements IZUGFeRDExportableItem {
//we need: name description unitcode
//and we additionally have vat%
// Bharti's homework 20241126:Streams https://www.youtube.com/watch?v=Lf01cBzmuXw
// and Lambdas https://www.youtube.com/watch?v=HCyx31NW8xg
setProduct(new Product(itemMap.getNode("Item").get()));
icnm.getAsString("Name").ifPresent(product::setName);
icnm.getAsString("Description").ifPresent(product::setDescription);
@@ -86,17 +84,6 @@ public class Item implements IZUGFeRDExportableItem {
BuyersItemIdentification.getAsString("ID").ifPresent(product::setBuyerAssignedID);
});
// String name = icnm.getAsStringOrNull("Name");
// String val = icnm.getAsStringOrNull("Value");
// if (name != null && val != null) {
// if (attributes == null) {
// attributes = new HashMap<>();
// }
// product.attributes.put(name, val);
// }
//icnm.getNode("AdditionalItemProperty").flatMap(n ->n.getAttributes()).ifPresent(product::setAttributes);
icnm.getAsNodeMap("ClassifiedTaxCategory").flatMap(m -> m.getAsBigDecimal("Percent"))
.ifPresent(product::setVATPercent);
});
@@ -175,11 +162,12 @@ public class Item implements IZUGFeRDExportableItem {
.ifPresent(product::setTaxExemptionReason);
icnm.getAllNodes("SpecifiedTradeAllowanceCharge").map(NodeMap::new).forEach(stac -> {
stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> { //CII
stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> {
String isChargeString = ci.getAsString("Indicator").get();
String percentString = stac.getAsStringOrNull("CalculationPercent");//MultiplierFactorNumeric
String amountString = stac.getAsStringOrNull("ActualAmount");//Amount
String reason = stac.getAsStringOrNull("Reason");//AllowanceChargeReason
String percentString = stac.getAsStringOrNull("CalculationPercent");
String amountString = stac.getAsStringOrNull("ActualAmount");
String basisAmountString = stac.getAsStringOrNull("BasisAmount");
String reason = stac.getAsStringOrNull("Reason");
Charge izac = new Charge();
if (isChargeString.equalsIgnoreCase("false")) {
izac = new Allowance();
@@ -188,6 +176,12 @@ public class Item implements IZUGFeRDExportableItem {
}
if (amountString != null) {
izac.setTotalAmount(new BigDecimal(amountString));
if (percentString!=null&&(percentString!="0")) {
izac.setTotalAmount(new BigDecimal(amountString).divide(getQuantity()));
}
}
if (basisAmountString != null) {
izac.setBasisAmount(new BigDecimal(basisAmountString));
}
if (percentString != null) {
izac.setPercent(new BigDecimal(percentString));
@@ -288,6 +282,16 @@ public class Item implements IZUGFeRDExportableItem {
return this;
}
@Override public IZUGFeRDAllowanceCharge[] getAllowances() {
IZUGFeRDAllowanceCharge[] izac=new IZUGFeRDAllowanceCharge[Allowances.size()];
return Allowances.toArray(izac);
}
@Override public IZUGFeRDAllowanceCharge[] getCharges() {
IZUGFeRDAllowanceCharge[] izac=new IZUGFeRDAllowanceCharge[Charges.size()];
return Charges.toArray(izac);
}
/***
* BT 132 (issue https://github.com/ZUGFeRD/mustangproject/issues/247)
* @return the line ID of the order (BT132)
@@ -461,6 +465,7 @@ public class Item implements IZUGFeRDExportableItem {
* @return fluent setter
*/
public Item addAllowance(IZUGFeRDAllowanceCharge izac) {
Allowances.add(izac);
return this;
}

View File

@@ -32,6 +32,8 @@ public class Product implements IZUGFeRDExportableProduct {
protected boolean isIntraCommunitySupply = false;
protected SchemedID globalId = null;
protected String countryOfOrigin = null;
protected ArrayList<Charge> charges=new ArrayList<>();
protected ArrayList<Allowance> allowances=new ArrayList<>();
protected HashMap<String, String> attributes = new HashMap<>();
protected List<IDesignatedProductClassification> classifications = new ArrayList<>();
@@ -393,4 +395,42 @@ public class Product implements IZUGFeRDExportableProduct {
this.classifications.add(classification);
return this;
}
public Product addCharge(Charge e) {
charges.add(e);
return this;
}
public Product addAllowance(Allowance a) {
allowances.add(a);
return this;
}
/***
* returns the AppliedTradeAllowanceCharges of this product which are actually Charges
* @return array of or null, if none
*/
@Override
public Charge[] getCharges() {
if (charges.size()==0) {
return null;
}
Charge[] chargeArr = new Charge[charges.size()];
return charges.toArray(chargeArr);
}
@Override
/***
* returns the AppliedTradeAllowanceCharges of this product which are actually Allowances
* @return array of or null, if none
*/
public Allowance[] getAllowances() {
if (allowances.size()==0) {
return null;
}
Allowance[] allowanceArr = new Allowance[allowances.size()];
return allowances.toArray(allowanceArr);
}
}

View File

@@ -38,6 +38,12 @@ public interface IZUGFeRDAllowanceCharge {
*/
default BigDecimal getPercent() {return null;}
/***
* returns a basis the precentage is calculated from
* @return null or the basis
*/
default BigDecimal getBasisAmount() {return null;}
/***
* get a description for the allowance/charge
* @return the description

View File

@@ -43,7 +43,11 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
* item level discounts
* @return array of the discounts on a single item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getItemAllowances() {
if (getProduct()!=null) {
return getProduct().getAllowances();
}
return null;
}
@@ -51,10 +55,27 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
* item level price additions
* @return array of the additional charges on the item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getItemCharges() {
if (getProduct()!=null) {
return getProduct().getCharges();
}
return null;
}
@Deprecated
default IZUGFeRDAllowanceCharge[] getAllowances() {
return null;
}
/**
* item level price additions
* @return array of the additional charges on the item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getCharges() {
return null;
}
/***
* BT 132 (issue https://github.com/ZUGFeRD/mustangproject/issues/247)
@@ -147,9 +168,9 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
}
/***
* specify allowances amount for the line item total
* get all (allowances and charges) SpecifiedTradeAllowanceCharges
*
* @return the sum of allowances for this item
* @return the real item lecel SpecifiedTradeAllowanceCharges
*/
default IZUGFeRDAllowanceCharge[] getItemTotalAllowances() {
return null;

View File

@@ -166,6 +166,25 @@ public interface IZUGFeRDExportableProduct {
return null;
}
/**
* product level discounts (AppliedTradeAllowanceCharge, will change net price)
* @return array of the discounts on a single product
*/
default IZUGFeRDAllowanceCharge[] getAllowances() {
return null;
}
/**
* product level charges (AppliedTradeAllowanceCharge, will change net price)
* @return array of the additional charges on the product
*/
default IZUGFeRDAllowanceCharge[] getCharges() {
return null;
}
/**
* Detailed information about the product
*

View File

@@ -2,30 +2,49 @@ package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
/***
* the linecalculator does the math within an item line, and e.g. calculates quantity*price.
* @see TransactionCalculator
*/
public class LineCalculator {
private final BigDecimal price;
private final BigDecimal priceGross;
private final BigDecimal itemTotalNetAmount;
private final BigDecimal itemTotalVATAmount;
private BigDecimal allowance = BigDecimal.ZERO;
private BigDecimal charge = BigDecimal.ZERO;
private BigDecimal allowanceItemTotal = BigDecimal.ZERO;
protected BigDecimal price;
protected BigDecimal priceGross;
protected BigDecimal itemTotalNetAmount;
protected BigDecimal itemTotalVATAmount;
protected BigDecimal lineAllowance = BigDecimal.ZERO;
protected BigDecimal lineCharge = BigDecimal.ZERO;
protected BigDecimal itemAllowance = BigDecimal.ZERO;
protected BigDecimal itemCharge = BigDecimal.ZERO;
protected BigDecimal allowanceItemTotal = BigDecimal.ZERO;
public LineCalculator(IZUGFeRDExportableItem currentItem) {
if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) {
for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) {
addAllowance(allowance.getTotalAmount(currentItem));
BigDecimal factor=BigDecimal.ONE;
BigDecimal singleAllowance=allowance.getTotalAmount(currentItem);
addItemAllowance(singleAllowance);
if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) {
factor=currentItem.getQuantity();
}
addAllowanceItemTotal(singleAllowance.multiply(factor));
}
}
if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) {
for (IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) {
addCharge(charge.getTotalAmount(currentItem));
BigDecimal factor=BigDecimal.ONE;
BigDecimal singleCharge=charge.getTotalAmount(currentItem);
addItemCharge(singleCharge);
if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) {
factor=currentItem.getQuantity();
}
subtractAllowanceItemTotal(singleCharge.multiply(factor));
}
}
if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) {
@@ -42,22 +61,39 @@ public class LineCalculator {
vatPercent = BigDecimal.ZERO;
}
BigDecimal multiplicator = vatPercent.divide(BigDecimal.valueOf(100));
priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159
price = priceGross.subtract(allowance).add(charge);
BigDecimal quantity=BigDecimal.ZERO;
if ((currentItem!=null)&&(currentItem.getQuantity()!=null)) {
quantity=currentItem.getQuantity();
}
price=currentItem.getPrice();
priceGross=price;
// price=price.subtract(itemAllowance).add(itemCharge);
// BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance);
// delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP);
BigDecimal delta=BigDecimal.ZERO;
if (currentItem.getProduct().getAllowances()!=null) {
for (IZUGFeRDAllowanceCharge ccaf:currentItem.getProduct().getAllowances()) {
delta=delta.subtract(ccaf.getTotalAmount(currentItem));
}
}
if (currentItem.getProduct().getCharges()!=null) {
for (IZUGFeRDAllowanceCharge ccaf : currentItem.getProduct().getCharges()) {
delta = delta.subtract(ccaf.getTotalAmount(currentItem));
}
}
price=price.add(delta);
// Division/Zero occurred here.
// Used the setScale only because that's also done in getBasisQuantity
BigDecimal basisQuantity = currentItem.getBasisQuantity().compareTo(BigDecimal.ZERO) == 0
? BigDecimal.ONE.setScale(4)
: currentItem.getBasisQuantity();
itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP)
.subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);
.add(lineCharge).subtract(lineAllowance).subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);//.setScale(2, RoundingMode.HALF_UP);
}
public BigDecimal getPrice() {
@@ -80,16 +116,27 @@ public class LineCalculator {
return priceGross;
}
public void addAllowance(BigDecimal b) {
allowance = allowance.add(b);
public void addLineAllowance(BigDecimal b) {
lineAllowance = lineAllowance.add(b);
}
public void addCharge(BigDecimal b) {
charge = charge.add(b);
public void addLineCharge(BigDecimal b) {
lineCharge = lineCharge.add(b);
}
public void addItemAllowance(BigDecimal b) {
itemAllowance = itemAllowance.add(b);
}
public void addItemCharge(BigDecimal b) {
itemCharge = itemCharge.add(b);
}
public void addAllowanceItemTotal(BigDecimal b) {
allowanceItemTotal = allowanceItemTotal.add(b);
}
public void subtractAllowanceItemTotal(BigDecimal b) {
allowanceItemTotal = allowanceItemTotal.subtract(b);
}
}

View File

@@ -321,11 +321,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
String reason = "";
if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) {
boolean isEN16931=(profile == Profiles.getByName("XRechnung")) || (profile == Profiles.getByName("EN16931"));
if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || isEN16931)) {
reason = "<ram:Reason>" + XMLTools.encodeXML(allowance.getReason()) + "</ram:Reason>";
}
String reasonCode = "";
if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("XRechnung"))) {
if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("Extended") || isEN16931)) {
// only in XRechnung profile
reasonCode = "<ram:ReasonCode>" + allowance.getReasonCode() + "</ram:ReasonCode>";
}
@@ -433,24 +434,29 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + "</ram:BuyerAssignedID>";
}
String allowanceChargeStr = "";
if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) {
if (currentItem.getProduct().getAllowances() != null && currentItem.getProduct().getAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge allowance : currentItem.getProduct().getAllowances()) {
allowanceChargeStr += getAllowanceChargeStr(allowance, currentItem);
}
}
if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) {
if (currentItem.getProduct().getCharges() != null && currentItem.getProduct().getCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge charge : currentItem.getProduct().getCharges()) {
allowanceChargeStr += getAllowanceChargeStr(charge, currentItem);
}
}
String itemTotalAllowanceChargeStr = "";
if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalAllowance : currentItem.getItemTotalAllowances()) {
if (currentItem.getAllowances() != null && currentItem.getAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalAllowance : currentItem.getAllowances()) {
itemTotalAllowanceChargeStr += getItemTotalAllowanceChargeStr(itemTotalAllowance, currentItem);
}
}
if (currentItem.getCharges() != null && currentItem.getCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalCharges : currentItem.getCharges()) {
itemTotalAllowanceChargeStr += getItemTotalAllowanceChargeStr(itemTotalCharges, currentItem);
}
}
xml += "<ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>";
if (currentItem.getProduct().getDescription() != null && currentItem.getProduct().getDescription().length() > 0) {
@@ -553,7 +559,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "</ram:BillingSpecifiedPeriod>";
}
xml += itemTotalAllowanceChargeStr;
// item charges/allowances
if (!itemTotalAllowanceChargeStr.isEmpty()) {
xml += itemTotalAllowanceChargeStr ;
}
xml += "<ram:SpecifiedTradeSettlementLineMonetarySummation>"
+ "<ram:LineTotalAmount>" + currencyFormat(lc.getItemTotalNetAmount())

View File

@@ -383,29 +383,7 @@ public class ZUGFeRDInvoiceImporter {
});
});
String street, name, additionalStreet, city, postal, countrySubentity, line, country = null;
/*
String idx = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"ID\"]");
street = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name()=\"Address\"]/*[local-name()=\"StreetName\"]");
additionalStreet = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"AdditionalStreetName\"]");
city = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"CityName\"]");
postal = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"PostalZone\"]");
countrySubentity = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"CountrySubentity\"]");
line = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]//*[local-name() = \"AddressLine\"]/*[local-name() = \"Line\"]");
country = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]//*[local-name() = \"Country\"]/*[local-name() = \"IdentificationCode\"]");
name = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"DeliveryParty\"]//*[local-name() = \"PartyName\"]/*[local-name() = \"Name\"]");
*/
zpp.setDeliveryAddress(delivery);
/*
zpp.setDeliveryAddress(new TradeParty()
.setStreet(street)
.setAdditionalAddress(additionalStreet)
.setLocation(city)
.setZIP(postal)
.setAdditionalAddressExtension(line)
.setCountry(country)
.setName(name)
);
*/
}
@@ -420,14 +398,6 @@ public class ZUGFeRDInvoiceImporter {
XPathExpression shipPayee = xpath.compile("//*[local-name()=\"PayeeParty\"]/*");
NodeList ublPayeeNodes = (NodeList) shipPayee.evaluate(getDocument(), XPathConstants.NODESET);
// if(ublPayeeNodes != null) {
// TradeParty payee = new TradeParty();
// NodeMap nodeMap = new NodeMap(ublPayeeNodes).getAsNodeMap("PayeeParty").get();
// nodeMap.getNode("ID").ifPresent(s -> {
// SchemedID sID = new SchemedID().setScheme(s.getAttributes().getNamedItem("schemeID").getTextContent()).setId(s.getTextContent());
// payee.addGlobalID(sID);
// });
// }
//NodeList UBLpayeeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (ublPayeeNodes.getLength() > 0) {
zpp.setPayee(new TradeParty(ublPayeeNodes));
@@ -836,9 +806,6 @@ public class ZUGFeRDInvoiceImporter {
}
}
}
// if ((paymentMeansChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("DirectDebitMandateID"))) {
// directDebitMandateID = paymentTermChilds.item(paymentTermChildIndex).getTextContent();
// }
if ((paymentMeansChilds.item(meansChildIndex).getLocalName() != null)
&& (paymentMeansChilds.item(meansChildIndex).getLocalName().equals("PaymentMandate"))) {
NodeList paymentMandateChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes();
@@ -974,6 +941,7 @@ public class ZUGFeRDInvoiceImporter {
NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes();
boolean isCharge = true;
String chargeAmount = null;
String basisAmount = null;
String reason = null;
String reasonCode = null;
String taxPercent = null;
@@ -1001,6 +969,8 @@ public class ZUGFeRDInvoiceImporter {
} else if (chargeChildName.equals("ActualAmount") || chargeChildName.equals("Amount")) {
chargeAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("BasisAmount")) {
basisAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("Reason") || chargeChildName.equals("AllowanceChargeReason")) {
reason = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("ReasonCode") || chargeChildName.equals("AllowanceChargeReasonCode")) {
@@ -1028,6 +998,9 @@ public class ZUGFeRDInvoiceImporter {
if (taxPercent != null) {
c.setTaxPercent(new BigDecimal(taxPercent));
}
if (basisAmount != null) {
c.setBasisAmount(new BigDecimal(basisAmount));
}
zpp.addCharge(c);
} else {
Allowance a = new Allowance(new BigDecimal(chargeAmount));
@@ -1040,6 +1013,9 @@ public class ZUGFeRDInvoiceImporter {
if (taxPercent != null) {
a.setTaxPercent(new BigDecimal(taxPercent));
}
if (basisAmount != null) {
a.setBasisAmount(new BigDecimal(basisAmount));
}
zpp.addAllowance(a);
}

View File

@@ -54,9 +54,9 @@ public class CalculationTest extends ResourceCase {
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(133.857).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1606.28).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(257.0048).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1769.89).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(283.1824).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
@@ -74,17 +74,20 @@ public class CalculationTest extends ResourceCase {
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(163.603).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1963.24).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(314.1184).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1799.63).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(287.9408).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorForeignCurrencyExample() {
/*
/*** xml of official fx sample with allowances and charges
* 10x100 with 10% and 50€ item discount =850€
* +8,75 charges on document level=858,75, +19%VAT=1021,91
* prepaid 500->due payable=521,91
*/
File inputCII = getResourceAsFile("Extended_fremdwaehrung.xml");
inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250407\\fremdwaehrung.xml");
ZUGFeRDInvoiceImporter zii=new ZUGFeRDInvoiceImporter();
Invoice invoice=null;
zii.doIgnoreCalculationErrors();
@@ -104,9 +107,9 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250
final TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(valueOf(521.91).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
assertEquals(valueOf(521.91).stripTrailingZeros(), calculator.getDuePayable().stripTrailingZeros());
*/
}
@@ -221,9 +224,94 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250
invoice.addAllowance(new Allowance().setPercent(total_discount_percent).setTaxPercent(sales_tax_percent1).setReasonCode("95").setReason("Rabatte"));
}
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(valueOf(101.86).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
assertEquals(valueOf(307.18).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
}
public void testSimpleItemPercentAllowance() {
/***
* a product with net 1.10 and qty 5 and relative item allowance of 10% should return 5 as line and grand total
*/
SimpleDateFormat sqlDate = new SimpleDateFormat("yyyy-MM-dd");
Invoice invoice = new Invoice();
invoice.setDocumentName("Rechnung");
invoice.setNumber("777777");
try {
invoice.setIssueDate(sqlDate.parse("2020-12-31"));
invoice.setDetailedDeliveryPeriod(sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[0]), sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[1]));
invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) {
LOGGER.error("Failed to set dates", e);
}
TradeParty sender = new TradeParty("Maier GmbH", "Musterweg 5", "11111", "Testung", "DE");
sender.addVATID("DE2222222222");
invoice.setSender(sender);
/* trade party (recipient) */
TradeParty recipient = new TradeParty("Teston GmbH" + " " + "Zentrale" + " " + "", "Testweg 5", "11111", "Testung", "DE");
recipient.setID("111111");
recipient.addVATID("DE111111111");
invoice.setRecipient(recipient);
/* item */
Product product;
Item item;
product = new Product("AAA", "", "H84", BigDecimal.ZERO);
item = new Item(product, new BigDecimal("1.10"), new BigDecimal(5.00));
item.addAllowance(new Allowance().setPercent(new BigDecimal(10)).setTaxPercent(BigDecimal.ZERO));
invoice.addItem(item);
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(new BigDecimal(5), calculator.getGrandTotal().stripTrailingZeros());
}
public void testSimpleItemTotalAllowance() {
/***
* a product with net 1 and qty 5 and absolute _item_ allowance of 1 should return 4 as line total, and grand total
*/
SimpleDateFormat sqlDate = new SimpleDateFormat("yyyy-MM-dd");
Invoice invoice = new Invoice();
invoice.setDocumentName("Rechnung");
invoice.setNumber("777777");
try {
invoice.setIssueDate(sqlDate.parse("2020-12-31"));
invoice.setDetailedDeliveryPeriod(sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[0]), sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[1]));
invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) {
LOGGER.error("Failed to set dates", e);
}
TradeParty sender = new TradeParty("Maier GmbH", "Musterweg 5", "11111", "Testung", "DE");
sender.addVATID("DE2222222222");
invoice.setSender(sender);
/* trade party (recipient) */
TradeParty recipient = new TradeParty("Teston GmbH" + " " + "Zentrale" + " " + "", "Testweg 5", "11111", "Testung", "DE");
recipient.setID("111111");
recipient.addVATID("DE111111111");
invoice.setRecipient(recipient);
/* item */
Product product;
Item item;
product = new Product("AAA", "", "H84", BigDecimal.ZERO);
item = new Item(product, new BigDecimal("1.00"), new BigDecimal(5.00));
item.addAllowance(new Allowance(new BigDecimal(1)).setTaxPercent(BigDecimal.ZERO));
invoice.addItem(item);
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(new BigDecimal(4), calculator.getGrandTotal().stripTrailingZeros());
}
/**
* LineCalculator should not throw an exception when calculating a non-terminating decimal expansion
* */

View File

@@ -395,7 +395,7 @@ public class DeSerializationTest extends ResourceCase {
try {
Invoice newInvoiceFromJSON = mapper.readValue(json, Invoice.class);
TransactionCalculator tc=new TransactionCalculator(newInvoiceFromJSON);
assertEquals(new BigDecimal("19.52"),tc.getGrandTotal());
assertEquals(new BigDecimal("18.92"),tc.getGrandTotal());
} catch (JsonProcessingException e) {
throw new RuntimeException(e);

View File

@@ -141,7 +141,7 @@ public class ZF2PushTest extends TestCase {
String senderDescription = "Kleinunternehmer";
String taxID = "9990815";
String theNote="oh lala";
String theNote = "oh lala";
BigDecimal price = new BigDecimal(priceStr);
try {
InputStream SOURCE_PDF = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
@@ -177,7 +177,7 @@ public class ZF2PushTest extends TestCase {
throw new RuntimeException(e);
}
assertEquals(i.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(),theNote);
assertEquals(i.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(), theNote);
assertEquals(senderDescription, i.getSender().getDescription());
// now check the contents (like MustangReaderTest)
@@ -255,16 +255,16 @@ public class ZF2PushTest extends TestCase {
// 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)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
Invoice i=new Invoice().setDueDate(new Date()).setIssueDate(new Date())
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")
.setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")))
.setNumber(number)
.addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AReason").setTaxPercent(new BigDecimal(19)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1"))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1")).setReasonCode("95")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)).setReason("In love with salesperson")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AnotherReason")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1"))));
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("Yet another reason")).addAllowance(new Allowance(new BigDecimal("1")).setReason("Something completely strange")));
ze.setTransaction(i);
@@ -275,21 +275,20 @@ public class ZF2PushTest extends TestCase {
fail("IOException should not be raised");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_ITEMCHARGESALLOWANCESPDF);
assertEquals("EUR", zi.getInvoiceCurrencyCode());
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
assertTrue(zi.getUTF8().contains("ABK"));
// Reading ZUGFeRD
assertEquals("19.52", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_ITEMCHARGESALLOWANCESPDF);
assertEquals("EUR", zi.getInvoiceCurrencyCode());
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
assertTrue(zi.getUTF8().contains("ABK"));
// Reading ZUGFeRD
assertEquals("18.92", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
assertEquals(zi.getVersion(), 2);
} catch (Exception e) {
e.printStackTrace();
fail("Exception should not be raised");
}
}
@@ -595,7 +594,7 @@ public class ZF2PushTest extends TestCase {
assertEquals(1, i.getInvoiceReferencedDocuments().size());
assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID());
assertEquals("4304171000002", i.getRecipient().getGlobalID());
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(occurrenceFrom, sdf.format(i.getDetailedDeliveryPeriodFrom()));
assertEquals(occurrenceTo, sdf.format(i.getDetailedDeliveryPeriodTo()));
assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID());
@@ -629,7 +628,7 @@ public class ZF2PushTest extends TestCase {
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).addAllowance(new Allowance(BigDecimal.ONE)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance(new BigDecimal(600)).setTaxPercent(new BigDecimal(19)))
);
String theXML = new String(ze.getProvider().getXML());
@@ -645,7 +644,7 @@ public class ZF2PushTest extends TestCase {
assertEquals("EUR", zi.getInvoiceCurrencyCode());
// Reading ZUGFeRD
assertEquals("4046.00", zi.getAmount());
assertEquals("10805.20", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {
@@ -675,8 +674,8 @@ public class ZF2PushTest extends TestCase {
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK")))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReason("Mengenrabatt"))
);
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
@@ -689,7 +688,7 @@ public class ZF2PushTest extends TestCase {
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_RELATIVECHARGESALLOWANCESPDF);
assertEquals("CHF", zi.getInvoiceCurrencyCode());
assertEquals("6.25", zi.getAmount());
assertEquals("11.10", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {

View File

@@ -262,7 +262,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
}
assertFalse(hasExceptions);
TransactionCalculator tc = new TransactionCalculator(invoice);
assertEquals(new BigDecimal("19.52"), tc.getGrandTotal());
assertEquals(new BigDecimal("18.92"), tc.getGrandTotal());
}
public void testBasisQuantityImport() {