diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 648ba71f1..04c8b5dc3 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -38,7 +38,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public class Invoice implements IExportableTransaction { - protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null; + protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null; protected Date issueDate = null, dueDate = null, deliveryDate = null; protected TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null; protected ArrayList<CashDiscount> cashDiscounts = null; @@ -57,7 +57,12 @@ public class Invoice implements IExportableTransaction { protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(), Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>(); protected IZUGFeRDPaymentTerms paymentTerms = null; + + protected String invoiceReferencedDocumentID = null; protected Date invoiceReferencedIssueDate; + // New field for storing Invoiced Object Identifier (BG-3) + protected ArrayList<ReferencedDocument> invoiceReferencedDocuments = null; + protected String specifiedProcuringProjectID = null; protected String specifiedProcuringProjectName = null; protected String despatchAdviceReferencedDocumentID = null; @@ -148,6 +153,7 @@ public Invoice setNumber(String number) { */ public Invoice setCorrection(String number) { setInvoiceReferencedDocumentID(number); + addInvoiceReferencedDocument(new ReferencedDocument(number)); documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE; return this; } @@ -546,6 +552,26 @@ public Invoice setSender(TradeParty sender) { return this; } + // Getter for BG-3 + @Override + public ArrayList<ReferencedDocument> getInvoiceReferencedDocuments() { + return invoiceReferencedDocuments; + } + + // Setter for BG-3 + public void setInvoiceReferencedDocuments(ArrayList<ReferencedDocument> invoiceReferencedDocuments) { + this.invoiceReferencedDocuments = invoiceReferencedDocuments; + } + + // Method to add a single ReferencedDocument for BG-3 + public Invoice addInvoiceReferencedDocument(ReferencedDocument doc) { + if (invoiceReferencedDocuments == null) { + invoiceReferencedDocuments = new ArrayList<>(); + } + invoiceReferencedDocuments.add(doc); + return this; + } + @Override public IZUGFeRDAllowanceCharge[] getZFAllowances() { if (Allowances.isEmpty()) { diff --git a/library/src/main/java/org/mustangproject/ReferencedDocument.java b/library/src/main/java/org/mustangproject/ReferencedDocument.java index 9255017fc..7a1c04a04 100644 --- a/library/src/main/java/org/mustangproject/ReferencedDocument.java +++ b/library/src/main/java/org/mustangproject/ReferencedDocument.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Date; + import org.mustangproject.ZUGFeRD.IReferencedDocument; import org.mustangproject.util.NodeMap; import org.w3c.dom.Node; @@ -13,20 +15,33 @@ public class ReferencedDocument implements IReferencedDocument { String issuerAssignedID; String typeCode; String referenceTypeCode; + Date formattedIssueDateTime; public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode) { - this.issuerAssignedID = issuerAssignedID; + this(issuerAssignedID); this.typeCode = typeCode; this.referenceTypeCode = referenceTypeCode; } + public ReferencedDocument(String issuerAssignedID, String typeCode, String referenceTypeCode, Date formattedIssueDateTime) { + this(issuerAssignedID, typeCode, referenceTypeCode); + this.formattedIssueDateTime = formattedIssueDateTime; + } + public ReferencedDocument(String issuerAssignedID, String referenceTypeCode) { + this(issuerAssignedID, "916", referenceTypeCode); // additional invoice related document + } + + public ReferencedDocument(String issuerAssingedID, Date formattedIssueDateTime) { + this(issuerAssingedID); + this.formattedIssueDateTime = formattedIssueDateTime; + } + + public ReferencedDocument(String issuerAssignedID) { this.issuerAssignedID = issuerAssignedID; - this.typeCode = "916"; // additional invoice related document - this.referenceTypeCode = referenceTypeCode; } - /*** + /*** * sets an ID assigned by the sender * @param issuerAssignedID the ID as a string :-) */ @@ -50,6 +65,15 @@ public void setReferenceTypeCode(String referenceTypeCode) { this.referenceTypeCode = referenceTypeCode; } + /** + * issue date of this line + * + * @param formattedIssueDateTime as Date + */ + public void setFormattedIssueDateTime(Date formattedIssueDateTime) { + this.formattedIssueDateTime = formattedIssueDateTime; + } + @Override public String getIssuerAssignedID() { return issuerAssignedID; @@ -65,6 +89,12 @@ public String getReferenceTypeCode() { return referenceTypeCode; } + @Override + public Date getFormattedIssueDateTime() + { + return formattedIssueDateTime; + } + public static ReferencedDocument fromNode(Node node) { if (!node.hasChildNodes()) { return null; @@ -72,6 +102,7 @@ public static ReferencedDocument fromNode(Node node) { NodeMap nodes = new NodeMap(node); return new ReferencedDocument(nodes.getAsStringOrNull("IssuerAssignedID", "ID"), nodes.getAsStringOrNull("TypeCode", "DocumentTypeCode"), - nodes.getAsStringOrNull("ReferenceTypeCode")); + nodes.getAsStringOrNull("ReferenceTypeCode"), + XMLTools.tryDate(nodes.getAsStringOrNull("FormattedIssueDateTime"))); } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java index 0a67a6beb..c7be4d28c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java @@ -30,11 +30,13 @@ * */ import java.math.BigDecimal; -import java.util.Date; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.mustangproject.FileAttachment; import org.mustangproject.IncludedNote; +import org.mustangproject.ReferencedDocument; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; /*** @@ -424,13 +426,23 @@ default String getBuyerOrderReferencedDocumentID() { * * @return the ID of the document */ + @Deprecated default String getInvoiceReferencedDocumentID() { return null; } + @Deprecated default Date getInvoiceReferencedIssueDate() { return null; } + + /** + * Getter for BG-3 + * @return list of documents + */ + default ArrayList<ReferencedDocument> getInvoiceReferencedDocuments() { + return null; + } /** * get the issue timestamp of the BuyerOrderReferencedDocument, which sits in diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IReferencedDocument.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IReferencedDocument.java index 9d46d9b6d..92ee27619 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IReferencedDocument.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IReferencedDocument.java @@ -1,5 +1,7 @@ package org.mustangproject.ZUGFeRD; +import java.util.Date; + public interface IReferencedDocument { /*** @@ -20,4 +22,12 @@ public interface IReferencedDocument { */ String getReferenceTypeCode(); + /*** + * + * issue date of this line + * + * @return date of the issue + */ + Date getFormattedIssueDateTime(); + } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java index 1d2a30e08..7347d4220 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java @@ -459,6 +459,19 @@ public void generateXML(IExportableTransaction trans) { } xml += "</ram:InvoiceReferencedDocument>"; } + if (trans.getInvoiceReferencedDocuments() != null) { + for (var doc : trans.getInvoiceReferencedDocuments()) { + xml += "<ram:InvoiceReferencedDocument>" + + "<ram:IssuerAssignedID>" + + XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>"; + if (doc.getFormattedIssueDateTime() != null) { + xml += "<ram:FormattedIssueDateTime>" + + DATE.qdtFormat(doc.getFormattedIssueDateTime()) + + "</ram:FormattedIssueDateTime>"; + } + xml += "</ram:InvoiceReferencedDocument>"; + } + } xml += "</ram:ApplicableHeaderTradeSettlement>"; // + " <IncludedSupplyChainTradeLineItem>" diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 6bd4f95d9..9faa8119c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -893,6 +893,19 @@ public void generateXML(IExportableTransaction trans) { } xml += "</ram:InvoiceReferencedDocument>"; } + if (trans.getInvoiceReferencedDocuments() != null) { + for (var doc : trans.getInvoiceReferencedDocuments()) { + xml += "<ram:InvoiceReferencedDocument>" + + "<ram:IssuerAssignedID>" + + XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>"; + if (doc.getFormattedIssueDateTime() != null) { + xml += "<ram:FormattedIssueDateTime>" + + DATE.qdtFormat(doc.getFormattedIssueDateTime()) + + "</ram:FormattedIssueDateTime>"; + } + xml += "</ram:InvoiceReferencedDocument>"; + } + } xml += "</ram:ApplicableHeaderTradeSettlement>"; // + "<IncludedSupplyChainTradeLineItem>\n" diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index ad3d3058e..aa249cd6c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -821,6 +821,23 @@ public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseEx } zpp.setInvoiceReferencedDocumentID(extractString("//*[local-name()=\"InvoiceReferencedDocument\"]/*[local-name()=\"IssuerAssignedID\"]|//*[local-name()=\"BillingReference\"]/*[local-name()=\"InvoiceDocumentReference\"]/*[local-name()=\"ID\"]")); + xpr = xpath.compile("//*[local-name()=\"InvoiceReferencedDocument\"]"); + NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + if (nodes.getLength() != 0) { + for (int i = 0; i < nodes.getLength(); i++) { + + Node currentItemNode = nodes.item(i); + ReferencedDocument doc = ReferencedDocument.fromNode(currentItemNode); + if (doc != null + && (!Objects.equals(zpp.getInvoiceReferencedDocumentID(), doc.getIssuerAssignedID()) + || !Objects.equals(zpp.getInvoiceReferencedIssueDate(), doc.getFormattedIssueDateTime()))) + { + zpp.addInvoiceReferencedDocument(doc); + } + } + } + zpp.setOwnOrganisationName(extractString("//*[local-name()=\"SellerTradeParty\"]/*[local-name()=\"Name\"]|//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyName\"]").trim()); String rounding = extractString("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"RoundingAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"Party\"]/*[local-name()=\"PayableRoundingAmount\"]"); @@ -839,7 +856,7 @@ public Invoice extractInto(Invoice zpp) throws XPathExpressionException, ParseEx } xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]"); - NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); if (nodes.getLength() != 0) { for (int i = 0; i < nodes.getLength(); i++) { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java index ba53bc3ad..b8012b577 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2PushTest.java @@ -542,7 +542,7 @@ public void testPushEdge() { .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) - .setInvoiceReferencedDocumentID("abc123") + .setInvoiceReferencedDocumentID("abc123").addInvoiceReferencedDocument(new ReferencedDocument("abcd1234")) ); } catch (ParseException e) { e.printStackTrace(); @@ -587,6 +587,8 @@ public void testPushEdge() { Invoice i = zii.extractInvoice(); assertEquals("abc123", i.getInvoiceReferencedDocumentID()); + assertEquals(1, i.getInvoiceReferencedDocuments().size()); + assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID()); assertEquals("4304171000002", i.getRecipient().getGlobalID()); assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID()); assertEquals(orgID, i.getSender().getID());