ZUGFeRD2PullProvider generates invalid XML for payments via SEPA direct debit, if a DirectDebitMandateID is provided.
The XML elements in <ram:SpecifiedTradePaymentTerms> are in the wrong order.
This leads to this error message during validation:
Invalid content was found starting with element '{"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":DueDateDateTime}'. One of '{"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":DirectDebitMandateID, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":PartialPaymentPercent, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":PaymentMeansID, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":PartialPaymentAmount, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":ApplicableTradePaymentPenaltyTerms, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":ApplicableTradePaymentDiscountTerms, "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100":PayeeTradeParty}' is expected.
According to the XML schema, the <ram:DueDateDateTime/> element must be placed before the <ram:DirectDebitMandateID/> element:
This is a quick and hacky workaround, via a custom ZUGFeRD2PullProvider and ZUGFeRDExporterFromPDFA. It's written in Kotlin but can be easyly adopted to Java.
class CustomZUGFeRDExporterFromPDFA : ZUGFeRDExporterFromPDFA() {
override fun determineAndSetExporter(pdfAVersion: Int) {
super.determineAndSetExporter(pdfAVersion)
if (theExporter == null) {
return
}
if (theExporter is ZUGFeRDExporterFromA3) {
theExporter = object : ZUGFeRDExporterFromA3() {
init {
setXMLProvider(CustomZUGFeRD2PullProvider())
}
}
return
}
if (theExporter is ZUGFeRDExporterFromA1) {
theExporter = object : ZUGFeRDExporterFromA1() {
init {
setXMLProvider(CustomZUGFeRD2PullProvider())
}
}
return
}
}
}
class CustomZUGFeRD2PullProvider : ZUGFeRD2PullProvider() {
companion object {
val elementOrderForSpecifiedTradePaymentTerms = listOf(
"ID",
"FromEventCode",
"SettlementPeriodMeasure",
"Description",
"DueDateDateTime",
"TypeCode",
"InstructionTypeCode",
"DirectDebitMandateID",
"PartialPaymentPercent",
"PaymentMeansID",
"PartialPaymentAmount",
"ApplicableTradePaymentPenaltyTerms",
"ApplicableTradePaymentDiscountTerms",
"PayeeTradeParty",
)
}
override fun generateXML(trans: IExportableTransaction?) {
super.generateXML(trans)
val doc = DocumentHelper.parseText(
zugferdData.toString(Charsets.UTF_8)
)
@Suppress("SpellCheckingInspection")
val namespaceMap = mapOf(
"ram" to "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
)
val xpath = doc.createXPath("//ram:SpecifiedTradePaymentTerms")
xpath.setNamespaceURIs(namespaceMap)
xpath.selectNodes(doc)
.forEach { node -> fixSpecifiedTradePaymentTerms(node as Element) }
zugferdData = XMLTools.removeBOM(
doc.asXML().toByteArray(charset = Charsets.UTF_8)
)
}
private fun fixSpecifiedTradePaymentTerms(element: Element) {
val childElements = buildList {
element.elements().forEach { child ->
element.remove(child)
add(child)
}
}
elementOrderForSpecifiedTradePaymentTerms.forEach { childName ->
childElements
.filter { it.name == childName }
.forEach { child ->
element.add(child)
}
}
}
}
ZUGFeRD2PullProvider
generates invalid XML for payments via SEPA direct debit, if a DirectDebitMandateID is provided.The XML elements in
<ram:SpecifiedTradePaymentTerms>
are in the wrong order.This leads to this error message during validation:
According to the XML schema, the
<ram:DueDateDateTime/>
element must be placed before the<ram:DirectDebitMandateID/>
element:This is a quick and hacky workaround, via a custom
ZUGFeRD2PullProvider
andZUGFeRDExporterFromPDFA
. It's written in Kotlin but can be easyly adopted to Java.