Skip to content

Commit

Permalink
Merge branch 'ZUGFeRD:master' into position-allowance
Browse files Browse the repository at this point in the history
  • Loading branch information
InnuceEAN authored Nov 27, 2024
2 parents 2a8d448 + 510aa11 commit af6563c
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 25 deletions.
13 changes: 13 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
2.15.1
=======
- #566
- have a bean contructor for direct debit
-? lineTotalAmount is null
? be able to access ID in error message
? log error IDs
- closes #579
- #581
- #576
- #578
- log error ids

2.15.0
=======
2024-11-18
Expand Down
15 changes: 14 additions & 1 deletion library/src/main/java/org/mustangproject/CalculatedInvoice.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

public class CalculatedInvoice extends Invoice implements Serializable {

protected BigDecimal grandTotal=null;
protected BigDecimal grandTotal=null;
protected BigDecimal lineTotalAmount=null;

public void calculate() {
TransactionCalculator tc=new TransactionCalculator(this);
grandTotal=tc.getGrandTotal();
lineTotalAmount=tc.getValue();
}
public BigDecimal getGrandTotal() {
if (grandTotal==null) {
Expand All @@ -27,4 +29,15 @@ public CalculatedInvoice setGrandTotal(BigDecimal grand) {
grandTotal=grand;
return this;
}
public BigDecimal getLineTotalAmount() {
if (lineTotalAmount==null) {
calculate();
}
return lineTotalAmount;
}
public CalculatedInvoice setLineTotalAmount(BigDecimal total) {
lineTotalAmount=total;
return this;
}

}
4 changes: 2 additions & 2 deletions library/src/main/java/org/mustangproject/DirectDebit.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ public class DirectDebit implements IZUGFeRDTradeSettlementDebit {
/**
* Debited account identifier (BT-91)
*/
protected final String IBAN;
protected String IBAN;

/**
* Mandate reference identifier (BT-89)
*/
protected final String mandate;
protected String mandate;

/**
* bean constructor
Expand Down
3 changes: 3 additions & 0 deletions library/src/main/java/org/mustangproject/Item.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public Item(NodeList itemChilds, boolean recalcPrice) {
icnm.getAsNodeMap("ClassifiedTaxCategory").flatMap(m -> m.getAsBigDecimal("Percent"))
.ifPresent(product::setVATPercent);
});
itemMap.getAsNodeMap("AssociatedDocumentLineDocument").ifPresent(icnm -> {
icnm.getAsString("LineID").ifPresent(this::setId);
});

itemMap.getAsNodeMap("Price").ifPresent(icnm -> {
// ubl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,12 @@ default IZUGFeRDAllowanceCharge[] getItemTotalAllowances() {
return null;
}


/***
*
* @return the line ID
*/
default String getId() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -401,14 +401,18 @@ public void generateXML(IExportableTransaction trans) {
int lineID = 0;
for (final IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
lineID++;
String lineIDStr = Integer.toString(lineID);
if (currentItem.getId()!=null) {
lineIDStr=currentItem.getId();
}
if (currentItem.getProduct().getTaxExemptionReason() != null) {
exemptionReason = "<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
final LineCalculator lc = new LineCalculator(currentItem);
if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) {
xml += "<ram:IncludedSupplyChainTradeLineItem>" +
"<ram:AssociatedDocumentLineDocument>"
+ "<ram:LineID>" + lineID + "</ram:LineID>"
+ "<ram:LineID>" + lineIDStr + "</ram:LineID>"
+ buildItemNotes(currentItem)
+ "</ram:AssociatedDocumentLineDocument>"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,21 @@ public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseEx
}
}

xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]");
xpr = xpath.compile("//*[local-name()=\"TotalPrepaidAmount\"]|//*[local-name()=\"PrepaidAmount\"]");
NodeList prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (prepaidNodes.getLength() > 0) {
zpp.setTotalPrepaidAmount(new BigDecimal(XMLTools.trimOrNull(prepaidNodes.item(0))));
}


xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"LineTotalAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"LineExtensionAmount\"]");
NodeList lineTotalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (lineTotalNodes.getLength() > 0) {
if (zpp instanceof CalculatedInvoice) {
((CalculatedInvoice) zpp).setLineTotalAmount(new BigDecimal(XMLTools.trimOrNull(lineTotalNodes.item(0))));
}
}

Date issueDate = null;
Date dueDate = null;
Date deliveryDate = null;
Expand Down Expand Up @@ -628,9 +637,9 @@ public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseEx

xpr = xpath.compile("//*[local-name()=\"BuyerReference\"]");
String buyerReference = null;
prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (prepaidNodes.getLength() > 0) {
buyerReference = XMLTools.trimOrNull(prepaidNodes.item(0));
lineTotalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (lineTotalNodes.getLength() > 0) {
buyerReference = XMLTools.trimOrNull(lineTotalNodes.item(0));
}
if (buyerReference != null) {
zpp.setReferenceNumber(buyerReference);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@
import org.junit.runners.MethodSorters;
import org.mustangproject.*;

import javax.xml.xpath.XPathExpressionException;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.ParseException;
import java.util.Date;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DeSerializationTest extends TestCase {
public class DeSerializationTest extends ResourceCase {
public void testJackson() throws JsonProcessingException {

ObjectMapper mapper = new ObjectMapper();
Expand All @@ -50,6 +56,33 @@ public void testJackson() throws JsonProcessingException {

}

public void testInvoiceLine() throws JsonProcessingException {
File inputCII = getResourceAsFile("factur-x.xml");
boolean hasExceptions = false;

ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
try {
zii.fromXML(new String(Files.readAllBytes(inputCII.toPath()), StandardCharsets.UTF_8));

} catch (IOException e) {
hasExceptions = true;
}

CalculatedInvoice ci=new CalculatedInvoice();
try {
zii.extractInto(ci);
} catch (XPathExpressionException e) {
hasExceptions = true;
} catch (ParseException e) {
hasExceptions = true;
}
ObjectMapper mapper = new ObjectMapper();
String jsonArray = mapper.writeValueAsString(ci);
assertFalse(hasExceptions);
assertTrue(jsonArray.contains("lineTotalAmount"));

}

public void testAllowanceRead() throws JsonProcessingException {

ObjectMapper mapper = new ObjectMapper();
Expand Down
22 changes: 11 additions & 11 deletions library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ public class ZF2PushTest extends TestCase {
final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf";

public void testPushExport() {
/***
* This writes to a filename like an official sample, please consider when changing (probably better not?)
*/
/***
* This writes to a filename like an official sample, please consider when changing (probably better not?)
*/
// the writing part
String orgname = "Bei Spiel GmbH";
String number = "RE-20201121/508";
Expand Down Expand Up @@ -96,8 +96,8 @@ public void testPushExport() {
fail("Exception should not be raised");
}

ZUGFeRDInvoiceImporter zii=new ZUGFeRDInvoiceImporter(TARGET_PDF);
Invoice i=new Invoice();
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PDF);
Invoice i = new Invoice();
try {
zii.extractInto(i);
} catch (XPathExpressionException e) {
Expand Down Expand Up @@ -164,15 +164,15 @@ public void testAttachmentsExport() {
fail("IOException should not be raised");
}
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_ATTACHMENTSPDF);
Invoice i= null;
Invoice i = null;
try {
i = zii.extractInvoice();
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
assertEquals(senderDescription,i.getSender().getDescription());
assertEquals(senderDescription, i.getSender().getDescription());

// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_ATTACHMENTSPDF);
Expand Down Expand Up @@ -302,7 +302,7 @@ public void testIntraCommunitySupplyItemExport() {
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));

ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815").addTaxID ("4711"))
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815").addTaxID("4711"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816")
.setContact(new Contact("contact testname", "123456", "[email protected]").setFax("0911623562")))
.setDeliveryAddress(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816"))
Expand Down Expand Up @@ -372,7 +372,7 @@ public void testIntraCommunitySupplyManualExport() {
fail("IOException should not be raised");
}
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_INTRACOMMUNITYSUPPLYMANUALPDF);
Invoice i= null;
Invoice i = null;
try {
i = zii.extractInvoice();
} catch (XPathExpressionException e) {
Expand Down Expand Up @@ -530,7 +530,7 @@ public void testPushEdge() {
.setContractReferencedDocument(contractID)
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addGlobalID(gln).setEmail("[email protected]").addVATID("DE4711")
.setContact(new Contact("Franz Müller", "01779999999", "[email protected]", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")).setAdditionalAddress("Hinterhaus 3"))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
.addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).setId("a123").addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
.addCashDiscount(new CashDiscount(new BigDecimal(2), 14))
Expand Down Expand Up @@ -562,7 +562,7 @@ public void testPushEdge() {
assertTrue(zi.getUTF8().contains(occurrenceFrom));
assertTrue(zi.getUTF8().contains(occurrenceTo));
assertTrue(zi.getUTF8().contains(contractID));

assertEquals(zi.importedInvoice.getZFItems()[0].getId(), "a123");
assertTrue(zi.getUTF8().contains("20200113")); // to contain item delivery periods
assertTrue(zi.getUTF8().contains("20200115")); // to contain item delivery periods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,31 @@ public void testImportMinimum() {

}

@Test
public void testImportPrepaid() throws XPathExpressionException, ParseException {
InputStream inputStream = this.getClass()
.getResourceAsStream("/EN16931_1_Teilrechnung.pdf");
ZUGFeRDInvoiceImporter importer = new ZUGFeRDInvoiceImporter();
importer.doIgnoreCalculationErrors();
importer.setInputStream(inputStream);

CalculatedInvoice invoice = new CalculatedInvoice();
importer.extractInto(invoice);

boolean isBD=invoice.getTotalPrepaidAmount() instanceof BigDecimal;
assertTrue(isBD);
BigDecimal expectedPrepaid=new BigDecimal(50);
BigDecimal expectedLineTotal=new BigDecimal("180.76");
if (isBD) {
BigDecimal amread=invoice.getTotalPrepaidAmount();
BigDecimal amline=invoice.getLineTotalAmount();
assertTrue(amread.compareTo(expectedPrepaid) == 0);
assertTrue(amline.compareTo(expectedLineTotal) == 0);
}

}


@Test
public void testImportIncludedNotes() throws XPathExpressionException, ParseException {
InputStream inputStream = this.getClass()
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ public void validate() throws IrrecoverableValidationError {
}

// step 2 validate XMP
final ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
final ZUGFeRDImporter zi = new ZUGFeRDImporter();
zi.doIgnoreCalculationErrors();//of course the calculation will still be schematron checked
zi.setInputStream(inputStream);
final String xmp = zi.getXMP();

final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ public String getCSVResult() {
return String.join(",", errorcodes);
}

/***
*
* @return the unique error IDs as comma separated string
*/
public String getCSVIDResult() {
final ArrayList<String> errorIDs = new ArrayList<>();
for (final ValidationResultItem validationResultItem : results) {
if (!validationResultItem.getID().isEmpty()) {
final String errorID=validationResultItem.getID();
errorIDs.add(errorID);

}
}
return String.join(",", errorIDs);
}

public void setInvalid() {
isValid = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class ValidationResultItem {

protected String message, location=null;
protected int section =-1;
protected String id =""; // e.g. "FX-SCH-A-000026"


private ESeverity severity=ESeverity.error;
Expand Down Expand Up @@ -103,6 +104,15 @@ public ESeverity getSeverity() {
return severity;
}

public ValidationResultItem setID(String id) {
this.id=id;
return this;
}

public String getID() {
return id;
}

public int getSection() {
return section;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ public void validateSchematron(String xml, String xsltFilename, int section, ESe

String thisFailText = "";
String thisFailID = "";
String thisFailIDStr = "";
String thisFailTest = "";
String thisFailLocation = "";
if (failedAsserts.getLength() > 0) {
Expand All @@ -461,7 +462,8 @@ public void validateSchematron(String xml, String xsltFilename, int section, ESe
//nodes.item(i).getTextContent())) {
Node currentFailNode = failedAsserts.item(nodeIndex);
if (currentFailNode.getAttributes().getNamedItem("id") != null) {
thisFailID = " [ID " + currentFailNode.getAttributes().getNamedItem("id").getNodeValue() + "]";
thisFailID = currentFailNode.getAttributes().getNamedItem("id").getNodeValue();
thisFailIDStr = " [ID " + thisFailID + "]";
}
if (currentFailNode.getAttributes().getNamedItem("test") != null) {
thisFailTest = currentFailNode.getAttributes().getNamedItem("test").getNodeValue();
Expand Down Expand Up @@ -494,8 +496,8 @@ public void validateSchematron(String xml, String xsltFilename, int section, ESe

LOGGER.info("FailedAssert ", thisFailText);

context.addResultItem(new ValidationResultItem(severity, thisFailText + thisFailID + " from " + xsltFilename + ")")
.setLocation(thisFailLocation).setCriterion(thisFailTest).setSection(section)
context.addResultItem(new ValidationResultItem(severity, thisFailText + thisFailIDStr + " from " + xsltFilename + ")")
.setLocation(thisFailLocation).setCriterion(thisFailTest).setSection(section).setID(thisFailID)
.setPart(EPart.fx));
failedRules++;

Expand Down
Loading

0 comments on commit af6563c

Please sign in to comment.