diff --git a/History.md b/History.md index afdc430a..9fb670dd 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,14 @@ +2.20.0 +======= +2025-10-30 + +- upgrade to pdfbox 3.0.6 +- #950 issues with nonshaded version: 2.19.1: ClassNotFoudException while running the PDFValidator +- #959 Added FactoorSharp to the list of valid pdf sources +- change return type of getCashDiscounts to CashDiscount object, not interface +- #923 Support for BT-17 (tender or lot reference) +- #960 incorrect calculation for product charges + 2.19.1 ======= 2025-10-09 diff --git a/Mustang-CLI/pom.xml b/Mustang-CLI/pom.xml index 8049c317..0d1cde8c 100644 --- a/Mustang-CLI/pom.xml +++ b/Mustang-CLI/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.19.2-SNAPSHOT + 2.20.1-SNAPSHOT 4.0.0 Mustang-CLI @@ -166,6 +166,7 @@ + false diff --git a/README.md b/README.md index 6c4ecb6c..5cb16cb2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you set up a Maven project, you can reference the mustang artifact like this: org.mustangproject validator - 2.17.0 + 2.20.0 shaded ``` diff --git a/library/pom.xml b/library/pom.xml index b7511156..1590363f 100644 --- a/library/pom.xml +++ b/library/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.19.2-SNAPSHOT + 2.20.1-SNAPSHOT 4.0.0 @@ -274,6 +274,7 @@ + false diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 3bc1f4b6..9f0b4208 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -54,6 +54,7 @@ public class Invoice implements IExportableTransaction { protected BigDecimal totalPrepaidAmount = null; protected Date detailedDeliveryDateStart = null; protected Date detailedDeliveryPeriodEnd = null; + protected IReferencedDocument tenderReference = null; protected ArrayList Allowances = new ArrayList<>(), Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>(); @@ -135,8 +136,8 @@ public class Invoice implements IExportableTransaction { } @Override - public IZUGFeRDCashDiscount[] getCashDiscounts() { - return cashDiscounts.toArray(new IZUGFeRDCashDiscount[0]); + public CashDiscount[] getCashDiscounts() { + return cashDiscounts.toArray(new CashDiscount[0]); } @Override @@ -144,6 +145,35 @@ public class Invoice implements IExportableTransaction { return number; } + + + @Override + /*** + * BT-17 + */ + public IReferencedDocument getTenderReferencedDocument() { + return tenderReference; + } + + + /*** + * BT-17 + * @param dr + * @return + */ + public Invoice setTenderReferencedDocument(ReferencedDocument dr) { + dr.setTypeCode("50");//50 is fixed for tender documents + tenderReference=dr; + return this; + } + + public Invoice setTenderReferencedDocument(String ID) { + ReferencedDocument dr=new ReferencedDocument(ID); + setTenderReferencedDocument(dr); + return this; + } + + public Invoice setNumber(String number) { this.number = number; return this; diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 5a3853a0..eff70131 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -3,10 +3,7 @@ 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.IReferencedDocument; -import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; -import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem; -import org.mustangproject.ZUGFeRD.LineCalculator; +import org.mustangproject.ZUGFeRD.*; import org.mustangproject.util.NodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -90,6 +87,9 @@ public class Item implements IZUGFeRDExportableItem { icnm.getAsNodeMap("ClassifiedTaxCategory") .flatMap(m -> m.getAsBigDecimal("Percent")) .ifPresent(product::setVATPercent); + + + }); itemMap.getAsNodeMap("AssociatedDocumentLineDocument") .flatMap(icnm -> icnm.getAsString("LineID")) @@ -132,6 +132,7 @@ public class Item implements IZUGFeRDExportableItem { } } + itemMap.getAsNodeMap("SpecifiedLineTradeAgreement", "SpecifiedSupplyChainTradeAgreement").ifPresent(icnm -> { icnm.getAsNodeMap("BuyerOrderReferencedDocument") .flatMap(bordNodes -> bordNodes.getAsString("LineID")) @@ -622,6 +623,7 @@ public class Item implements IZUGFeRDExportableItem { return detailedDeliveryPeriodTo; } + public IZUGFeRDExportableItem addNotes(Collection notes) { if (notes == null) { return this; diff --git a/library/src/main/java/org/mustangproject/ReferencedDocument.java b/library/src/main/java/org/mustangproject/ReferencedDocument.java index 7a1c04a0..b5eb239a 100644 --- a/library/src/main/java/org/mustangproject/ReferencedDocument.java +++ b/library/src/main/java/org/mustangproject/ReferencedDocument.java @@ -2,6 +2,7 @@ package org.mustangproject; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; + import java.util.Date; import org.mustangproject.ZUGFeRD.IReferencedDocument; @@ -17,6 +18,10 @@ public class ReferencedDocument implements IReferencedDocument { String referenceTypeCode; Date formattedIssueDateTime; + public ReferencedDocument() { + //bean + } + public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode) { this(issuerAssignedID); this.typeCode = typeCode; @@ -36,7 +41,7 @@ public class ReferencedDocument implements IReferencedDocument { this(issuerAssingedID); this.formattedIssueDateTime = formattedIssueDateTime; } - + public ReferencedDocument(String issuerAssignedID) { this.issuerAssignedID = issuerAssignedID; } @@ -51,6 +56,7 @@ public class ReferencedDocument implements IReferencedDocument { /** * which type is the document? e.g. "916" for additional invoice related + * * @param typeCode as String, e.g. 916 */ public void setTypeCode(String typeCode) { @@ -59,6 +65,7 @@ public class ReferencedDocument implements IReferencedDocument { /** * type of the reference of this line, a UNTDID 1153 code + * * @param referenceTypeCode three uppercase character reference type code as string */ public void setReferenceTypeCode(String referenceTypeCode) { @@ -90,8 +97,7 @@ public class ReferencedDocument implements IReferencedDocument { } @Override - public Date getFormattedIssueDateTime() - { + public Date getFormattedIssueDateTime() { return formattedIssueDateTime; } @@ -100,9 +106,20 @@ public class ReferencedDocument implements IReferencedDocument { return null; } NodeMap nodes = new NodeMap(node); - return new ReferencedDocument(nodes.getAsStringOrNull("IssuerAssignedID", "ID"), + ReferencedDocument rd = new ReferencedDocument(nodes.getAsStringOrNull("IssuerAssignedID", "ID"), nodes.getAsStringOrNull("TypeCode", "DocumentTypeCode"), nodes.getAsStringOrNull("ReferenceTypeCode"), XMLTools.tryDate(nodes.getAsStringOrNull("FormattedIssueDateTime"))); + if (nodes.getAsStringOrNull("ID") != null) { + //sure sign for UBL: here ReferenceTypeCode is no element but a "schemeID" attribute to ID + Node idNode = nodes.getNode("ID").get(); + if (idNode != null) { + Node schemeIDAttr = idNode.getAttributes().getNamedItem("schemeID"); + if (schemeIDAttr != null) { + rd.setReferenceTypeCode(schemeIDAttr.getNodeValue()); + } + } + } + return rd; } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java index 5d72a517..a7cbdbd4 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java @@ -215,6 +215,15 @@ public interface IExportableTransaction { return null; } + /** + * BT-17 tender or lot reference + * + * @return mandatory ID, optional Date + */ + default IReferencedDocument getTenderReferencedDocument() { + return null; + } + /** * own name * diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java index 05d86170..05f4d437 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java @@ -157,11 +157,11 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{ - /*** - * specifies the item level delivery period (there is also one on document level), - * this will be included in a BillingSpecifiedPeriod element - * @return the beginning of the delivery period - */ + /*** + * specifies the item level delivery period (there is also one on document level), + * this will be included in a BillingSpecifiedPeriod element + * @return the beginning of the delivery period + */ default Date getDetailedDeliveryPeriodFrom() { return null; } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java index 33f883f4..faeb5671 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/LineCalculator.java @@ -73,7 +73,7 @@ public class LineCalculator { } if (currentItem.getProduct().getCharges()!=null) { for (IZUGFeRDAllowanceCharge ccaf : currentItem.getProduct().getCharges()) { - delta = delta.subtract(ccaf.getTotalAmount(currentItem)); + delta = delta.add(ccaf.getTotalAmount(currentItem)); } } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index b303c353..cddadf7a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -631,6 +631,18 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } } + if(trans.getTenderReferencedDocument() != null){ + xml += "" + + "" + XMLTools.encodeXML(trans.getTenderReferencedDocument().getIssuerAssignedID()) + "" + + "" + 50 + ""; + if (trans.getTenderReferencedDocument().getFormattedIssueDateTime()!=null) { + final SimpleDateFormat dateFormat102 = new SimpleDateFormat("yyyyMMdd"); + xml += ""+XMLTools.encodeXML(dateFormat102.format(trans.getTenderReferencedDocument().getFormattedIssueDateTime()))+""; + } + + + xml += ""; + } if (trans.getSpecifiedProcuringProjectID() != null) { xml += "" + "" + XMLTools.encodeXML(trans.getSpecifiedProcuringProjectID()) + ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java index 6eb0fc08..66787cc3 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java @@ -696,6 +696,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte if ((meta != null) && (meta.getLength() > 0)) { try { DomXmpParser xmpParser = new DomXmpParser(); + xmpParser.setStrictParsing(false); return xmpParser.parse(meta.toByteArray()); } catch (XmpParsingException e) { throw new IOException(e); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java index e128f0a6..12653cdf 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java @@ -111,6 +111,7 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter { if (metadata != null) { try { DomXmpParser xmpParser = new DomXmpParser(); + xmpParser.setStrictParsing(false); XMPMetadata xmp = xmpParser.parse(metadata.createInputStream()); PDFAIdentificationSchema pdfaSchema = xmp.getPDFAIdentificationSchema(); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 2f7cc2f4..5535e6f2 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -612,6 +612,18 @@ public class ZUGFeRDInvoiceImporter { if (!issueDateStr.isEmpty()) { issueDate = parseDate(issueDateStr, "yyyy-MM-dd"); } + + String tenderReference = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"OriginatorDocumentReference\"]/*[local-name()=\"ID\"]").trim(); + String tenderReferenceDate = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"OriginatorDocumentReference\"]/*[local-name()=\"ID\"]").trim(); + if((tenderReference != null)&&(!tenderReference.isEmpty())){ + if((tenderReferenceDate != null)&&(!tenderReferenceDate.isEmpty())){ + zpp.setTenderReferencedDocument(new ReferencedDocument(tenderReference, parseDate(tenderReferenceDate, "yyyy-MM-dd"))); + } else { + zpp.setTenderReferencedDocument(tenderReference); + } + + } + String dueDt = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"DueDate\"]").trim(); if (!dueDt.isEmpty()) { dueDate = parseDate(dueDt, "yyyy-MM-dd"); @@ -677,6 +689,9 @@ public class ZUGFeRDInvoiceImporter { NodeList headerTradeAgreementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); String buyerOrderIssuerAssignedID = null; String sellerOrderIssuerAssignedID = null; + String additionalReferencedDocument = null; + Date additionalReferencedDocumentDate = null; + for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) { // XMLTools.trimOrNull(nodes.item(i)))) { Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i); @@ -702,12 +717,55 @@ public class ZUGFeRDInvoiceImporter { } } } + int typeC = 0; + additionalReferencedDocument=null; + additionalReferencedDocumentDate=null; + //Reading BT-17 + if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("AdditionalReferencedDocument")) { + NodeList additionalChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); + for (int additionalChildIndex = 0; additionalChildIndex < additionalChilds.getLength(); additionalChildIndex++) { + + if ((additionalChilds.item(additionalChildIndex).getLocalName() != null) + && additionalChilds.item(additionalChildIndex).getLocalName().equals("TypeCode")) { + typeC = Integer.parseInt(XMLTools.trimOrNull(additionalChilds.item(additionalChildIndex))); + } + + if ((additionalChilds.item(additionalChildIndex).getLocalName() != null) + && (additionalChilds.item(additionalChildIndex).getLocalName().equals("IssuerAssignedID"))) { + additionalReferencedDocument = XMLTools.trimOrNull(additionalChilds.item(additionalChildIndex)); + } + if ((additionalChilds.item(additionalChildIndex).getLocalName() != null) + && (additionalChilds.item(additionalChildIndex).getLocalName().equals("FormattedIssueDateTime"))) { + + NodeList FormattedIssueDateTimeChilds = additionalChilds.item(additionalChildIndex).getChildNodes(); + for (int dateChildIndex = 0; dateChildIndex < FormattedIssueDateTimeChilds.getLength(); dateChildIndex++) { + if ((FormattedIssueDateTimeChilds.item(dateChildIndex).getLocalName() != null) + && (FormattedIssueDateTimeChilds.item(dateChildIndex).getLocalName().equals("DateTimeString"))) { + additionalReferencedDocumentDate = XMLTools.tryDate(FormattedIssueDateTimeChilds.item(dateChildIndex)); + } + + } + } + + } + if (typeC == 50) { + if (additionalReferencedDocument != null){ + if (additionalReferencedDocumentDate!=null) { + zpp.setTenderReferencedDocument(new ReferencedDocument(additionalReferencedDocument, additionalReferencedDocumentDate)); + } else { + zpp.setTenderReferencedDocument(additionalReferencedDocument); + } + } + + } + } } } - } + + String currency = extractString("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]/*[local-name()=\"InvoiceCurrencyCode\"]|//*[local-name()=\"DocumentCurrencyCode\"]"); zpp.setCurrency(currency); @@ -950,6 +1008,7 @@ public class ZUGFeRDInvoiceImporter { zpp.setDespatchAdviceReferencedDocumentID(s); } } + String invoiceReferencedDocumentID = extractString("//*[local-name()=\"InvoiceReferencedDocument\"]/*[local-name()=\"IssuerAssignedID\"]|//*[local-name()=\"BillingReference\"]/*[local-name()=\"InvoiceDocumentReference\"]/*[local-name()=\"ID\"]"); if (!invoiceReferencedDocumentID.isEmpty()) { zpp.setInvoiceReferencedDocumentID(invoiceReferencedDocumentID); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 3af67886..530e9994 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -249,6 +249,54 @@ public class CalculationTest extends ResourceCase { assertEquals(valueOf(101.85).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros()); } + @Test + public void testNullifyingAllowancesCharges() { + 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); + + } + + /* trade party (sender) */ + 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; + BigDecimal amount=new BigDecimal("10.00"); + + product = new Product("AAA", "", "H87", BigDecimal.ZERO).setSellerAssignedID("1AAA"); + product.addCharge(new Charge(amount).setReasonCode("ZZZ").setReason("Zuschlag")); + product.addAllowance((Allowance) new Allowance(amount).setReasonCode("95").setReason("Rabatt")); + item = new Item(product, new BigDecimal("4.750"), new BigDecimal(1.00)); + + // set values for additional charge and discount used for next lines + item.addCharge(new Charge(amount).setReasonCode("ZZZ").setReason("Zuschlag")); + item.addAllowance((Allowance) new Allowance(amount).setReasonCode("95").setReason("Rabatt")); + invoice.addItem(item); + + TransactionCalculator calculator = new TransactionCalculator(invoice); + assertEquals(valueOf(4.750).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros()); + } + public void testSimpleItemPercentAllowance() { /*** * a product with net 1.10 and qty 5 and relative item allowance of 10% should return 5 as line and grand total diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java index f095372b..a82b160d 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2EdgeTest.java @@ -144,6 +144,38 @@ public class ZF2EdgeTest extends MustangReaderTestCase { return "DE"; } + @Override + public IReferencedDocument getTenderReferencedDocument() { + return new IReferencedDocument() { + @Override + public String getIssuerAssignedID() { + return "983-jk-787"; + } + + @Override + public String getTypeCode() { + return "50"; + } + + @Override + public String getReferenceTypeCode() { + return ""; + } + + @Override + public Date getFormattedIssueDateTime() { + SimpleDateFormat sdf=new SimpleDateFormat("YYYY-mm-dd"); + try { + return sdf.parse("2025-10-12"); + } catch (ParseException e) { + // wont happen, I promise :-) + } + return null; // wont happen either + } + + }; + } + @Override public String getOwnLocation() { return "Stadthausen"; diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index 32d77d49..428d8f89 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -596,6 +596,9 @@ public class ZF2PushTest extends TestCase { try { SchemedID gtin = new SchemedID("0160", "2001015001325"); SchemedID gln = new SchemedID("0088", "4304171000002"); + ReferencedDocument dr1=new ReferencedDocument("90-kl-98798-C", sdf.parse("2025-10-12")); + ReferencedDocument dr2=new ReferencedDocument("90-kl-98798-C1", sdf.parse("2025-10-13")); + dr2.setReferenceTypeCode("AAG"); ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setPaymentReference("Verwendungszweck").setDocumentName("Rechnung") .setSellerOrderReferencedDocumentID("9384").setBuyerOrderReferencedDocumentID("28934") .setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo)) @@ -604,12 +607,12 @@ public class ZF2PushTest extends TestCase { .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")) - .setInvoicer( new TradeParty("Abweichender Rechnungssteller", "Teststr.12", "04711", "Entenhausen", "DE") ) - .setInvoicee( new TradeParty("Abweichender Rechnungsempfänger", "Teststr.42", "00815", "Entenhausen", "DE") ) - .addItem(new Item(new Product("Testprodukt", "", "H87", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).setId("a123").addBuyerOrderReferencedDocumentID("orderId").addBuyerOrderReferencedDocumentLineID("xxx").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"). + addAdditionalReference(dr2).addBuyerOrderReferencedDocumentID("orderId").addBuyerOrderReferencedDocumentLineID("xxx").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)) + .setTenderReferencedDocument(dr1) .setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE) .setInvoiceReferencedDocumentID("abc123").addInvoiceReferencedDocument(new ReferencedDocument("abcd1234")) ); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index b99f7108..e9c65968 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -20,6 +20,7 @@ */ package org.mustangproject.ZUGFeRD; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -178,16 +179,79 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { hasExceptions = true; } assertFalse(hasExceptions); + SimpleDateFormat sdf=new SimpleDateFormat("YYYY-MM-dd"); // Reading ZUGFeRD assertEquals("4711", invoice.getZFItems()[0].getProduct().getSellerAssignedID()); assertEquals("9384", invoice.getSellerOrderReferencedDocumentID()); + assertEquals("90-kl-98798-C", invoice.getTenderReferencedDocument().getIssuerAssignedID()); + + IReferencedDocument[] rd=invoice.getZFItems()[0].getAdditionalReferences(); + assertEquals("90-kl-98798-C1", rd[0].getIssuerAssignedID()); + assertEquals("AAG", rd[0].getReferenceTypeCode()); + + + + assertEquals("90-kl-98798-C", invoice.getTenderReferencedDocument().getIssuerAssignedID()); + assertEquals("2025-10-12", sdf.format(invoice.getTenderReferencedDocument().getFormattedIssueDateTime())); assertEquals("sender@test.org", invoice.getSender().getEmail()); assertEquals("recipient@test.org", invoice.getRecipient().getEmail()); assertEquals("28934", invoice.getBuyerOrderReferencedDocumentID()); + + + } + public void testBT17InvoiceImport() { + boolean hasExceptions = false; + Invoice invoice = null; + + ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2PushEdge.pdf"); + + try { + invoice = zii.extractInvoice(); + } catch (XPathExpressionException | ParseException e) { + hasExceptions = true; + } + assertFalse(hasExceptions); + SimpleDateFormat sdf=new SimpleDateFormat("YYYY-MM-dd"); + // Reading ZUGFeRD + assertEquals("90-kl-98798-C", invoice.getTenderReferencedDocument().getIssuerAssignedID()); + assertNotNull(invoice.getTenderReferencedDocument().getFormattedIssueDateTime()); + assertEquals("2025-10-12", sdf.format(invoice.getTenderReferencedDocument().getFormattedIssueDateTime())); + try { + zii.setInputStream(new FileInputStream(getResourceAsFile("cii/bt17-response_1760553749128.cii.xml"))); + invoice = zii.extractInvoice(); + } catch (XPathExpressionException | ParseException | FileNotFoundException e) { + hasExceptions = true; + } + assertFalse(hasExceptions); + // Reading ZUGFeRD + assertEquals("Testing1", invoice.getTenderReferencedDocument().getIssuerAssignedID()); + + } + + public void testBT128InvoiceImport() { + boolean hasExceptions = false; + Invoice invoice = null; + + ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); + try { + zii.setInputStream(new FileInputStream(getResourceAsFile("ubl/BT-128.ubl.xml"))); + + invoice = zii.extractInvoice(); + } catch (XPathExpressionException | ParseException | FileNotFoundException e) { + hasExceptions = true; + } + assertFalse(hasExceptions); + SimpleDateFormat sdf=new SimpleDateFormat("YYYY-mm-dd"); + // Reading ZUGFeRD + assertEquals("90-kl-98798-C1", invoice.getZFItems()[0].getAdditionalReferences()[0].getIssuerAssignedID()); + assertEquals("AAG", invoice.getZFItems()[0].getAdditionalReferences()[0].getReferenceTypeCode()); + + } + public void testZF1Import() { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-MustangGnuaccountingBeispielRE-20171118_506zf1.pdf"); diff --git a/library/src/test/resources/cii/bt17-response_1760553749128.cii.xml b/library/src/test/resources/cii/bt17-response_1760553749128.cii.xml new file mode 100644 index 00000000..a54da434 --- /dev/null +++ b/library/src/test/resources/cii/bt17-response_1760553749128.cii.xml @@ -0,0 +1,181 @@ + + + urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended + RE-20170509/505 + 2017-05-09 + 2022-02-28 + 380 + USD + AB321 + + 123 + + + Testing1 + + + + + Ecke 12 + Stadthausen + 12345 + + DE + + + + DE0815 + + VAT + + + + 0815 + + FC + + + + Bei Spiel GmbH + + + + + + + Bahnstr. 42 + Hinterhaus + Spielkreis + 88802 + + Zweiter Stock + + + DE + + + + DE999999999 + + VAT + + + + Theodor Est + + + + + 2017-05-07 + + + Bahnstr. 42 + Hinterhaus + Spielkreis + 88802 + + Zweiter Stock + + + DE + + + + + + Theodor Est + + + + + 54 + + + 14 Tage 2% Skonto, 30 Tage rein netto + + + 0 + + 337.6 + 0 + + K + 0 + Intra-community supply + + VAT + + + + + + 337.6 + 337.6 + 337.6 + 0 + 0 + 0 + 337.6 + + + 1 + 1 + 1.6 + + 1825 + 130 + + + Künstlerische Gestaltung (Stunde): Einer Beispielrechnung + + K + 0 + + VAT + + + + + 160 + 100 + + + + 2 + 400 + 316 + + Bestellerweiterung für E&F Umbau + + K + 0 + + VAT + + + + + 0.79 + 1 + + + + 3 + 200 + 20 + + Heiße Luft pro Liter + + K + 0 + + VAT + + + + + 0.1 + 1 + + + \ No newline at end of file diff --git a/library/src/test/resources/ubl/BT-128.ubl.xml b/library/src/test/resources/ubl/BT-128.ubl.xml new file mode 100644 index 00000000..fa9b13b5 --- /dev/null +++ b/library/src/test/resources/ubl/BT-128.ubl.xml @@ -0,0 +1,365 @@ + + + urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + Test_EeISI_100 + 2018-11-12 + 2018-11-30 + 380 + #AAA#invoice note text + #AAA#invoice note text 2 + EUR + NOK + uvz + 123 + + 2018-11-12 + 2018-11-30 + + + abc + def + + + + abc123 + 2018-10-04 + + + + lmn + + + ghi + + + 789 + + + Supporting document ref + Supporting document descr + + ZGVmYXVsdA== + + External document location + + + + + rst + 130 + + + 456 + + + + Seller electronic address + + Seller identifier 1 + + + Seller identifier 2 + + + Seller trading name + + + Seller address line 1 + Seller address line 2 + Seller city + 12345 + Seller country subdivision + + Seller address line 3 + + + DE + + + + DE12345677 + + VAT + + + + DE49294093 + + FC + + + + Seller name + Seller additional legal information + + + Seller contact point + +41 345 654455 + seller@contact.de + + + + + + Buyer electronic address + + Buyer identifier + + + Buyer trading name + + + Buyer address line 1 + Buyer address line 2 + Buyer city + 34562 + Buyer country subdivision + + Buyer address line 3 + + + IE + + + + IE394838894 + + VAT + + + + Buyer name + Buyer legal registration identifier + + + Buyer contact point + +353 2948584 + buyer@contact.ie + + + + + + Payee identifier + + + Payee name + + + + + Tax representative name + + + Tax representative address line 1 + Tax representative address line 2 + Tax representative city + 23455 + Tax representative country subdivision + + Tax representative address line 3 + + + DE + + + + DE3949053 + + VAT + + + + + 2018-12-04 + + deliver location identifier + + Deliver to address line 1 + Deliver to address line 2 + Deliver to city + 98765 + Deliver to country subdivision + + Deliver to address line 3 + + + IE + + + + + + Deliver to party name + + + + + total amount + + + false + 95 + Doc allowance reason text + 1.00 + 10 + 1000 + + S + 5 + + VAT + + + + + true + AAA + Doc charge reason text + 1.00 + 10 + 1000 + + S + 5 + + VAT + + + + + 50 + + 1000 + 50 + + S + 5 + + VAT + + + + + 1000 + 0 + + E + 0 + VATEX-EU-O + Exemtion reason text + + VAT + + + + + + 46 + + + 200 + 200 + 205 + 10 + 10 + 0 + 205 + + + 1a + Invoice line note + 10 + 1000 + 6789 + + 2018-11-12 + 2018-11-30 + + + 12345 + + + 90-kl-98798-C1 + 130 + + + false + 95 + Invoice line allowance reason + 1.00 + 10 + 1000 + + + true + AAA + Invoice line charge reason + 1.00 + 10 + 1000 + + + Item description + Item name + + Item buyer's identifier + + + Item seller's identifier + + + Item standar identifier + + + IT + + + Item classification identifier0 + + + S + 5 + + VAT + + + + Color + Red + + + Size + L + + + + 10 + 1 + + false + 1 + 11 + + + + + 1b + 10 + 1000 + + Item name 2 + + E + 0 + + VAT + + + + + 10 + + + diff --git a/pom.xml b/pom.xml index a2af2828..f7a14780 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.mustangproject core - 2.19.2-SNAPSHOT + 2.20.1-SNAPSHOT pom Mustang @@ -71,7 +71,7 @@ 2.20.0 4.0.2 12.8 - 3.0.5 + 3.0.6 2.11 3.1.12 2.1.5 @@ -163,11 +163,11 @@ ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots + https://central.sonatype.com/repository/maven-snapshots/ ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/ diff --git a/validator/pom.xml b/validator/pom.xml index c0e260a7..42fbcadf 100644 --- a/validator/pom.xml +++ b/validator/pom.xml @@ -3,7 +3,7 @@ org.mustangproject core - 2.19.2-SNAPSHOT + 2.20.1-SNAPSHOT 4.0.0 validator @@ -185,6 +185,7 @@ + false diff --git a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java index f8fe6156..d1ba1afc 100644 --- a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java @@ -283,6 +283,7 @@ public class PDFValidator extends Validator { final byte[] cibpdfbrewerSignature = "CIB pdf brewer".getBytes(StandardCharsets.UTF_8); final byte[] lexofficeSignature = "lexoffice".getBytes(StandardCharsets.UTF_8); final byte[] s2IndustriesSignature = "s2industries.ZUGFeRD.PDF".getBytes(StandardCharsets.UTF_8); // https://github.com/stephanstapel/ZUGFeRD-csharp + final byte[] factoorSharpSignature = "FactoorSharp".getBytes(StandardCharsets.UTF_8); // https://github.com/S2-Industries/FactoorSharp final byte[] sevdeskSignature = "sevdesk".getBytes(StandardCharsets.UTF_8); if (ByteArraySearcher.contains(fileContents, symtraxSignature)) { @@ -305,6 +306,8 @@ public class PDFValidator extends Validator { Signature = "Lexware office"; } else if (ByteArraySearcher.contains(fileContents, s2IndustriesSignature)) { Signature = "ZUGFeRD.PDF-csharp"; + } else if (ByteArraySearcher.contains(fileContents, factoorSharpSignature)) { + Signature = "FactoorSharp"; } else if (ByteArraySearcher.contains(fileContents, sevdeskSignature)) { Signature = "sevdesk"; }