From 8932646b59dd71fa3d7a1678721b5cae726995c1 Mon Sep 17 00:00:00 2001 From: jstaerk Date: Thu, 28 Nov 2024 14:11:14 +0100 Subject: [PATCH] allow some more JSONing --- History.md | 14 +- .../java/org/mustangproject/Allowance.java | 2 + .../java/org/mustangproject/BankDetails.java | 1 + .../org/mustangproject/CalculatedInvoice.java | 16 + .../java/org/mustangproject/CashDiscount.java | 27 ++ .../main/java/org/mustangproject/Charge.java | 2 + .../java/org/mustangproject/ClassCode.java | 2 + .../DesignatedProductClassification.java | 2 + .../java/org/mustangproject/DirectDebit.java | 2 + .../org/mustangproject/FileAttachment.java | 3 + .../java/org/mustangproject/IncludedNote.java | 135 +++--- .../main/java/org/mustangproject/Invoice.java | 5 + .../mustangproject/ReferencedDocument.java | 2 + .../java/org/mustangproject/SchemedID.java | 4 + .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 8 + .../ZUGFeRD/DeSerializationTest.java | 440 +++++++++++++++++- .../ZUGFeRD/ZF2ZInvoiceImporterTest.java | 7 +- 17 files changed, 603 insertions(+), 69 deletions(-) diff --git a/History.md b/History.md index 225d887a..00825a48 100644 --- a/History.md +++ b/History.md @@ -1,15 +1,15 @@ 2.15.1 ======= -- #566 -- have a bean contructor for direct debit --? lineTotalAmount is null +- #566 Failed to parse PDF - Could not reproduce the invoice ? be able to access ID in error message -- closes #579 -- #581 -- #576 -- #578 +- closes #579 prepaidamount is only read in UBL +- #581 parse lineTotalAmount +- #576 read lineid, #578 set lineid - log error ids - #503 import more ubl +- allow jackson to run over more classes, e.g., DirectDebit, bean contructor for direct debit +- allow json includedNotes +- support importing duePayableAmount 2.15.0 ======= diff --git a/library/src/main/java/org/mustangproject/Allowance.java b/library/src/main/java/org/mustangproject/Allowance.java index 9c26d02e..aa15de39 100644 --- a/library/src/main/java/org/mustangproject/Allowance.java +++ b/library/src/main/java/org/mustangproject/Allowance.java @@ -1,12 +1,14 @@ package org.mustangproject; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.math.BigDecimal; /*** * (absolute) allowances on item and document level */ +@JsonIgnoreProperties(ignoreUnknown = true) public class Allowance extends Charge { /*** diff --git a/library/src/main/java/org/mustangproject/BankDetails.java b/library/src/main/java/org/mustangproject/BankDetails.java index 5998bf16..1a28c78b 100644 --- a/library/src/main/java/org/mustangproject/BankDetails.java +++ b/library/src/main/java/org/mustangproject/BankDetails.java @@ -7,6 +7,7 @@ import org.mustangproject.ZUGFeRD.IZUGFeRDTradeSettlementPayment; /** * provides e.g. the IBAN to transfer money to :-) */ +@JsonIgnoreProperties(ignoreUnknown = true) public class BankDetails implements IZUGFeRDTradeSettlementPayment { /** * the bank account number diff --git a/library/src/main/java/org/mustangproject/CalculatedInvoice.java b/library/src/main/java/org/mustangproject/CalculatedInvoice.java index c6a24fb1..bde4ae72 100644 --- a/library/src/main/java/org/mustangproject/CalculatedInvoice.java +++ b/library/src/main/java/org/mustangproject/CalculatedInvoice.java @@ -2,6 +2,7 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.TransactionCalculator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,8 +10,10 @@ import org.slf4j.LoggerFactory; import java.io.Serializable; import java.math.BigDecimal; +@JsonIgnoreProperties(ignoreUnknown = true) public class CalculatedInvoice extends Invoice implements Serializable { + protected BigDecimal duePayable=null; protected BigDecimal grandTotal=null; protected BigDecimal lineTotalAmount=null; @@ -18,6 +21,7 @@ public class CalculatedInvoice extends Invoice implements Serializable { TransactionCalculator tc=new TransactionCalculator(this); grandTotal=tc.getGrandTotal(); lineTotalAmount=tc.getValue(); + duePayable=tc.getDuePayable(); } public BigDecimal getGrandTotal() { if (grandTotal==null) { @@ -29,6 +33,18 @@ public class CalculatedInvoice extends Invoice implements Serializable { grandTotal=grand; return this; } + + public BigDecimal getDuePayable() { + if (duePayable==null) { + calculate(); + } + return duePayable; + } + public CalculatedInvoice setDuePayable(BigDecimal due) { + duePayable=due; + return this; + } + public BigDecimal getLineTotalAmount() { if (lineTotalAmount==null) { calculate(); diff --git a/library/src/main/java/org/mustangproject/CashDiscount.java b/library/src/main/java/org/mustangproject/CashDiscount.java index c7fb7a03..664c982b 100644 --- a/library/src/main/java/org/mustangproject/CashDiscount.java +++ b/library/src/main/java/org/mustangproject/CashDiscount.java @@ -1,5 +1,6 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IZUGFeRDCashDiscount; import java.math.BigDecimal; @@ -7,6 +8,7 @@ import java.math.BigDecimal; /*** * A class to represent discounts for early payments ("Skonto") */ +@JsonIgnoreProperties(ignoreUnknown = true) public class CashDiscount implements IZUGFeRDCashDiscount { /*** @@ -31,6 +33,31 @@ public class CashDiscount implements IZUGFeRDCashDiscount { this.days = days; } + /*** + * bean contructor + */ + public CashDiscount() { + + } + + public BigDecimal getPercent() { + return percent; + } + + public CashDiscount setPercent(BigDecimal percent) { + this.percent = percent; + return this; + } + + public Integer getDays() { + return days; + } + + public CashDiscount setDays(Integer days) { + this.days = days; + return this; + } + /*** * @return this particular cash discount as cross industry invoice XML */ diff --git a/library/src/main/java/org/mustangproject/Charge.java b/library/src/main/java/org/mustangproject/Charge.java index 0e081397..1af13943 100644 --- a/library/src/main/java/org/mustangproject/Charge.java +++ b/library/src/main/java/org/mustangproject/Charge.java @@ -1,6 +1,7 @@ package org.mustangproject; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; @@ -9,6 +10,7 @@ import java.math.BigDecimal; /*** * Absolute and relative charges for document and item level */ +@JsonIgnoreProperties(ignoreUnknown = true) public class Charge implements IZUGFeRDAllowanceCharge { /** diff --git a/library/src/main/java/org/mustangproject/ClassCode.java b/library/src/main/java/org/mustangproject/ClassCode.java index 8b0a58cc..af7eea1f 100644 --- a/library/src/main/java/org/mustangproject/ClassCode.java +++ b/library/src/main/java/org/mustangproject/ClassCode.java @@ -20,12 +20,14 @@ */ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * A schemed classification for products. The scheme can be anything defined in UNTDID 7143. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class ClassCode { private final String listID; private final String code; diff --git a/library/src/main/java/org/mustangproject/DesignatedProductClassification.java b/library/src/main/java/org/mustangproject/DesignatedProductClassification.java index ac74bf36..f80f8ae2 100644 --- a/library/src/main/java/org/mustangproject/DesignatedProductClassification.java +++ b/library/src/main/java/org/mustangproject/DesignatedProductClassification.java @@ -20,12 +20,14 @@ */ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IDesignatedProductClassification; /** * An implementation of {@link IDesignatedProductClassification} for describing a {@link org.mustangproject.Product} * */ +@JsonIgnoreProperties(ignoreUnknown = true) public class DesignatedProductClassification implements IDesignatedProductClassification { private final ClassCode classCode; private String className; diff --git a/library/src/main/java/org/mustangproject/DirectDebit.java b/library/src/main/java/org/mustangproject/DirectDebit.java index d90ba661..9f345881 100644 --- a/library/src/main/java/org/mustangproject/DirectDebit.java +++ b/library/src/main/java/org/mustangproject/DirectDebit.java @@ -1,10 +1,12 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IZUGFeRDTradeSettlementDebit; /** * provides e.g. the IBAN to transfer money to :-) */ +@JsonIgnoreProperties(ignoreUnknown = true) public class DirectDebit implements IZUGFeRDTradeSettlementDebit { /** * Debited account identifier (BT-91) diff --git a/library/src/main/java/org/mustangproject/FileAttachment.java b/library/src/main/java/org/mustangproject/FileAttachment.java index dfced588..7f4486d5 100644 --- a/library/src/main/java/org/mustangproject/FileAttachment.java +++ b/library/src/main/java/org/mustangproject/FileAttachment.java @@ -1,5 +1,8 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) public class FileAttachment { protected String filename; diff --git a/library/src/main/java/org/mustangproject/IncludedNote.java b/library/src/main/java/org/mustangproject/IncludedNote.java index eff8385b..3af7bf18 100644 --- a/library/src/main/java/org/mustangproject/IncludedNote.java +++ b/library/src/main/java/org/mustangproject/IncludedNote.java @@ -1,75 +1,98 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + /** * A grouping of business terms to indicate accounting-relevant free texts including a qualification of these. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class IncludedNote { - private String content; - private SubjectCode subjectCode; + private String content; - private static final String INCLUDE_START = ""; - private static final String INCLUDE_END = ""; - private static final String CONTENT_START = ""; - private static final String CONTENT_END = ""; - private static final String SUBJECT_CODE_START = ""; - private static final String SUBJECT_CODE_END = ""; + private SubjectCode subjectCode; - private IncludedNote(String content, SubjectCode subjectCode) { - this.content = content; - this.subjectCode = subjectCode; - } + private static final String INCLUDE_START = ""; + private static final String INCLUDE_END = ""; + private static final String CONTENT_START = ""; + private static final String CONTENT_END = ""; + private static final String SUBJECT_CODE_START = ""; + private static final String SUBJECT_CODE_END = ""; + + private IncludedNote(String content, SubjectCode subjectCode) { + this.content = content; + this.subjectCode = subjectCode; + } /** * bean constructor */ - public IncludedNote() { + public IncludedNote() { } - public static IncludedNote generalNote(String content) { - return new IncludedNote(content, SubjectCode.AAI); - } - public static IncludedNote regulatoryNote(String content) { - return new IncludedNote(content, SubjectCode.REG); - } - public static IncludedNote legalNote(String content) { - return new IncludedNote(content, SubjectCode.ABL); - } - public static IncludedNote customsNote(String content) { - return new IncludedNote(content, SubjectCode.CUS); - } - public static IncludedNote sellerNote(String content) { - return new IncludedNote(content, SubjectCode.SUR); - } - public static IncludedNote taxNote(String content) { - return new IncludedNote(content, SubjectCode.TXD); - } - public static IncludedNote introductionNote(String content) { - return new IncludedNote(content, SubjectCode.ACY); - } - public static IncludedNote discountBonusNote(String content) { - return new IncludedNote(content, SubjectCode.AAK); - } + public static IncludedNote generalNote(String content) { + return new IncludedNote(content, SubjectCode.AAI); + } - public static IncludedNote unspecifiedNote(String content) { - return new IncludedNote(content, null); - } + public static IncludedNote regulatoryNote(String content) { + return new IncludedNote(content, SubjectCode.REG); + } - public String getContent() { - return content; - } + public static IncludedNote legalNote(String content) { + return new IncludedNote(content, SubjectCode.ABL); + } + + public static IncludedNote customsNote(String content) { + return new IncludedNote(content, SubjectCode.CUS); + } + + public static IncludedNote sellerNote(String content) { + return new IncludedNote(content, SubjectCode.SUR); + } + + public static IncludedNote taxNote(String content) { + return new IncludedNote(content, SubjectCode.TXD); + } + + public static IncludedNote introductionNote(String content) { + return new IncludedNote(content, SubjectCode.ACY); + } + + public static IncludedNote discountBonusNote(String content) { + return new IncludedNote(content, SubjectCode.AAK); + } + + public static IncludedNote unspecifiedNote(String content) { + return new IncludedNote(content, null); + } + + public String getContent() { + return content; + } + + public SubjectCode getSubjectCode() { + return subjectCode; + } + + + public IncludedNote setSubjectCode(SubjectCode subjectCode) { + this.subjectCode = subjectCode; + return this; + } + + public IncludedNote setContent(String content) { + this.content = content; + return this; + } + + + public String toCiiXml() { + String result = INCLUDE_START + CONTENT_START + + XMLTools.encodeXML(getContent()) + CONTENT_END; + if (getSubjectCode() != null) { + result += SUBJECT_CODE_START + getSubjectCode() + SUBJECT_CODE_END; + } + return result + INCLUDE_END; + } - public SubjectCode getSubjectCode() { - return subjectCode; - } - - public String toCiiXml(){ - String result = INCLUDE_START + CONTENT_START + - XMLTools.encodeXML(getContent() )+ CONTENT_END; - if (getSubjectCode() != null) { - result += SUBJECT_CODE_START + getSubjectCode() + SUBJECT_CODE_END; - } - return result + INCLUDE_END; - } - } diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 88e6cc53..10ba057f 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -413,6 +413,11 @@ public class Invoice implements IExportableTransaction { return includedNotes; } + public Invoice setNotesWithSubjectCode(List theList) { + includedNotes=theList; + return this; + } + @Override public String getCurrency() { return currency; diff --git a/library/src/main/java/org/mustangproject/ReferencedDocument.java b/library/src/main/java/org/mustangproject/ReferencedDocument.java index 4d2151ca..221b167f 100644 --- a/library/src/main/java/org/mustangproject/ReferencedDocument.java +++ b/library/src/main/java/org/mustangproject/ReferencedDocument.java @@ -1,9 +1,11 @@ package org.mustangproject; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import org.mustangproject.ZUGFeRD.IReferencedDocument; import org.mustangproject.util.NodeMap; import org.w3c.dom.Node; +@JsonIgnoreProperties(ignoreUnknown = true) public class ReferencedDocument implements IReferencedDocument { String issuerAssignedID; diff --git a/library/src/main/java/org/mustangproject/SchemedID.java b/library/src/main/java/org/mustangproject/SchemedID.java index 9df8a2d9..3cfd3974 100644 --- a/library/src/main/java/org/mustangproject/SchemedID.java +++ b/library/src/main/java/org/mustangproject/SchemedID.java @@ -1,5 +1,9 @@ package org.mustangproject; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) public class SchemedID { protected String scheme; protected String id; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 909a0840..819323de 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -415,6 +415,14 @@ public class ZUGFeRDInvoiceImporter { } } + xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"DuePayableAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"PayableAmount\"]"); + NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + if (lineDueNodes.getLength() > 0) { + if (zpp instanceof CalculatedInvoice) { + ((CalculatedInvoice) zpp).setDuePayable(new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0)))); + } + } + Date issueDate = null; Date dueDate = null; Date deliveryDate = null; diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java index 0dbd2dc0..a9c35c1b 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/DeSerializationTest.java @@ -24,21 +24,25 @@ package org.mustangproject.ZUGFeRD; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import junit.framework.TestCase; +import org.junit.Assert; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; import org.mustangproject.*; +import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants; import javax.xml.xpath.XPathExpressionException; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class DeSerializationTest extends ResourceCase { +public class DeSerializationTest extends ResourceCase { public void testJackson() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); @@ -68,7 +72,7 @@ public class DeSerializationTest extends ResourceCase { hasExceptions = true; } - CalculatedInvoice ci=new CalculatedInvoice(); + CalculatedInvoice ci = new CalculatedInvoice(); try { zii.extractInto(ci); } catch (XPathExpressionException e) { @@ -154,14 +158,57 @@ public class DeSerializationTest extends ResourceCase { " }\n" + " ]\n" + "}", Invoice.class); - TransactionCalculator tc=new TransactionCalculator(fromJSON); - assertEquals(tc.getGrandTotal(),new BigDecimal("234.43")); + TransactionCalculator tc = new TransactionCalculator(fromJSON); + assertEquals(tc.getGrandTotal(), new BigDecimal("234.43")); assertEquals(fromJSON.getNumber(), fromJSON.getNumber()); assertEquals(fromJSON.getZFItems().length, fromJSON.getZFItems().length); } + public void testIssuerAssignedIDRoundtrip() { + String occurrenceFrom = "20201001"; + String occurrenceTo = "20201005"; + String contractID = "376zreurzu0983"; + + String orgID = "0009845"; + String orgname = "Test company"; + String number = "123"; + String priceStr = "1.00"; + String taxID = "9990815"; + BigDecimal price = new BigDecimal(priceStr); + Invoice newInvoiceFromJSON = null; + boolean hasExceptions = false; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + try { + SchemedID gtin = new SchemedID("0160", "2001015001325"); + SchemedID gln = new SchemedID("0088", "4304171000002"); + Invoice i = new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) + .setSellerOrderReferencedDocumentID("9384").setBuyerOrderReferencedDocumentID("28934") + .setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo)) + .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).setEmail("sender@test.org").setID(orgID).addVATID("DE0815")) + .setDeliveryAddress(new TradeParty("just the other side of the street", "teststr.12a", "55232", "Entenhausen", "DE").addVATID("DE47110")) + .setContractReferencedDocument(contractID) + .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addGlobalID(gln).setEmail("recipient@test.org").addVATID("DE4711") + .setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")).setAdditionalAddress("Hinterhaus 3")) + .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)) + .setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE); + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(i); + newInvoiceFromJSON = mapper.readValue(json, Invoice.class); + } catch (ParseException e) { + hasExceptions = true; + } catch (JsonProcessingException e) { + hasExceptions = true; + } + assertEquals(newInvoiceFromJSON.getBuyerOrderReferencedDocumentID(), "28934"); + assertFalse(hasExceptions); + + + } public void testDueDateRoundtrip() throws JsonProcessingException { @@ -219,4 +266,389 @@ public class DeSerializationTest extends ResourceCase { } + public void testDirectDebit() throws JsonProcessingException { + + ObjectMapper mapper = new ObjectMapper(); + + // [{"stringValue":"a","intValue":1,"booleanValue":true}, + // {"stringValue":"bc","intValue":3,"booleanValue":false}] + + boolean exceptions = false; + String theXML = ""; + try { + Invoice fromJSON = mapper.readValue("\n" + + "\t{\n" + + "\t\t\"documentCode\": \"380\",\n" + + "\t\t\"number\": \"F20220031\",\n" + + "\t\t\"referenceNumber\": \"SERVEXEC\",\n" + + "\t\t\"buyerOrderReferencedDocumentID\": \"PO201925478\",\n" + + "\t\t\"ownOrganisationName\": \"LE FOURNISSEUR\",\n" + + "\t\t\"currency\": \"EUR\",\n" + + "\t\t\"issueDate\": \"2022-01-30T23:00:00.000+00:00\",\n" + + "\t\t\"dueDate\": \"2022-03-01T23:00:00.000+00:00\",\n" + + "\t\t\"deliveryDate\": \"2022-01-27T23:00:00.000+00:00\",\n" + + "\t\t\"sender\": {\n" + + "\t\t\"name\": \"LE FOURNISSEUR\",\n" + + "\t\t\t\"zip\": \"75018\",\n" + + "\t\t\t\"street\": \"35 rue d'ici\",\n" + + "\t\t\t\"location\": \"PARIS\",\n" + + "\t\t\t\"country\": \"FR\",\n" + + "\t\t\t\"vatID\": \"FR11123456782\",\n" + + "\t\t\t\"additionalAddress\": \"Seller line 2\",\n" + + "\t\t\t\"additionalAddressExtension\": \"Seller line 3\",\n" + + "\t\t\t\"bankDetails\": [\n" + + "\t\t{\n" + + "\t\t\t\"accountName\": null,\n" + + "\t\t\t\"iban\": \"FR20 1254 2547 2569 8542 5874 698\",\n" + + "\t\t\t\"bic\": \"BIC_MONCOMPTE\"\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"debitDetails\": [\n" + + "\t\t{\n" + + "\t\t\t\"mandate\": \"MANDATE PT\",\n" + + "\t\t\t\"iban\": \"FR20 1254 2547 2569 8542 5874 698\"\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"contact\": {\n" + + "\t\t\t\"name\": \"M. CONTACT\",\n" + + "\t\t\t\t\"phone\": \"01 02 03 54 87\",\n" + + "\t\t\t\t\"email\": \"seller@seller.com\",\n" + + "\t\t\t\t\"zip\": null,\n" + + "\t\t\t\t\"street\": null,\n" + + "\t\t\t\t\"location\": null,\n" + + "\t\t\t\t\"country\": null,\n" + + "\t\t\t\t\"fax\": null,\n" + + "\t\t\t\t\"vatid\": null,\n" + + "\t\t\t\t\"id\": null,\n" + + "\t\t\t\t\"additionalAddress\": null\n" + + "\t\t},\n" + + "\t\t\"email\": \"moi@seller.com\",\n" + + "\t\t\t\"vatid\": \"FR11123456782\",\n" + + "\t\t\t\"id\": \"123\",\n" + + "\t\t\t\"legalOrganisation\": {\n" + + "\t\t\t\"schemedID\": null,\n" + + "\t\t\t\t\"tradingBusinessName\": \"SELLER TRADE NAME\"\n" + + "\t\t},\n" + + "\t\t\"globalID\": \"587451236587\",\n" + + "\t\t\t\"globalIDScheme\": \"0088\"\n" + + "\t},\n" + + "\t\t\"recipient\": {\n" + + "\t\t\"name\": \"LE CLIENT\",\n" + + "\t\t\t\"zip\": \"06000\",\n" + + "\t\t\t\"street\": \"MON ADRESSE LIGNE 1\",\n" + + "\t\t\t\"location\": \"MA VILLE\",\n" + + "\t\t\t\"country\": \"FR\",\n" + + "\t\t\t\"vatID\": \"FR 05 987 654 321\",\n" + + "\t\t\t\"additionalAddress\": \"Buyer line 2\",\n" + + "\t\t\t\"additionalAddressExtension\": \"Buyer line 3\",\n" + + "\t\t\t\"contact\": {\n" + + "\t\t\t\"name\": \"Buyer contact name\",\n" + + "\t\t\t\t\"phone\": \"01 01 25 45 87\",\n" + + "\t\t\t\t\"email\": \"buyer@buyer.com\",\n" + + "\t\t\t\t\"zip\": null,\n" + + "\t\t\t\t\"street\": null,\n" + + "\t\t\t\t\"location\": null,\n" + + "\t\t\t\t\"country\": null,\n" + + "\t\t\t\t\"fax\": null,\n" + + "\t\t\t\t\"vatid\": null,\n" + + "\t\t\t\t\"id\": null,\n" + + "\t\t\t\t\"additionalAddress\": null\n" + + "\t\t},\n" + + "\t\t\"email\": \"me@buyer.com\",\n" + + "\t\t\t\"vatid\": \"FR 05 987 654 321\",\n" + + "\t\t\t\"legalOrganisation\": {\n" + + "\t\t\t\"schemedID\": null,\n" + + "\t\t\t\t\"tradingBusinessName\": null\n" + + "\t\t},\n" + + "\t\t\"globalID\": \"3654789851\",\n" + + "\t\t\t\"globalIDScheme\": \"0088\"\n" + + "\t},\n" + + "\t\t\"deliveryAddress\": {\n" + + "\t\t\"name\": \"DEL Name\",\n" + + "\t\t\t\"zip\": \"06000\",\n" + + "\t\t\t\"street\": \"DEL ADRESSE LIGNE 1\",\n" + + "\t\t\t\"location\": \"NICE\",\n" + + "\t\t\t\"country\": \"FR\",\n" + + "\t\t\t\"additionalAddress\": \"DEL line 2\",\n" + + "\t\t\t\"id\": \"PRIVATE_ID_DEL\"\n" + + "\t},\n" + + "\t\t\"payee\": {\n" + + "\t\t\"name\": \"PAYEE NAME\",\n" + + "\t\t\t\"legalOrganisation\": {\n" + + "\t\t\t\"schemedID\": null,\n" + + "\t\t\t\t\"tradingBusinessName\": null\n" + + "\t\t},\n" + + "\t\t\"globalID\": \"587451236586\",\n" + + "\t\t\t\"globalIDScheme\": \"0088\"\n" + + "\t},\n" + + "\t\t\"sellerOrderReferencedDocumentID\": \"SALES REF 2547\",\n" + + "\t\t\"despatchAdviceReferencedDocumentID\": \"DESPADV002\",\n" + + "\t\t\"grandTotal\": 107.82,\n" + + "\t\t\"detailedDeliveryPeriodFrom\": \"2021-12-31T23:00:00.000+00:00\",\n" + + "\t\t\"detailedDeliveryPeriodTo\": \"2022-12-30T23:00:00.000+00:00\",\n" + + "\t\t\"notesWithSubjectCode\": [\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"FOURNISSEUR F SARL au capital de 50 000 EUR\",\n" + + "\t\t\t\"subjectCode\": \"REG\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"RCS MAVILLE 123 456 782\",\n" + + "\t\t\t\"subjectCode\": \"ABL\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"35 ma rue a moi, code postal Ville Pays – contact@masociete.fr - www.masociete.fr – N° TVA : FR32 123 456 789\",\n" + + "\t\t\t\"subjectCode\": \"AAI\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"Tout retard de paiement engendre une pénalité exigible à compter de la date d'échéance, calculée sur la base de trois fois le taux d'intérêt légal.\",\n" + + "\t\t\t\"subjectCode\": null\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"Indemnité forfaitaire pour frais de recouvrement en cas de retard de paiement : 40 €.\",\n" + + "\t\t\t\"subjectCode\": null\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"content\": \"Les réglements reçus avant la date d'échéance ne donneront pas lieu à escompte.\",\n" + + "\t\t\t\"subjectCode\": null\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"zfallowances\": [\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": null,\n" + + "\t\t\t\"reasonCode\": \"100\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 2,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"REMISE VOLUME\",\n" + + "\t\t\t\"reasonCode\": \"71\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": null,\n" + + "\t\t\t\"reasonCode\": \"100\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1.4,\n" + + "\t\t\t\"taxPercent\": 20,\n" + + "\t\t\t\"reason\": \"REMISE COMMERCIALE\",\n" + + "\t\t\t\"reasonCode\": \"100\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1.2,\n" + + "\t\t\t\"taxPercent\": 10,\n" + + "\t\t\t\"reason\": \"REMISE COMMERCIALE\",\n" + + "\t\t\t\"reasonCode\": \"100\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"zfcharges\": [\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": null,\n" + + "\t\t\t\"reasonCode\": \"ADL\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 1,\n" + + "\t\t\t\"taxPercent\": null,\n" + + "\t\t\t\"reason\": \"FRAIS PALETTE\",\n" + + "\t\t\t\"reasonCode\": null,\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 2.8,\n" + + "\t\t\t\"taxPercent\": 20,\n" + + "\t\t\t\"reason\": \"FRAIS DEPLACEMENT\",\n" + + "\t\t\t\"reasonCode\": \"FC\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"percent\": null,\n" + + "\t\t\t\"totalAmount\": 0.6,\n" + + "\t\t\t\"taxPercent\": 10,\n" + + "\t\t\t\"reason\": \"FRAIS DEPLACEMENT\",\n" + + "\t\t\t\"reasonCode\": \"ADR\",\n" + + "\t\t\t\"categoryCode\": \"S\"\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"tradeSettlement\": [\n" + + "\t\t{\n" + + "\t\t\t\"accountName\": null,\n" + + "\t\t\t\"iban\": \"FR20 1254 2547 2569 8542 5874 698\",\n" + + "\t\t\t\"bic\": \"BIC_MONCOMPTE\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"mandate\": \"MANDATE PT\",\n" + + "\t\t\t\"iban\": \"FR20 1254 2547 2569 8542 5874 698\"\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"zfitems\": [\n" + + "\t\t{\n" + + "\t\t\t\"price\": 60,\n" + + "\t\t\t\"quantity\": 1,\n" + + "\t\t\t\"basisQuantity\": 1,\n" + + "\t\t\t\"product\": {\n" + + "\t\t\t\"unit\": \"C62\",\n" + + "\t\t\t\t\"name\": \"REMBOURSEMENT AFFRANCHISSEMENT\",\n" + + "\t\t\t\t\"description\": \"Description\",\n" + + "\t\t\t\t\"taxCategoryCode\": \"Z\",\n" + + "\t\t\t\t\"vatpercent\": 0,\n" + + "\t\t\t\t\"reverseCharge\": false,\n" + + "\t\t\t\t\"intraCommunitySupply\": false,\n" + + "\t\t\t\t\"globalID\": \"598785412598745\",\n" + + "\t\t\t\t\"globalIDScheme\": \"0160\"\n" + + "\t\t},\n" + + "\t\t\t\"buyerOrderReferencedDocumentLineID\": \"1\",\n" + + "\t\t\t\"value\": 60\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"price\": 30,\n" + + "\t\t\t\"quantity\": 3,\n" + + "\t\t\t\"basisQuantity\": 3,\n" + + "\t\t\t\"product\": {\n" + + "\t\t\t\"unit\": \"C62\",\n" + + "\t\t\t\t\"name\": \"FOURNITURES DIVERSES\",\n" + + "\t\t\t\t\"description\": \"Description\",\n" + + "\t\t\t\t\"taxCategoryCode\": \"S\",\n" + + "\t\t\t\t\"vatpercent\": 20,\n" + + "\t\t\t\t\"reverseCharge\": false,\n" + + "\t\t\t\t\"intraCommunitySupply\": false\n" + + "\t\t},\n" + + "\t\t\t\"buyerOrderReferencedDocumentLineID\": \"3\",\n" + + "\t\t\t\"value\": 30\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"price\": 12,\n" + + "\t\t\t\"quantity\": 1,\n" + + "\t\t\t\"basisQuantity\": 1,\n" + + "\t\t\t\"product\": {\n" + + "\t\t\t\"unit\": \"C62\",\n" + + "\t\t\t\t\"name\": \"APPEL\",\n" + + "\t\t\t\t\"description\": \"Description\",\n" + + "\t\t\t\t\"taxCategoryCode\": \"S\",\n" + + "\t\t\t\t\"vatpercent\": 10,\n" + + "\t\t\t\t\"reverseCharge\": false,\n" + + "\t\t\t\t\"intraCommunitySupply\": false\n" + + "\t\t},\n" + + "\t\t\t\"buyerOrderReferencedDocumentLineID\": \"2\",\n" + + "\t\t\t\"value\": 12\n" + + "\t\t}\n" + + " ],\n" + + "\t\t\"ownStreet\": \"35 rue d'ici\",\n" + + "\t\t\"ownCountry\": \"FR\",\n" + + "\t\t\"ownZIP\": \"75018\",\n" + + "\t\t\"ownLocation\": \"PARIS\",\n" + + "\t\t\"ownVATID\": \"FR11123456782\",\n" + + "\t\t\"valid\": false\n" + + "\t}\n", Invoice.class); + ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider(); + zf2p.setProfile(Profiles.getByName("XRechnung")); + zf2p.generateXML(fromJSON); + theXML = new String(zf2p.getXML()); + } catch (Exception e) { + exceptions = true; + } + assertTrue(theXML.contains("pour frais de recouvrement en cas de retard de paiement")); + assertFalse(exceptions); + + } + + } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index a0b6ad16..3472af79 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -461,11 +461,14 @@ this would test if for all elements/attributes assertTrue(isBD); BigDecimal expectedPrepaid=new BigDecimal(50); BigDecimal expectedLineTotal=new BigDecimal("180.76"); + BigDecimal expectedDue=new BigDecimal("147.65"); if (isBD) { BigDecimal amread=invoice.getTotalPrepaidAmount(); - BigDecimal amline=invoice.getLineTotalAmount(); + BigDecimal importedLineTotal=invoice.getLineTotalAmount(); + BigDecimal importedDuePayable=invoice.getDuePayable(); assertTrue(amread.compareTo(expectedPrepaid) == 0); - assertTrue(amline.compareTo(expectedLineTotal) == 0); + assertTrue(importedLineTotal.compareTo(expectedLineTotal) == 0); + assertTrue(importedDuePayable.compareTo(expectedDue) == 0); } }