- make Line Calculation, e.g. total line net amount, accessible via JSON using getCalculation

This commit is contained in:
jstaerk
2025-09-30 19:44:02 +02:00
parent cae963a1ca
commit ec5bbfdc5c
12 changed files with 179 additions and 15 deletions

View File

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IReferencedDocument;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.LineCalculator;
import org.mustangproject.util.NodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

View File

@@ -79,7 +79,7 @@ public class DAPullProvider extends ZUGFeRD2PullProvider {
if (currentItem.getProduct().getTaxExemptionReason() != null) {
// exemptionReason = "<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
final LineCalculator lc = new LineCalculator(currentItem);
final LineCalculator lc = currentItem.getCalculation();
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
"<ram:AssociatedDocumentLineDocument>"
+ "<ram:LineID>" + lineID + "</ram:LineID>"

View File

@@ -205,4 +205,6 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
default String getAccountingReference() {
return null;
}
default LineCalculator getCalculation() {return new LineCalculator(this); };
}

View File

@@ -107,7 +107,7 @@ public class OXPullProvider extends ZUGFeRD2PullProvider {
// exemptionReason = "<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
final LineCalculator lc = new LineCalculator(currentItem);
final LineCalculator lc = currentItem.getCalculation();
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
"<ram:AssociatedDocumentLineDocument>"
+ "<ram:LineID>" + lineID + "</ram:LineID>"

View File

@@ -195,7 +195,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
percent = currentItem.getProduct().getVATPercent();
}
if (percent != null) {
LineCalculator lc = new LineCalculator(currentItem);
LineCalculator lc = currentItem.getCalculation();
VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode);
String reasonText = currentItem.getProduct().getTaxExemptionReason();
@@ -265,7 +265,7 @@ public class TransactionCalculator implements IAbsoluteValueProvider {
}
if (percent != null)
{
final LineCalculator lc = new LineCalculator(currentItem);
final LineCalculator lc = currentItem.getCalculation();
final VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
currentItem.getProduct().getTaxCategoryCode(), vatDueDateTypeCode, percent);
final String reasonText = currentItem.getProduct().getTaxExemptionReason();

View File

@@ -344,7 +344,7 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider {
}
final LineCalculator lc = new LineCalculator(currentItem);
final LineCalculator lc = currentItem.getCalculation();
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
"<ram:AssociatedDocumentLineDocument>"
+ "<ram:LineID>" + lineID + "</ram:LineID>"

View File

@@ -415,7 +415,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
if (currentItem.getId() != null) {
lineIDStr = currentItem.getId();
}
final LineCalculator lc = new LineCalculator(currentItem);
final LineCalculator lc = currentItem.getCalculation();
if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) {
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
"<ram:AssociatedDocumentLineDocument>"

View File

@@ -1146,7 +1146,7 @@ public class ZUGFeRDInvoiceImporter {
try {
moreDetails = " with tax basis " + tc.getTaxBasis() + " and with positions " + tc.getTotal() + " = "
+ Stream.of(tc.trans.getZFItems())
.map(item -> new LineCalculator(item).getItemTotalNetAmount().toPlainString())
.map(item -> item.getCalculation().getItemTotalNetAmount().toPlainString())
.collect(Collectors.joining(" + "));
} catch (Exception ignored) {
}

View File

@@ -35,7 +35,7 @@ public class CalculationTest extends ResourceCase {
.setQuantity(TEN)
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
final LineCalculator calculator = currentItem.getCalculation();
assertEquals(valueOf(100).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1000).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
@@ -54,7 +54,7 @@ public class CalculationTest extends ResourceCase {
.setItemAllowances(new IZUGFeRDAllowanceCharge[]{allowance})
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
final LineCalculator calculator = currentItem.getCalculation();
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1769.89).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
@@ -74,7 +74,7 @@ public class CalculationTest extends ResourceCase {
.setItemCharges(new IZUGFeRDAllowanceCharge[]{charge})
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
final LineCalculator calculator = currentItem.getCalculation();
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1799.63).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
@@ -113,13 +113,13 @@ public class CalculationTest extends ResourceCase {
product.addAllowance(new Allowance(new BigDecimal(1)));
item = new Item(product, new BigDecimal("9.50"), new BigDecimal(25));
item.addCharge(new Charge(new BigDecimal(10)).setReasonCode("ZZZ").setReason("Zuschlag"));
LineCalculator lc = new LineCalculator(item);
LineCalculator lc = item.getCalculation();
assertEquals(new BigDecimal("222.50"), lc.getItemTotalNetAmount());
invoice.addItem(item);
product = new Product("Paper", "", "H87", new BigDecimal(25));
item = new Item(product, new BigDecimal("4.50"), new BigDecimal(15));
item.addAllowance(new Allowance().setPercent(new BigDecimal(5)).setReasonCode("ZZZ").setReason("Zuschlag"));
lc = new LineCalculator(item);
lc = item.getCalculation();
assertEquals(new BigDecimal("64.12"), lc.getItemTotalNetAmount());
invoice.addItem(item);
invoice.addAllowance(new Allowance().setPercent(new BigDecimal(10)).setTaxPercent(new BigDecimal(25)).setReasonCode("ZZZ").setReason("Mengenrabatt"));
@@ -459,7 +459,7 @@ public class CalculationTest extends ResourceCase {
.setQuantity(BigDecimal.valueOf(31))
.setBasisQuantity(BigDecimal.valueOf(366))
.setProduct(product);
final LineCalculator calculator = new LineCalculator(currentItem);
final LineCalculator calculator = currentItem.getCalculation();
assertEquals(BigDecimal.valueOf(32.74), calculator.getItemTotalNetAmount());
}

View File

@@ -479,6 +479,20 @@ public class DeSerializationTest extends ResourceCase {
assertEquals("sender@test.org", fromJSON.getSender().getEmail());
}
public void testItemAbsoluteChargeFromJSON() throws JsonProcessingException {
String globalID = "4000001123452";
String globalIDScheme = "0088";
String json="{\"number\":\"471102\",\"currency\":\"EUR\",\"issueDate\":\"2018-03-04T00:00:00.000+01:00\",\"dueDate\":\"2018-03-04T00:00:00.000+01:00\",\"deliveryDate\":\"2018-03-04T00:00:00.000+01:00\",\"sender\":{\"name\":\"Lieferant GmbH\",\"zip\":\"80333\",\"street\":\"Lieferantenstraße 20\",\"location\":\"München\",\"country\":\"DE\",\"taxID\":\"201/113/40209\",\"vatID\":\"DE123456789\",\"globalID\":\"4000001123452\",\"globalIDScheme\":\"0088\"},\"recipient\":{\"name\":\"Kunden AG Mitte\",\"zip\":\"69876\",\"street\":\"Kundenstraße 15\",\"location\":\"Frankfurt\",\"country\":\"DE\"},\"zfitems\":[{\"price\":9.9,\"quantity\":20,\"product\":{\"unit\":\"H87\",\"name\":\"Trennblätter A4\",\"description\":\"\",\"vatpercent\":19,\"taxCategoryCode\":\"S\"},\"itemCharges\":[{\"totalAmount\":1,\"taxPercent\":19,\"reason\":\"Invoice line charge reason\",\"categoryCode\":\"S\"}]},{\"price\":5.5,\"quantity\":50,\"product\":{\"unit\":\"H87\",\"name\":\"Joghurt Banane\",\"description\":\"\",\"vatpercent\":7,\"taxCategoryCode\":\"S\"}}]}";
ObjectMapper mapper = new ObjectMapper();
Invoice fromJSON = mapper.readValue(json, Invoice.class);
assertEquals(globalID, fromJSON.getSender().getGlobalID());
assertEquals(globalIDScheme, fromJSON.getSender().getGlobalIDScheme());
TransactionCalculator tc=new TransactionCalculator(fromJSON);
assertEquals(new BigDecimal("531.06"), tc.getDuePayable());
}
public void testGrossFromJSON() throws JsonProcessingException {
String json="{ \"documentCode\": \"380\", \"number\": \"123\", \"currency\": \"EUR\", \"paymentTermDescription\": \"Please remit until 28.07.2025\", \"issueDate\": 1753653600000, \"dueDate\": 1753653600000, \"sender\": { \"name\": \"Test company\", \"zip\": \"55232\", \"street\": \"teststr\", \"location\": \"teststadt\", \"country\": \"DE\", \"taxID\": \"4711\", \"vatID\": \"DE0815\", \"vatid\": \"DE0815\" }, \"recipient\": { \"name\": \"Franz Müller\", \"zip\": \"55232\", \"street\": \"teststr.12\", \"location\": \"Entenhausen\", \"country\": \"DE\", \"contact\": { \"name\": \"contact testname\", \"phone\": \"123456\", \"email\": \"contact.testemail@example.org\", \"fax\": \"0911623562\" } }, \"totalPrepaidAmount\": 0.00, \"lineTotalAmount\": 29.00, \"duePayable\": 34.51, \"grandTotal\": 34.51, \"taxBasis\": 29.00, \"valid\": true, \"zfitems\": [ { \"price\": 3.0000, \"quantity\": 10.0000, \"basisQuantity\": 1.0000, \"id\": \"1\", \"product\": { \"unit\": \"H87\", \"name\": \"Testprodukt\", \"taxCategoryCode\": \"S\", \"allowances\": [ { \"totalAmount\": 0.1000, \"categoryCode\": \"S\" } ], \"vatpercent\": 19.00, \"intraCommunitySupply\": false, \"reverseCharge\": false }, \"value\": 3.0000 } ], \"ownVATID\": \"DE0815\", \"ownTaxID\": \"4711\", \"ownLocation\": \"teststadt\", \"ownZIP\": \"55232\", \"ownCountry\": \"DE\", \"ownStreet\": \"teststr\"}";

View File

@@ -350,7 +350,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
assertEquals(version,2);
assertTrue(new BigDecimal("1").compareTo(invoice.getZFItems()[0].getQuantity()) == 0);
LineCalculator lc=new LineCalculator(invoice.getZFItems()[0]);
LineCalculator lc=invoice.getZFItems()[0].getCalculation();
assertTrue(new BigDecimal("1").compareTo(lc.getItemTotalNetAmount()) == 0);
assertEquals("Z", invoice.getZFItems()[0].getProduct().getTaxCategoryCode());
@@ -439,7 +439,153 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
String expectedIssueDate= String.valueOf(morning.toInstant().getEpochSecond() *1000);
String expectedPaymentTermDesciption="Please remit until "+german.format(now);
JSONAssert.assertEquals("{ \"documentCode\": \"380\", \"number\": \"123\", \"currency\": \"EUR\", \"paymentTermDescription\": \""+expectedPaymentTermDesciption+"\", \"issueDate\": "+expectedIssueDate+", \"dueDate\": "+expectedDueDate+", \"sender\": { \"name\": \"Test company\", \"zip\": \"55232\", \"street\": \"teststr\", \"location\": \"teststadt\", \"country\": \"DE\", \"taxID\": \"4711\", \"vatID\": \"DE0815\", \"vatid\": \"DE0815\" }, \"recipient\": { \"name\": \"Franz Müller\", \"zip\": \"55232\", \"street\": \"teststr.12\", \"location\": \"Entenhausen\", \"country\": \"DE\", \"contact\": { \"name\": \"contact testname\", \"phone\": \"123456\", \"email\": \"contact.testemail@example.org\", \"fax\": \"0911623562\" } }, \"totalPrepaidAmount\": 0.00, \"zfitems\": [ { \"price\": 3.0000, \"quantity\": 1.0000, \"basisQuantity\": 1.0000, \"id\": \"1\", \"product\": { \"unit\": \"H87\", \"name\": \"Testprodukt\", \"taxCategoryCode\": \"S\", \"vatpercent\": 19.00, }, \"itemAllowances\": [ { \"totalAmount\": 0.10, \"taxPercent\": 0, \"categoryCode\": \"S\" } ], \"value\": 3.0000 }, { \"price\": 3.0000, \"quantity\": 1.0000, \"basisQuantity\": 1.0000, \"id\": \"2\", \"product\": { \"unit\": \"H87\", \"name\": \"Testprodukt\", \"taxCategoryCode\": \"S\", \"vatpercent\": 19.00,}, \"itemAllowances\": [ { \"percent\": 50.00, \"totalAmount\": 1.5, \"basisAmount\": 3.00, \"taxPercent\": 0, \"reason\": \"In love with salesperson\", \"categoryCode\": \"S\" } ], \"value\": 3.0000 }, { \"price\": 3.0000, \"quantity\": 2.0000, \"basisQuantity\": 1.0000, \"id\": \"3\", \"product\": { \"unit\": \"H87\", \"name\": \"Testprodukt\", \"taxCategoryCode\": \"S\", \"vatpercent\": 19.00}, \"itemCharges\": [ { \"totalAmount\": 1.00, \"taxPercent\": 0, \"reason\": \"AnotherReason\", \"categoryCode\": \"S\" } ], \"value\": 3.0000 }, { \"price\": 3.0000, \"quantity\": 1.0000, \"basisQuantity\": 1.0000, \"id\": \"4\", \"product\": { \"unit\": \"H87\", \"name\": \"Testprodukt\", \"taxCategoryCode\": \"S\", \"vatpercent\": 19.00 }, \"itemCharges\": [ { \"totalAmount\": 1.00, \"taxPercent\": 0, \"reason\": \"Yet another reason\", \"categoryCode\": \"S\" } ], \"itemAllowances\": [ { \"totalAmount\": 1.00, \"taxPercent\": 0, \"reason\": \"Something completely strange\", \"categoryCode\": \"S\" } ], \"value\": 3.0000 } ], \"zfcharges\": [ { \"totalAmount\": 1.00, \"taxPercent\": 19.00, \"reason\": \"AReason\", \"reasonCode\": \"ABK\", \"categoryCode\": \"S\" } ]}",jsonArray,true);
JSONAssert.assertEquals("{\n" +
" \"documentCode\" : \"380\",\n" +
" \"number\" : \"123\",\n" +
" \"currency\" : \"EUR\",\n" +
" \"paymentTermDescription\" : \"Please remit until 30.09.2025\",\n" +
" \"issueDate\" : 1759190400000,\n" +
" \"dueDate\" : 1759190400000,\n" +
" \"sender\" : {\n" +
" \"name\" : \"Test company\",\n" +
" \"zip\" : \"55232\",\n" +
" \"street\" : \"teststr\",\n" +
" \"location\" : \"teststadt\",\n" +
" \"country\" : \"DE\",\n" +
" \"taxID\" : \"4711\",\n" +
" \"vatID\" : \"DE0815\",\n" +
" \"vatid\" : \"DE0815\"\n" +
" },\n" +
" \"recipient\" : {\n" +
" \"name\" : \"Franz Müller\",\n" +
" \"zip\" : \"55232\",\n" +
" \"street\" : \"teststr.12\",\n" +
" \"location\" : \"Entenhausen\",\n" +
" \"country\" : \"DE\",\n" +
" \"contact\" : {\n" +
" \"name\" : \"contact testname\",\n" +
" \"phone\" : \"123456\",\n" +
" \"email\" : \"contact.testemail@example.org\",\n" +
" \"fax\" : \"0911623562\"\n" +
" }\n" +
" },\n" +
" \"totalPrepaidAmount\" : 0.0,\n" +
" \"zfitems\" : [ {\n" +
" \"price\" : 3.0,\n" +
" \"quantity\" : 1.0,\n" +
" \"basisQuantity\" : 1.0,\n" +
" \"id\" : \"1\",\n" +
" \"product\" : {\n" +
" \"unit\" : \"H87\",\n" +
" \"name\" : \"Testprodukt\",\n" +
" \"taxCategoryCode\" : \"S\",\n" +
" \"vatpercent\" : 19.0\n" +
" },\n" +
" \"itemAllowances\" : [ {\n" +
" \"totalAmount\" : 0.1,\n" +
" \"taxPercent\" : 0,\n" +
" \"categoryCode\" : \"S\"\n" +
" } ],\n" +
" \"value\" : 3.0,\n" +
" \"calculation\" : {\n" +
" \"price\" : 3.0,\n" +
" \"priceGross\" : 3.0,\n" +
" \"itemTotalNetAmount\" : 2.9,\n" +
" \"itemTotalVATAmount\" : 0.551,\n" +
" \"itemTotalGrossAmount\" : 2.9\n" +
" }\n" +
" }, {\n" +
" \"price\" : 3.0,\n" +
" \"quantity\" : 1.0,\n" +
" \"basisQuantity\" : 1.0,\n" +
" \"id\" : \"2\",\n" +
" \"product\" : {\n" +
" \"unit\" : \"H87\",\n" +
" \"name\" : \"Testprodukt\",\n" +
" \"taxCategoryCode\" : \"S\",\n" +
" \"vatpercent\" : 19.0\n" +
" },\n" +
" \"itemAllowances\" : [ {\n" +
" \"percent\" : 50.0,\n" +
" \"totalAmount\" : 1.5,\n" +
" \"basisAmount\" : 3.0,\n" +
" \"taxPercent\" : 0,\n" +
" \"reason\" : \"In love with salesperson\",\n" +
" \"categoryCode\" : \"S\"\n" +
" } ],\n" +
" \"value\" : 3.0,\n" +
" \"calculation\" : {\n" +
" \"price\" : 3.0,\n" +
" \"priceGross\" : 3.0,\n" +
" \"itemTotalNetAmount\" : 1.5,\n" +
" \"itemTotalVATAmount\" : 0.285,\n" +
" \"itemTotalGrossAmount\" : 1.5\n" +
" }\n" +
" }, {\n" +
" \"price\" : 3.0,\n" +
" \"quantity\" : 2.0,\n" +
" \"basisQuantity\" : 1.0,\n" +
" \"id\" : \"3\",\n" +
" \"product\" : {\n" +
" \"unit\" : \"H87\",\n" +
" \"name\" : \"Testprodukt\",\n" +
" \"taxCategoryCode\" : \"S\",\n" +
" \"vatpercent\" : 19.0\n" +
" },\n" +
" \"itemCharges\" : [ {\n" +
" \"totalAmount\" : 1.0,\n" +
" \"taxPercent\" : 0,\n" +
" \"reason\" : \"AnotherReason\",\n" +
" \"categoryCode\" : \"S\"\n" +
" } ],\n" +
" \"value\" : 3.0,\n" +
" \"calculation\" : {\n" +
" \"price\" : 3.0,\n" +
" \"priceGross\" : 3.0,\n" +
" \"itemTotalNetAmount\" : 7.0,\n" +
" \"itemTotalVATAmount\" : 1.33,\n" +
" \"itemTotalGrossAmount\" : 7.0\n" +
" }\n" +
" }, {\n" +
" \"price\" : 3.0,\n" +
" \"quantity\" : 1.0,\n" +
" \"basisQuantity\" : 1.0,\n" +
" \"id\" : \"4\",\n" +
" \"product\" : {\n" +
" \"unit\" : \"H87\",\n" +
" \"name\" : \"Testprodukt\",\n" +
" \"taxCategoryCode\" : \"S\",\n" +
" \"vatpercent\" : 19.0\n" +
" },\n" +
" \"itemAllowances\" : [ {\n" +
" \"totalAmount\" : 1.0,\n" +
" \"taxPercent\" : 0,\n" +
" \"reason\" : \"Something completely strange\",\n" +
" \"categoryCode\" : \"S\"\n" +
" } ],\n" +
" \"itemCharges\" : [ {\n" +
" \"totalAmount\" : 1.0,\n" +
" \"taxPercent\" : 0,\n" +
" \"reason\" : \"Yet another reason\",\n" +
" \"categoryCode\" : \"S\"\n" +
" } ],\n" +
" \"value\" : 3.0,\n" +
" \"calculation\" : {\n" +
" \"price\" : 3.0,\n" +
" \"priceGross\" : 3.0,\n" +
" \"itemTotalNetAmount\" : 3.0,\n" +
" \"itemTotalVATAmount\" : 0.57,\n" +
" \"itemTotalGrossAmount\" : 3.0\n" +
" }\n" +
" } ],\n" +
" \"zfcharges\" : [ {\n" +
" \"totalAmount\" : 1.0,\n" +
" \"taxPercent\" : 19.0,\n" +
" \"reason\" : \"AReason\",\n" +
" \"reasonCode\" : \"ABK\",\n" +
" \"categoryCode\" : \"S\"\n" +
" } ]\n" +
"}",jsonArray,true);
} catch (IOException e) {
fail("IOException not expected");
} catch (XPathExpressionException e) {