Merge pull request #952 from langfr/InvoiceeInvoicer

Add optional Invoicer and Invoicee for Extended profile.
This commit is contained in:
Jochen Staerk
2025-11-13 15:55:47 +01:00
committed by GitHub
5 changed files with 81 additions and 3 deletions

View File

@@ -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, 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 TradeParty sender = null, recipient = null, deliveryAddress = null, payee = null, invoicer = null, invoicee = null;
protected ArrayList<CashDiscount> cashDiscounts = null;
@JsonDeserialize(contentAs = Item.class)
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
@@ -752,6 +752,36 @@ public class Invoice implements IExportableTransaction {
return this;
}
@Override
public TradeParty getInvoicer() {
return this.invoicer;
}
/***
* if the invoicer is not the seller, it can be specified here
* @param invoicer the invoice issuing organisation
* @return fluent setter
*/
public Invoice setInvoicer(TradeParty invoicer) {
this.invoicer = invoicer;
return this;
}
@Override
public TradeParty getInvoicee() {
return this.invoicee;
}
/***
* if the invoicee is not the buyer, it can be specified here
* @param invoicee the invoice receiving organisation
* @return fluent setter
*/
public Invoice setInvoicee(TradeParty invoicee) {
this.invoicee = invoicee;
return this;
}
/***
* Adds a cash discount (skonto)
* @param c the CashDiscount percent/period combination

View File

@@ -38,6 +38,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import org.mustangproject.FileAttachment;
import org.mustangproject.IncludedNote;
import org.mustangproject.ReferencedDocument;
import org.mustangproject.TradeParty;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
/***
@@ -502,12 +503,30 @@ public interface IExportableTransaction {
/***
* payee / payment receiver, if different from seller, ram:Payee (only supported for zf2)
*
* @return the IZUGFeRDExportableTradeParty payment receiver, if different from sellver
* @return the IZUGFeRDExportableTradeParty payment receiver, if different from seller
*/
default IZUGFeRDExportableTradeParty getPayee() {
return null;
}
/***
* invoicer / invoice sender, if different from seller, ram:InvoicerTradeParty
*
* @return the IZUGFeRDExportableTradeParty invoice sender, if different from seller
*/
default IZUGFeRDExportableTradeParty getInvoicer() {
return null;
}
/***
* invoicee / invoice receiver, if different from buyer, ram:InvoiceeTradeParty
*
* @return the IZUGFeRDExportableTradeParty invoice receiver, if different from buyer
*/
default IZUGFeRDExportableTradeParty getInvoicee() {
return null;
}
/***
* specifies the document level delivery period, will be included in a
* BillingSpecifiedPeriod element

View File

@@ -689,6 +689,17 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:PaymentReference>" + XMLTools.encodeXML(trans.getPaymentReference()) + "</ram:PaymentReference>";
}
xml += "<ram:InvoiceCurrencyCode>" + trans.getCurrency() + "</ram:InvoiceCurrencyCode>";
if (this.trans.getInvoicer() != null && getProfile() == Profiles.getByName("Extended")) {
xml += "<ram:InvoicerTradeParty>" +
getTradePartyAsXML(this.trans.getInvoicer(), false, false) +
"</ram:InvoicerTradeParty>";
}
if (this.trans.getInvoicee() != null && getProfile() == Profiles.getByName("Extended")) {
xml += "<ram:InvoiceeTradeParty>" +
getTradePartyAsXML(this.trans.getInvoicee(), false, false) +
"</ram:InvoiceeTradeParty>";
}
if (this.trans.getPayee() != null) {
xml += "<ram:PayeeTradeParty>" +
getTradePartyPayeeAsXML(this.trans.getPayee()) +

View File

@@ -315,7 +315,7 @@ public class ZUGFeRDInvoiceImporter {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//REDHAT
//https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf
dbf.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
@@ -436,6 +436,12 @@ public class ZUGFeRDInvoiceImporter {
xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*");
NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
xpr = xpath.compile("//*[local-name()=\"InvoicerTradeParty\"]");
NodeList invoicerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
xpr = xpath.compile("//*[local-name()=\"InvoiceeTradeParty\"]");
NodeList invoiceeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
xpr = xpath.compile("//*[local-name()=\"PayeeTradeParty\"]");
NodeList payeeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
@@ -966,6 +972,14 @@ public class ZUGFeRDInvoiceImporter {
}
if (invoicerNodes.getLength() > 0) {
zpp.setInvoicer(new TradeParty(invoicerNodes));
}
if (invoiceeNodes.getLength() > 0) {
zpp.setInvoicee(new TradeParty(invoiceeNodes));
}
if (payeeNodes.getLength() > 0) {
zpp.setPayee(new TradeParty(payeeNodes));
}

View File

@@ -607,6 +607,8 @@ 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").
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)))
@@ -674,6 +676,8 @@ public class ZF2PushTest extends TestCase {
assertEquals("++49555123456",i.getRecipient().getContact().getFax());
assertNotNull(i.getInvoicer());
assertNotNull(i.getInvoicee());
} catch (XPathExpressionException e) {
fail("XPathExpressionException should not be raised");
} catch (ParseException e) {