diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index fc71df6e..8461e977 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -41,7 +41,7 @@ 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 Date issueDate = null, dueDate = null, deliveryDate = null; - protected TradeParty sender = null, recipient = null, deliveryAddress = null; + protected TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null; protected ArrayList cashDiscounts = null; @JsonDeserialize(contentAs = Item.class) protected ArrayList ZFItems = null; @@ -577,6 +577,22 @@ public class Invoice implements IExportableTransaction { this.deliveryAddress = deliveryAddress; return this; } + + @Override + public TradeParty getPayee() { + return this.payee; + } + + /*** + * if the payee is not the seller, it can be specified here + * @param payee the payment receiving organisation + * @return fluent setter + */ + public Invoice setPayee(TradeParty payee) { + this.payee = payee; + return this; + } + /*** * Adds a cash discount (skonto) * @param c the CashDiscount percent/period combination diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java index 87a29aaa..f43b5f62 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IExportableTransaction.java @@ -450,11 +450,19 @@ public interface IExportableTransaction { * * @return the IZUGFeRDExportableTradeParty delivery address */ - default IZUGFeRDExportableTradeParty getDeliveryAddress() { return null; } + /*** + * payee / payment receiver, if different from seller, ram:Payee (only supported for zf2) + * + * @return the IZUGFeRDExportableTradeParty payment receiver, if different from sellver + */ + default IZUGFeRDExportableTradeParty getPayee() { + return null; + } + /*** * specifies the document level delivery period, will be included in a * BillingSpecifiedPeriod element diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index eaefecaa..bafa9c3c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -153,11 +153,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if ((party.getContact() != null) && (isSender || profile == Profiles.getByName("EN16931") || profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { xml += ""; if (party.getContact().getName() != null) { - xml += "" + XMLTools.encodeXML(party.getContact().getName()) + xml += "" + + XMLTools.encodeXML(party.getContact().getName()) + ""; } if (party.getContact().getPhone() != null) { - xml += "" + XMLTools.encodeXML(party.getContact().getPhone()) + "" + ""; @@ -198,7 +198,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + ""; } -//country IS mandatory + //country IS mandatory xml += "" + XMLTools.encodeXML(party.getCountry()) + "" + ""; @@ -226,6 +226,30 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } + protected String getTradePartyPayeeAsXML(IZUGFeRDExportableTradeParty party) { + String xml = ""; + // According EN16931 either GlobalID or seller assigned ID might be present for a Payee + if (party.getID() != null) { + xml += "" + XMLTools.encodeXML(party.getID()) + ""; + } + if ((party.getGlobalIDScheme() != null) && (party.getGlobalID() != null)) { + xml += "" + + XMLTools.encodeXML(party.getGlobalID()) + + ""; + } + xml += "" + XMLTools.encodeXML(party.getName()) + ""; + + if (party.getLegalOrganisation() != null) { + xml += " "; + if (party.getLegalOrganisation().getSchemedID() != null) { + xml += "" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getID()) + ""; + } + xml += ""; + } + + return xml; + } + /*** * returns the XML for a charge or allowance on item level @@ -245,8 +269,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } String reason = ""; - if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended"))) { - // only in extended profile + if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { reason = "" + XMLTools.encodeXML(allowance.getReason()) + ""; } String reasonCode = ""; @@ -281,8 +304,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } String reason = ""; - if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended"))) { - // only in extended profile + if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { reason = "" + XMLTools.encodeXML(allowance.getReason()) + ""; } String reasonCode = ""; @@ -586,6 +608,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { ""; } + if (this.trans.getPayee() != null) { + xml += "" + + getTradePartyPayeeAsXML(this.trans.getPayee()) + + ""; + } + if (trans.getDeliveryDate() != null) { xml += "" diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 117254fb..54174335 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -74,6 +74,10 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*"); NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + + xpr = xpath.compile("//*[local-name()=\"PayeeTradeParty\"]|//*[local-name()=\"PayeeParty\"]/*"); + NodeList payeeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]"); NodeList ExchangedDocumentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); @@ -282,8 +286,13 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter { } zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode); + bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail)); + if (payeeNodes.getLength() > 0) { + zpp.setPayee(new TradeParty(payeeNodes)); + } + if (buyerOrderIssuerAssignedID != null) { zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID); } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java index 56dfeb5e..ae2e9edf 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java @@ -95,6 +95,7 @@ public class XRTest extends TestCase { .setReferenceNumber("991-01484-64")//leitweg-id // not using any VAT, this is also a test of zero-rated goods: .setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0))) + .setPayee( new TradeParty().setName("VR Factoring GmbH").setID("DE813838785").setLegalOrganisation(new LegalOrganisation("0199", "391200LDDFJDMIPPMZ54"))) .embedFileInXML(fe1); @@ -109,6 +110,9 @@ public class XRTest extends TestCase { .asInt() .isEqualTo(1); //2 errors are OK because there is a known bug + assertThat(theXML).valueByXPath("count(//*[local-name()='PayeeTradeParty'])") + .asInt() + .isEqualTo(1); assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']") .asDouble() diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index b281a67c..b27572eb 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -139,6 +139,9 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { assertEquals("DE", invoice.getSender().getCountry()); assertEquals("Stadthausen", invoice.getSender().getLocation()); + assertTrue(invoice.getPayee() != null); + assertEquals("VR Factoring GmbH", invoice.getPayee().getName()); + TransactionCalculator tc = new TransactionCalculator(invoice); assertEquals(new BigDecimal("571.04"), tc.getGrandTotal()); @@ -285,6 +288,8 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { TransactionCalculator tc = new TransactionCalculator(invoice); assertEquals(new BigDecimal("1.00"), tc.getGrandTotal()); + assertTrue(invoice.getPayee() != null); + assertEquals("VR Factoring GmbH", invoice.getPayee().getName()); } /** diff --git a/library/src/test/resources/testout-ZF2new.ubl.xml b/library/src/test/resources/testout-ZF2new.ubl.xml index 35eabf13..9be2f765 100644 --- a/library/src/test/resources/testout-ZF2new.ubl.xml +++ b/library/src/test/resources/testout-ZF2new.ubl.xml @@ -66,6 +66,11 @@ + + + VR Factoring GmbH + + 2017-05-07