From cbcdaaec0ed071acdc529fe58669583a5da01a7b Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 6 May 2025 13:32:34 +0200 Subject: [PATCH 01/23] added a test, working on percents --- .../main/java/org/mustangproject/Charge.java | 4 +- .../ZUGFeRD/LineCalculator.java | 6 ++- .../ZUGFeRD/CalculationTest.java | 40 +++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index aaa14ba2..ab42ba51 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -124,7 +124,9 @@ public class Charge implements IZUGFeRDAllowanceCharge { if(totalAmount != null) { return totalAmount; } else if (percent!=null) { - return currentItem.getValue().multiply(getPercent().divide(new BigDecimal(100))); + BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100)))); + BigDecimal singlePriceDiff=currentItem.getValue().subtract(singlePrice); + return singlePriceDiff; } else { throw new RuntimeException("percent must be set"); } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index d7e010cb..632c1d66 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -20,12 +20,14 @@ public class LineCalculator { if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) { for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) { - addAllowance(allowance.getTotalAmount(currentItem)); + BigDecimal singleAllowance=allowance.getTotalAmount(currentItem); + addAllowance(singleAllowance.multiply(currentItem.getQuantity())); } } if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) { for (IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) { - addCharge(charge.getTotalAmount(currentItem)); + BigDecimal singleCharge=charge.getTotalAmount(currentItem); + addCharge(singleCharge.multiply(currentItem.getQuantity())); } } if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 73499123..55881ba7 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -223,6 +223,46 @@ public class CalculationTest extends ResourceCase { assertEquals(valueOf(101.86).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros()); } + public void testSimpleItemPercentAllowance() { + 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()); + } + + /** * LineCalculator should not throw an exception when calculating a non-terminating decimal expansion * */ From a4046d2861cf09d89835c4c899e68f130edf1c23 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 12 May 2025 15:35:47 +0200 Subject: [PATCH 02/23] prevent arithmetricexception --- library/src/main/java/org/mustangproject/Charge.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index ab42ba51..cea018c1 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -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 @@ -124,7 +125,7 @@ public class Charge implements IZUGFeRDAllowanceCharge { if(totalAmount != null) { return totalAmount; } else if (percent!=null) { - BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100)))); + BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100))), 18, RoundingMode.HALF_UP); BigDecimal singlePriceDiff=currentItem.getValue().subtract(singlePrice); return singlePriceDiff; } else { From df28d4f2491300810510477123a3a6cef972a783 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Wed, 14 May 2025 09:06:06 +0200 Subject: [PATCH 03/23] wip --- .../ZUGFeRD/LineCalculator.java | 21 +++++++--- .../ZUGFeRD/CalculationTest.java | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 632c1d66..e7ce588d 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -20,14 +20,24 @@ public class LineCalculator { if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) { for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) { + BigDecimal factor=BigDecimal.ONE; BigDecimal singleAllowance=allowance.getTotalAmount(currentItem); - addAllowance(singleAllowance.multiply(currentItem.getQuantity())); + if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) { + factor=allowance.getPercent().divide(new BigDecimal(100), 18, RoundingMode.HALF_UP); + } + addAllowance(singleAllowance.multiply(factor)); + } } if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) { for (IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) { + BigDecimal factor=BigDecimal.ONE; BigDecimal singleCharge=charge.getTotalAmount(currentItem); - addCharge(singleCharge.multiply(currentItem.getQuantity())); + if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) { + factor=charge.getPercent().divide(new BigDecimal(100), 18, RoundingMode.HALF_UP).multiply(currentItem.getQuantity()); + } + addCharge(singleCharge.multiply(factor)); + } } if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) { @@ -52,9 +62,10 @@ public class LineCalculator { } price=currentItem.getPrice(); - BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); - delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); - priceGross=currentItem.getPrice().add(delta); + priceGross=price; +// BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); +// delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); +// priceGross=currentItem.getPrice().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 diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 55881ba7..3a48ff1d 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -262,6 +262,45 @@ public class CalculationTest extends ResourceCase { assertEquals(new BigDecimal(5), calculator.getGrandTotal().stripTrailingZeros()); } + public void testSimpleItemTotalAllowance() { + 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 From beb5241de690582936d43e4f9f2249f03ce6cbe2 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 15 May 2025 09:45:40 +0200 Subject: [PATCH 04/23] still need to support basisamount --- .../main/java/org/mustangproject/ZUGFeRD/LineCalculator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index e7ce588d..5fc826a1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -22,8 +22,9 @@ public class LineCalculator { for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) { BigDecimal factor=BigDecimal.ONE; BigDecimal singleAllowance=allowance.getTotalAmount(currentItem); + if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) { - factor=allowance.getPercent().divide(new BigDecimal(100), 18, RoundingMode.HALF_UP); + factor=currentItem.getQuantity(); } addAllowance(singleAllowance.multiply(factor)); @@ -34,7 +35,7 @@ public class LineCalculator { BigDecimal factor=BigDecimal.ONE; BigDecimal singleCharge=charge.getTotalAmount(currentItem); if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) { - factor=charge.getPercent().divide(new BigDecimal(100), 18, RoundingMode.HALF_UP).multiply(currentItem.getQuantity()); + factor=currentItem.getQuantity(); } addCharge(singleCharge.multiply(factor)); From b6e2c2ea2fc4ea5944754530034fbf5f5c512982 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 15 May 2025 10:23:13 +0200 Subject: [PATCH 05/23] read basisamount --- .../main/java/org/mustangproject/Charge.java | 20 +++++++++++ .../main/java/org/mustangproject/Item.java | 36 ++++++++++--------- .../ZUGFeRD/IZUGFeRDAllowanceCharge.java | 6 ++++ .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 9 +++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index cea018c1..ee32facc 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -24,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 */ @@ -104,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; diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 956979fd..7549590a 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -97,7 +97,6 @@ public class Item implements IZUGFeRDExportableItem { //icnm.getNode("AdditionalItemProperty").flatMap(n ->n.getAttributes()).ifPresent(product::setAttributes); - icnm.getAsNodeMap("ClassifiedTaxCategory").flatMap(m -> m.getAsBigDecimal("Percent")) .ifPresent(product::setVATPercent); }); @@ -119,7 +118,7 @@ public class Item implements IZUGFeRDExportableItem { }); itemMap.getAllNodes("DocumentReference").map(ReferencedDocument::fromNode) - .forEach(this::addAdditionalReference); + .forEach(this::addAdditionalReference); // ubl @@ -136,8 +135,6 @@ public class Item implements IZUGFeRDExportableItem { .ifPresent(this::addNote); - - itemMap.getAsNodeMap("SpecifiedLineTradeAgreement", "SpecifiedSupplyChainTradeAgreement").ifPresent(icnm -> { icnm.getAsNodeMap("BuyerOrderReferencedDocument") .flatMap(bordNodes -> bordNodes.getAsString("LineID")) @@ -178,24 +175,28 @@ public class Item implements IZUGFeRDExportableItem { .ifPresent(product::setTaxExemptionReason); icnm.getAllNodes("SpecifiedTradeAllowanceCharge").map(NodeMap::new).forEach(stac -> { stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> { - String isChargeString=ci.getAsString("Indicator").get(); - String percentString=stac.getAsStringOrNull("CalculationPercent"); - String amountString=stac.getAsStringOrNull("ActualAmount"); - String reason=stac.getAsStringOrNull("Reason"); - Charge izac= new Charge(); + String isChargeString = ci.getAsString("Indicator").get(); + 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(); } else { izac = new Charge(); } - if (amountString!=null) { + if (amountString != null) { izac.setTotalAmount(new BigDecimal(amountString)); } - if(percentString!=null) { - izac.setPercent(new BigDecimal(percentString)); + if (basisAmountString != null) { + izac.setBasisAmount(new BigDecimal(basisAmountString)); } - if(reason!=null) { - izac.setReason(reason); + if (percentString != null) { + izac.setPercent(new BigDecimal(percentString)); + } + if (reason != null) { + izac.setReason(reason); } if (isChargeString.equalsIgnoreCase("false")) { @@ -275,7 +276,7 @@ public class Item implements IZUGFeRDExportableItem { } public Item setNotesWithSubjectCode(List theList) { - includedNotes=theList; + includedNotes = theList; return this; } @@ -373,18 +374,19 @@ public class Item implements IZUGFeRDExportableItem { * jackson convenience method */ public void setItemAllowances(ArrayList theAllowances) { - if (theAllowances!=null) { + if (theAllowances != null) { Allowances.clear(); for (Allowance theAllowance : theAllowances) { Allowances.add(theAllowance); } } } + /*** * jackson convenience method */ public void setItemCharges(ArrayList theCharges) { - if (theCharges!=null) { + if (theCharges != null) { Charges.clear(); for (Charge theCharge : theCharges) { Charges.add(theCharge); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDAllowanceCharge.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDAllowanceCharge.java index 0c61ba5b..bcdab413 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDAllowanceCharge.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDAllowanceCharge.java @@ -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 diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 3015f91c..699dc4f3 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -962,6 +962,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; @@ -989,6 +990,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")) { @@ -1016,6 +1019,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)); @@ -1028,6 +1034,9 @@ public class ZUGFeRDInvoiceImporter { if (taxPercent != null) { a.setTaxPercent(new BigDecimal(taxPercent)); } + if (basisAmount != null) { + a.setBasisAmount(new BigDecimal(basisAmount)); + } zpp.addAllowance(a); } From 6f1f3fa2d2d43f2682315263b0e92df7b7ffc4c5 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 15 May 2025 10:40:42 +0200 Subject: [PATCH 06/23] corrected parsing (our totalAmount!=actualAmount) --- library/src/main/java/org/mustangproject/Item.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 7549590a..c248d2fe 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -188,6 +188,9 @@ 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)); From d0c213b495babe1c9fcba1fe83f5d51f8ca3bb9f Mon Sep 17 00:00:00 2001 From: jstaerk Date: Wed, 21 May 2025 10:09:41 +0200 Subject: [PATCH 07/23] correcting amounts --- .../main/java/org/mustangproject/ZUGFeRD/LineCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index c58e177b..0e7981cb 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -72,7 +72,7 @@ public class LineCalculator { ? BigDecimal.ONE.setScale(4) : currentItem.getBasisQuantity(); itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP) - .subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); + .subtract(allowanceItemTotal).subtract(allowance).add(charge).setScale(2, RoundingMode.HALF_UP); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); } From dea5372688bd4491a69b914209f26ed1523d0210 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Wed, 21 May 2025 12:20:38 +0200 Subject: [PATCH 08/23] failing tests down from 10 to 7 --- .../mustangproject/ZUGFeRD/LineCalculator.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 0e7981cb..77771e5b 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -8,13 +8,13 @@ import java.math.RoundingMode; * @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 allowance = BigDecimal.ZERO; + protected BigDecimal charge = BigDecimal.ZERO; + protected BigDecimal allowanceItemTotal = BigDecimal.ZERO; public LineCalculator(IZUGFeRDExportableItem currentItem) { @@ -63,6 +63,7 @@ public class LineCalculator { price=currentItem.getPrice(); priceGross=price; + price=price.subtract(allowance).add(charge); // BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); // delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); // priceGross=currentItem.getPrice().add(delta); @@ -72,7 +73,7 @@ public class LineCalculator { ? BigDecimal.ONE.setScale(4) : currentItem.getBasisQuantity(); itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP) - .subtract(allowanceItemTotal).subtract(allowance).add(charge).setScale(2, RoundingMode.HALF_UP); + .subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); } From 220eaaff7a0c829ab68583744cba978846b3d3ec Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 22 May 2025 07:01:55 +0200 Subject: [PATCH 09/23] from 7 to 6 errors --- .../ZUGFeRD/LineCalculator.java | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 77771e5b..a5ffc3da 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -12,8 +12,10 @@ public class LineCalculator { protected BigDecimal priceGross; protected BigDecimal itemTotalNetAmount; protected BigDecimal itemTotalVATAmount; - protected BigDecimal allowance = BigDecimal.ZERO; - protected BigDecimal charge = BigDecimal.ZERO; + 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) { @@ -22,11 +24,12 @@ public class LineCalculator { for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) { BigDecimal factor=BigDecimal.ONE; BigDecimal singleAllowance=allowance.getTotalAmount(currentItem); + addItemAllowance(singleAllowance); if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) { factor=currentItem.getQuantity(); } - addAllowance(singleAllowance.multiply(factor)); + addLineAllowance(singleAllowance.multiply(factor)); } } @@ -34,10 +37,11 @@ public class LineCalculator { for (IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) { BigDecimal factor=BigDecimal.ONE; BigDecimal singleCharge=charge.getTotalAmount(currentItem); + addItemCharge(singleCharge); if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) { factor=currentItem.getQuantity(); } - addCharge(singleCharge.multiply(factor)); + addLineCharge(singleCharge.multiply(factor)); } } @@ -63,7 +67,7 @@ public class LineCalculator { price=currentItem.getPrice(); priceGross=price; - price=price.subtract(allowance).add(charge); + price=price.subtract(itemAllowance).add(itemCharge); // BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); // delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); // priceGross=currentItem.getPrice().add(delta); @@ -97,12 +101,20 @@ 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) { From c312e14dd2253872e4f310cd417ccd11d1e7c7bf Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 22 May 2025 08:03:15 +0200 Subject: [PATCH 10/23] reapply test --- .../ZUGFeRD/CalculationTest.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index fc3a7041..f82bbd72 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -81,10 +81,13 @@ public class CalculationTest extends ResourceCase { @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()); + - */ } @@ -225,6 +228,9 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250 } 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(); @@ -264,6 +270,9 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250 } 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(); From b6160f9f89aa04f65594c78b91b517535cff9d08 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 22 May 2025 10:08:34 +0200 Subject: [PATCH 11/23] corrected calculation, test --- .../main/java/org/mustangproject/ZUGFeRD/LineCalculator.java | 4 ++-- .../org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index a5ffc3da..bedfd67f 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -76,8 +76,8 @@ public class LineCalculator { 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); + itemTotalNetAmount = quantity.multiply(priceGross).divide(basisQuantity, 18, RoundingMode.HALF_UP) + .add(lineCharge).subtract(lineAllowance).setScale(2, RoundingMode.HALF_UP); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 3323c168..4d39cd98 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -264,7 +264,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() { From c69867c55ee0bd5f2bb65e2b49967c1291af2276 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Wed, 28 May 2025 14:22:56 +0200 Subject: [PATCH 12/23] corrected a test, corrected calculation --- .../main/java/org/mustangproject/Charge.java | 3 +- .../mustangproject/ZUGFeRD/ZF2PushTest.java | 31 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index ee32facc..2e928fe5 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -145,7 +145,8 @@ public class Charge implements IZUGFeRDAllowanceCharge { 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().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 { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 10f28a3a..16a815bb 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -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,7 +255,7 @@ 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"))) @@ -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.33", 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()); From c0c9ad1405a6aa0a62e51f5c159fd27ec424fbb5 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 5 Jun 2025 14:04:37 +0200 Subject: [PATCH 13/23] changes for new feature --- .../main/java/org/mustangproject/Product.java | 40 +++++++++++++++++++ .../ZUGFeRD/IZUGFeRDExportableItem.java | 25 +++++++++++- .../ZUGFeRD/IZUGFeRDExportableProduct.java | 19 +++++++++ .../mustangproject/ZUGFeRD/ZF2PushTest.java | 2 +- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Product.java b/library/src/main/java/org/mustangproject/Product.java index 4f0397ee..93e48a79 100644 --- a/library/src/main/java/org/mustangproject/Product.java +++ b/library/src/main/java/org/mustangproject/Product.java @@ -32,6 +32,8 @@ public class Product implements IZUGFeRDExportableProduct { protected boolean isIntraCommunitySupply = false; protected SchemedID globalId = null; protected String countryOfOrigin = null; + protected ArrayList charges=new ArrayList<>(); + protected ArrayList allowances=new ArrayList<>(); protected HashMap attributes = new HashMap<>(); protected List 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); + } + + } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java index 6d68cdff..1dee20c2 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java @@ -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; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java index ae9eb5b8..6ee5f22e 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java @@ -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 * diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 16a815bb..a9d13a02 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -628,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()); From 56ca021e7789bf1c7bd59baa2e56a2339f2fcd7e Mon Sep 17 00:00:00 2001 From: jstaerk Date: Fri, 6 Jun 2025 14:59:56 +0200 Subject: [PATCH 14/23] take product allowance/charge into consideration --- .../mustangproject/ZUGFeRD/LineCalculator.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index bedfd67f..f6a9ebea 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -2,6 +2,8 @@ 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. @@ -70,7 +72,20 @@ public class LineCalculator { price=price.subtract(itemAllowance).add(itemCharge); // BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); // delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); -// priceGross=currentItem.getPrice().add(delta); + + 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 From 1a699a63af1b10db53754d1504e8d5793ec31c9a Mon Sep 17 00:00:00 2001 From: jstaerk Date: Fri, 6 Jun 2025 15:51:19 +0200 Subject: [PATCH 15/23] distinguish in writing between item and product discounts/charges --- .../main/java/org/mustangproject/Item.java | 11 +++++++++ .../ZUGFeRD/LineCalculator.java | 13 +++++++---- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 23 +++++++++++++------ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 07067e94..247f2e3b 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -295,6 +295,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) @@ -468,6 +478,7 @@ public class Item implements IZUGFeRDExportableItem { * @return fluent setter */ public Item addAllowance(IZUGFeRDAllowanceCharge izac) { + Allowances.add(izac); return this; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index f6a9ebea..08702773 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -31,7 +31,7 @@ public class LineCalculator { if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) { factor=currentItem.getQuantity(); } - addLineAllowance(singleAllowance.multiply(factor)); + addAllowanceItemTotal(singleAllowance.multiply(factor)); } } @@ -43,7 +43,7 @@ public class LineCalculator { if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) { factor=currentItem.getQuantity(); } - addLineCharge(singleCharge.multiply(factor)); + subtractAllowanceItemTotal(singleCharge.multiply(factor)); } } @@ -69,7 +69,7 @@ public class LineCalculator { price=currentItem.getPrice(); priceGross=price; - price=price.subtract(itemAllowance).add(itemCharge); +// price=price.subtract(itemAllowance).add(itemCharge); // BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance); // delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP); @@ -91,8 +91,8 @@ public class LineCalculator { BigDecimal basisQuantity = currentItem.getBasisQuantity().compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ONE.setScale(4) : currentItem.getBasisQuantity(); - itemTotalNetAmount = quantity.multiply(priceGross).divide(basisQuantity, 18, RoundingMode.HALF_UP) - .add(lineCharge).subtract(lineAllowance).setScale(2, RoundingMode.HALF_UP); + itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP) + .add(lineCharge).subtract(lineAllowance).subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); } @@ -135,5 +135,8 @@ public class LineCalculator { public void addAllowanceItemTotal(BigDecimal b) { allowanceItemTotal = allowanceItemTotal.add(b); } + public void subtractAllowanceItemTotal(BigDecimal b) { + allowanceItemTotal = allowanceItemTotal.subtract(b); + } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 8fe07564..75356d86 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -432,24 +432,29 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + ""; } 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 += "" + XMLTools.encodeXML(currentItem.getProduct().getName()) + ""; if (currentItem.getProduct().getDescription() != null && currentItem.getProduct().getDescription().length() > 0) { @@ -540,6 +545,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + "" + vatFormat(currentItem.getProduct().getVATPercent()) + "" + ""; + // item charges/allowances + if (!itemTotalAllowanceChargeStr.isEmpty()) { + xml += ""+ itemTotalAllowanceChargeStr +""; + } + if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) { xml += ""; if (currentItem.getDetailedDeliveryPeriodFrom() != null) { @@ -551,7 +561,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += ""; } - xml += itemTotalAllowanceChargeStr; xml += "" + "" + currencyFormat(lc.getItemTotalNetAmount()) From 17b6a7be5ac97b07b7e1553c31c2a2085451e755 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Sat, 7 Jun 2025 14:19:34 +0200 Subject: [PATCH 16/23] corrected test --- .../src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index a9d13a02..6b7490ad 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -644,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 { From 58f153fc46bb74eec141fd56ea79909cdc207767 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Sat, 7 Jun 2025 14:55:29 +0200 Subject: [PATCH 17/23] corrected writing --- .../main/java/org/mustangproject/Item.java | 13 ----- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 2 +- .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 48 ------------------- 3 files changed, 1 insertion(+), 62 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 247f2e3b..6d57722d 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -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); }); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 75356d86..008f99d2 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -547,7 +547,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + ""; // item charges/allowances if (!itemTotalAllowanceChargeStr.isEmpty()) { - xml += ""+ itemTotalAllowanceChargeStr +""; + xml += itemTotalAllowanceChargeStr ; } if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 19a0084f..180d64bb 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -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,31 +398,8 @@ 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); zpp.setPayee(new TradeParty(ublPayeeNodes)); -// TradeParty payee =new TradeParty(); -// NodeMap payeeID = new NodeMap(UBLpayeeNodes).getAsNodeMap("PartyIdentification").get(); -// if (payeeID !=null) { -// payeeID.getAsString("Name").ifPresent(t->payee.setName(t)); -// } -// if (payeeNodes != null) { -// TradeParty payee =new TradeParty(); -// NodeMap nodeMap = new NodeMap(payeeNodes).getAsNodeMap("PartyIdentification").get(); -// if (nodeMap != null) { -// nodeMap.getNode("ID").ifPresent(s -> { -// SchemedID sID = new SchemedID().setScheme(s.getAttributes().getNamedItem("schemeID").getTextContent()).setId(s.getTextContent()); -// payee.addGlobalID(sID); -// }); -// } -// } xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]"); @@ -834,9 +789,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(); From da29a92e81f0883a7afb3e9524fe59f193ae16ca Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 9 Jun 2025 15:21:06 +0200 Subject: [PATCH 18/23] corrected calculation --- .../java/org/mustangproject/Allowance.java | 18 ++++++++++++++++++ .../main/java/org/mustangproject/Charge.java | 6 +++--- .../mustangproject/ZUGFeRD/LineCalculator.java | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/library/src/main/java/org/mustangproject/Allowance.java b/library/src/main/java/org/mustangproject/Allowance.java index e378f767..d8b5f3fe 100644 --- a/library/src/main/java/org/mustangproject/Allowance.java +++ b/library/src/main/java/org/mustangproject/Allowance.java @@ -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 diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index 2e928fe5..c906a704 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -145,9 +145,9 @@ public class Charge implements IZUGFeRDAllowanceCharge { 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); + 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"); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 08702773..5cff3f9b 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -93,7 +93,7 @@ public class LineCalculator { : currentItem.getBasisQuantity(); itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP) .add(lineCharge).subtract(lineAllowance).subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); - itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); + itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator).setScale(2, RoundingMode.HALF_UP); } public BigDecimal getPrice() { From 7668622d5e7dd054039a596da8bbb2c22e172eb6 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 9 Jun 2025 15:21:23 +0200 Subject: [PATCH 19/23] corrected two tests --- .../org/mustangproject/ZUGFeRD/CalculationTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index f82bbd72..43511706 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -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.18).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); } @Test @@ -74,9 +74,9 @@ 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.94).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); } @Test From 21ddec633bcd7ab16cb9f07df96cfcfc67cbcf7a Mon Sep 17 00:00:00 2001 From: jstaerk Date: Mon, 9 Jun 2025 15:33:52 +0200 Subject: [PATCH 20/23] corrected some more tests --- .../test/java/org/mustangproject/ZUGFeRD/CalculationTest.java | 2 +- .../java/org/mustangproject/ZUGFeRD/DeSerializationTest.java | 2 +- .../src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 43511706..3c79d79f 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -224,7 +224,7 @@ public class CalculationTest extends ResourceCase { 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() { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java index bb41fb5a..fd0e7b97 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java @@ -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); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 6b7490ad..67e7b6aa 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -283,7 +283,7 @@ public class ZF2PushTest extends TestCase { assertTrue(zi.getUTF8().contains("ABK")); // Reading ZUGFeRD - assertEquals("18.33", zi.getAmount()); + assertEquals("18.92", zi.getAmount()); assertEquals(orgname, zi.getHolder()); assertEquals(number, zi.getForeignReference()); assertEquals(zi.getVersion(), 2); @@ -688,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 { From ed72a5667814b551c503fba84bebe78eddb81c21 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 10 Jun 2025 07:50:36 +0200 Subject: [PATCH 21/23] corrected output xml --- .../java/org/mustangproject/ZUGFeRD/LineCalculator.java | 2 +- .../org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 8 ++++---- .../java/org/mustangproject/ZUGFeRD/CalculationTest.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 5cff3f9b..d5f06a9a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -93,7 +93,7 @@ public class LineCalculator { : currentItem.getBasisQuantity(); itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP) .add(lineCharge).subtract(lineAllowance).subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); - itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator).setScale(2, RoundingMode.HALF_UP); + itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);//.setScale(2, RoundingMode.HALF_UP); } public BigDecimal getPrice() { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 008f99d2..21321e92 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -545,10 +545,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + "" + vatFormat(currentItem.getProduct().getVATPercent()) + "" + ""; - // item charges/allowances - if (!itemTotalAllowanceChargeStr.isEmpty()) { - xml += itemTotalAllowanceChargeStr ; - } if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) { xml += ""; @@ -561,6 +557,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += ""; } + // item charges/allowances + if (!itemTotalAllowanceChargeStr.isEmpty()) { + xml += itemTotalAllowanceChargeStr ; + } xml += "" + "" + currencyFormat(lc.getItemTotalNetAmount()) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 3c79d79f..7fc94f5f 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -56,7 +56,7 @@ public class CalculationTest extends ResourceCase { assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros()); assertEquals(valueOf(1769.89).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros()); - assertEquals(valueOf(283.18).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); + assertEquals(valueOf(283.1824).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); } @Test @@ -76,7 +76,7 @@ public class CalculationTest extends ResourceCase { assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros()); assertEquals(valueOf(1799.63).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros()); - assertEquals(valueOf(287.94).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); + assertEquals(valueOf(287.9408).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros()); } @Test From f36be3eaf60ceb87e059ba17bb30a260b3c0d17e Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 10 Jun 2025 08:32:59 +0200 Subject: [PATCH 22/23] corrected inclusion of reasons and reason codes --- .../org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 5 +++-- .../test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 21321e92..504e5cf9 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -320,11 +320,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 = "" + XMLTools.encodeXML(allowance.getReason()) + ""; } String reasonCode = ""; - if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("XRechnung"))) { + if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("Extended") || isEN16931)) { // only in XRechnung profile reasonCode = "" + allowance.getReasonCode() + ""; } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 67e7b6aa..cfb6f446 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -261,10 +261,10 @@ public class ZF2PushTest extends TestCase { .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); From 19564d14edb1004c469a812a75cc89fa6cfad677 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Tue, 10 Jun 2025 09:57:03 +0200 Subject: [PATCH 23/23] corrected a test --- .../src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index cfb6f446..9c60b75b 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -674,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("