Merge branch 'master' of github.com:ZUGFeRD/mustangproject

This commit is contained in:
Jochen Stärk
2026-05-16 08:49:25 +02:00
5 changed files with 76 additions and 7 deletions

View File

@@ -41,8 +41,9 @@ public class Invoice implements IExportableTransaction {
protected boolean testIndicator; protected boolean testIndicator;
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 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 String deliveryTypeCode;
protected Date issueDate = null, dueDate = null, deliveryDate = null; protected Date issueDate = null, dueDate = null, deliveryDate = null;
protected TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null, invoicer = null, invoicee = null; protected TradeParty sender = null, recipient = null, deliveryAddress = null, endCustomerDeliveryAddress = null, payee = null, invoicer = null, invoicee = null;
protected ArrayList<CashDiscount> cashDiscounts = null; protected ArrayList<CashDiscount> cashDiscounts = null;
@JsonDeserialize(contentAs = Item.class) @JsonDeserialize(contentAs = Item.class)
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null; protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
@@ -332,6 +333,15 @@ public class Invoice implements IExportableTransaction {
return this; return this;
} }
public String getDeliveryTypeCode() {
return deliveryTypeCode;
}
public Invoice setDeliveryTypeCode(String deliveryTypeCode) {
this.deliveryTypeCode = deliveryTypeCode;
return this;
}
@Override @Override
public String getBuyerOrderReferencedDocumentID() { public String getBuyerOrderReferencedDocumentID() {
return buyerOrderReferencedDocumentID; return buyerOrderReferencedDocumentID;
@@ -790,6 +800,16 @@ public class Invoice implements IExportableTransaction {
return this; return this;
} }
@Override
public TradeParty getEndCustomerDeliveryAddress() {
return endCustomerDeliveryAddress;
}
public Invoice setEndCustomerDeliveryAddress(TradeParty endCustomerDeliveryAddress) {
this.endCustomerDeliveryAddress = endCustomerDeliveryAddress;
return this;
}
@Override @Override
public TradeParty getPayee() { public TradeParty getPayee() {
return this.payee; return this.payee;

View File

@@ -438,6 +438,10 @@ public interface IExportableTransaction {
return null; return null;
} }
default String getDeliveryTypeCode() {
return null;
}
/** /**
* get the ID of the SellerOrderReferencedDocument, which sits in the * get the ID of the SellerOrderReferencedDocument, which sits in the
* ApplicableSupplyChainTradeAgreement/ApplicableHeaderTradeAgreement * ApplicableSupplyChainTradeAgreement/ApplicableHeaderTradeAgreement
@@ -513,6 +517,15 @@ public interface IExportableTransaction {
return null; return null;
} }
/***
* ultimate delivery address, i.e. ram:UltimateShipToTradeParty (only supported for zf2)
*
* @return the IZUGFeRDExportableTradeParty delivery address
*/
default IZUGFeRDExportableTradeParty getEndCustomerDeliveryAddress() {
return null;
}
/*** /***
* payee / payment receiver, if different from seller, ram:Payee (only supported for zf2) * payee / payment receiver, if different from seller, ram:Payee (only supported for zf2)
* *

View File

@@ -160,29 +160,31 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
if ((party.getContact() != null) && (isSender || profile == Profiles.getByName("EN16931") || profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) { if ((party.getContact() != null) && (isSender || profile == Profiles.getByName("EN16931") || profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) {
xml += "<ram:DefinedTradeContact>"; String definedTradeContactXML = "";
if (party.getContact().getName() != null) { if (party.getContact().getName() != null) {
xml += "<ram:PersonName>" definedTradeContactXML += "<ram:PersonName>"
+ XMLTools.encodeXML(party.getContact().getName()) + XMLTools.encodeXML(party.getContact().getName())
+ "</ram:PersonName>"; + "</ram:PersonName>";
} }
if (party.getContact().getPhone() != null) { if (party.getContact().getPhone() != null) {
xml += "<ram:TelephoneUniversalCommunication><ram:CompleteNumber>" definedTradeContactXML += "<ram:TelephoneUniversalCommunication><ram:CompleteNumber>"
+ XMLTools.encodeXML(party.getContact().getPhone()) + "</ram:CompleteNumber>" + XMLTools.encodeXML(party.getContact().getPhone()) + "</ram:CompleteNumber>"
+ "</ram:TelephoneUniversalCommunication>"; + "</ram:TelephoneUniversalCommunication>";
} }
if ((party.getContact().getFax() != null) && (profile == Profiles.getByName("Extended"))) { if ((party.getContact().getFax() != null) && (profile == Profiles.getByName("Extended"))) {
xml += "<ram:FaxUniversalCommunication><ram:CompleteNumber>" definedTradeContactXML += "<ram:FaxUniversalCommunication><ram:CompleteNumber>"
+ XMLTools.encodeXML(party.getContact().getFax()) + "</ram:CompleteNumber>" + XMLTools.encodeXML(party.getContact().getFax()) + "</ram:CompleteNumber>"
+ "</ram:FaxUniversalCommunication>"; + "</ram:FaxUniversalCommunication>";
} }
if (party.getContact().getEMail() != null) { if (party.getContact().getEMail() != null) {
xml += "<ram:EmailURIUniversalCommunication><ram:URIID>" definedTradeContactXML += "<ram:EmailURIUniversalCommunication><ram:URIID>"
+ XMLTools.encodeXML(party.getContact().getEMail()) + "</ram:URIID>" + XMLTools.encodeXML(party.getContact().getEMail()) + "</ram:URIID>"
+ "</ram:EmailURIUniversalCommunication>"; + "</ram:EmailURIUniversalCommunication>";
} }
xml += "</ram:DefinedTradeContact>"; if (!definedTradeContactXML.isEmpty()) {
xml += "<ram:DefinedTradeContact>" + definedTradeContactXML + "</ram:DefinedTradeContact>";
}
} }
xml += "<ram:PostalTradeAddress>"; xml += "<ram:PostalTradeAddress>";
@@ -661,6 +663,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:SellerTradeParty>" + getTradePartyAsXML(trans.getSender(), true, false) + "</ram:SellerTradeParty>"; xml += "<ram:SellerTradeParty>" + getTradePartyAsXML(trans.getSender(), true, false) + "</ram:SellerTradeParty>";
xml += "<ram:BuyerTradeParty>" + getTradePartyAsXML(trans.getRecipient(), false, false) + "</ram:BuyerTradeParty>"; xml += "<ram:BuyerTradeParty>" + getTradePartyAsXML(trans.getRecipient(), false, false) + "</ram:BuyerTradeParty>";
if (trans.getDeliveryTypeCode() != null && getProfile() == Profiles.getByName("Extended")) {
xml += "<ram:ApplicableTradeDeliveryTerms>"
+ "<ram:DeliveryTypeCode>"
+ trans.getDeliveryTypeCode()
+ "</ram:DeliveryTypeCode>"
+ "</ram:ApplicableTradeDeliveryTerms>";
}
if (trans.getSellerOrderReferencedDocumentID() != null && !trans.getSellerOrderReferencedDocumentID().trim().isEmpty()) { if (trans.getSellerOrderReferencedDocumentID() != null && !trans.getSellerOrderReferencedDocumentID().trim().isEmpty()) {
xml += "<ram:SellerOrderReferencedDocument>" xml += "<ram:SellerOrderReferencedDocument>"
+ "<ram:IssuerAssignedID>" + "<ram:IssuerAssignedID>"
@@ -732,6 +741,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) + getTradePartyAsXML(this.trans.getDeliveryAddress(), false, true) +
"</ram:ShipToTradeParty>"; "</ram:ShipToTradeParty>";
} }
if (this.trans.getEndCustomerDeliveryAddress() != null) {
xml += "<ram:UltimateShipToTradeParty>" +
getTradePartyAsXML(this.trans.getEndCustomerDeliveryAddress(), false, true) +
"</ram:UltimateShipToTradeParty>";
}
if (trans.getDeliveryDate() != null) { if (trans.getDeliveryDate() != null) {

View File

@@ -396,12 +396,19 @@ public class ZUGFeRDInvoiceImporter {
XPath xpath = xpathFact.newXPath(); XPath xpath = xpathFact.newXPath();
XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*"); XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*");
NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
XPathExpression shipEx = xpath.compile("//*[local-name()=\"ShipToTradeParty\"]"); XPathExpression shipEx = xpath.compile("//*[local-name()=\"ShipToTradeParty\"]");
NodeList deliveryNodes = (NodeList) shipEx.evaluate(getDocument(), XPathConstants.NODESET); NodeList deliveryNodes = (NodeList) shipEx.evaluate(getDocument(), XPathConstants.NODESET);
if (deliveryNodes.getLength() > 0) { if (deliveryNodes.getLength() > 0) {
zpp.setDeliveryAddress(new TradeParty(deliveryNodes)); zpp.setDeliveryAddress(new TradeParty(deliveryNodes));
} }
XPathExpression ultimateShipEx = xpath.compile("//*[local-name()=\"UltimateShipToTradeParty\"]");
NodeList ultimateDeliveryNodes = (NodeList) ultimateShipEx.evaluate(getDocument(), XPathConstants.NODESET);
if (ultimateDeliveryNodes.getLength() > 0) {
zpp.setEndCustomerDeliveryAddress(new TradeParty(ultimateDeliveryNodes));
}
List<IncludedNote> includedNotes = new ArrayList<>(); List<IncludedNote> includedNotes = new ArrayList<>();
//UBL... //UBL...
@@ -749,6 +756,16 @@ public class ZUGFeRDInvoiceImporter {
NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes(); NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes();
for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) { for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) {
if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName() != null) { if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName() != null) {
if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("ApplicableTradeDeliveryTerms")) {
NodeList applicableTradeDeliveryTermsChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes();
for (int applicableTradeDeliveryTermsChildIndex = 0; applicableTradeDeliveryTermsChildIndex < applicableTradeDeliveryTermsChilds.getLength(); applicableTradeDeliveryTermsChildIndex++) {
if ((applicableTradeDeliveryTermsChilds.item(applicableTradeDeliveryTermsChildIndex).getLocalName() != null)
&& (applicableTradeDeliveryTermsChilds.item(applicableTradeDeliveryTermsChildIndex).getLocalName().equals("DeliveryTypeCode"))) {
zpp.setDeliveryTypeCode(XMLTools.trimOrNull(applicableTradeDeliveryTermsChilds.item(applicableTradeDeliveryTermsChildIndex)));
}
}
}
if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("BuyerOrderReferencedDocument")) { if (headerTradeAgreementChilds.item(agreementChildIndex).getLocalName().equals("BuyerOrderReferencedDocument")) {
NodeList buyerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes(); NodeList buyerOrderChilds = headerTradeAgreementChilds.item(agreementChildIndex).getChildNodes();
for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) { for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) {

View File

@@ -605,6 +605,7 @@ public class ZF2PushTest extends TestCase {
.setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo)) .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")) .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")) .setDeliveryAddress(new TradeParty("just the other side of the street", "teststr.12a", "55232", "Entenhausen", "DE").addVATID("DE47110"))
.setEndCustomerDeliveryAddress(new TradeParty("Max Mustermann", "Glückswinkel 42", "98765", "Musterhausen", "DE"))
.setContractReferencedDocument(contractID) .setContractReferencedDocument(contractID)
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addGlobalID(gln).setEmail("recipient@test.org").addVATID("DE4711") .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")) .setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")).setAdditionalAddress("Hinterhaus 3"))
@@ -623,6 +624,7 @@ public class ZF2PushTest extends TestCase {
.setTenderReferencedDocument(dr1) .setTenderReferencedDocument(dr1)
.setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE) .setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
.setInvoiceReferencedDocumentID("abc123").addInvoiceReferencedDocument(new ReferencedDocument("abcd1234")) .setInvoiceReferencedDocumentID("abc123").addInvoiceReferencedDocument(new ReferencedDocument("abcd1234"))
.setDeliveryTypeCode("EXW")
); );
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
@@ -652,6 +654,7 @@ public class ZF2PushTest extends TestCase {
assertTrue(zi.getUTF8().contains(occurrenceFrom)); assertTrue(zi.getUTF8().contains(occurrenceFrom));
assertTrue(zi.getUTF8().contains(occurrenceTo)); assertTrue(zi.getUTF8().contains(occurrenceTo));
assertTrue(zi.getUTF8().contains(contractID)); assertTrue(zi.getUTF8().contains(contractID));
assertTrue(zi.getUTF8().contains("EXW"));
assertEquals(zi.importedInvoice.getZFItems()[0].getId(), "a123"); assertEquals(zi.importedInvoice.getZFItems()[0].getId(), "a123");
assertEquals(zi.importedInvoice.getZFItems()[0].getDeliveryNoteReferencedDocumentID(), "deliverynote123"); assertEquals(zi.importedInvoice.getZFItems()[0].getDeliveryNoteReferencedDocumentID(), "deliverynote123");
assertEquals(zi.importedInvoice.getZFItems()[0].getDeliveryNoteReferencedDocumentLineID(), "deliverypos456"); assertEquals(zi.importedInvoice.getZFItems()[0].getDeliveryNoteReferencedDocumentLineID(), "deliverypos456");
@@ -677,6 +680,7 @@ public class ZF2PushTest extends TestCase {
Invoice i = zii.extractInvoice(); Invoice i = zii.extractInvoice();
assertEquals("abc123", i.getInvoiceReferencedDocumentID()); assertEquals("abc123", i.getInvoiceReferencedDocumentID());
assertEquals("EXW", i.getDeliveryTypeCode());
assertEquals(1, i.getInvoiceReferencedDocuments().size()); assertEquals(1, i.getInvoiceReferencedDocuments().size());
assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID()); assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID());
assertEquals("4304171000002", i.getRecipient().getGlobalID()); assertEquals("4304171000002", i.getRecipient().getGlobalID());
@@ -692,6 +696,7 @@ public class ZF2PushTest extends TestCase {
assertNotNull(i.getInvoicer()); assertNotNull(i.getInvoicer());
assertNotNull(i.getInvoicee()); assertNotNull(i.getInvoicee());
assertNotNull(i.getEndCustomerDeliveryAddress());
} catch (XPathExpressionException e) { } catch (XPathExpressionException e) {
fail("XPathExpressionException should not be raised"); fail("XPathExpressionException should not be raised");
} catch (ParseException e) { } catch (ParseException e) {