[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] 02/02: parse balances and batches correctly
From: |
gnunet |
Subject: |
[libeufin] 02/02: parse balances and batches correctly |
Date: |
Tue, 07 Jul 2020 22:15:14 +0200 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository libeufin.
commit da0c0cdc71d27670aba52dc566fb146046bffdc9
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Wed Jul 8 01:38:56 2020 +0530
parse balances and batches correctly
---
nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 19 +-
.../tech/libeufin/nexus/iso20022/Iso20022.kt | 261 +++++++++++++++++----
.../camt.053/de.camt.053.001.02.xml | 128 +++++++++-
util/src/main/kotlin/ISO20022.kt | 69 ------
4 files changed, 357 insertions(+), 120 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index a5b7ac5..9d009f7 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -33,9 +33,11 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import tech.libeufin.nexus.server.serverMain
import tech.libeufin.util.CryptoUtil.hashpw
-import ch.qos.logback.classic.Level
-import ch.qos.logback.classic.LoggerContext
+import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
+import tech.libeufin.nexus.iso20022.parseCamtMessage
+import tech.libeufin.util.XMLUtil
import tech.libeufin.util.setLogLevel
+import java.io.File
val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus")
@@ -58,6 +60,17 @@ class Serve : CliktCommand("Run nexus HTTP server") {
}
}
+class ParseCamt : CliktCommand("Parse a camt file") {
+ private val logLevel by option()
+ private val filename by argument()
+ override fun run() {
+ setLogLevel(logLevel)
+ val camtText = File(filename).readText(Charsets.UTF_8)
+ val res = parseCamtMessage(XMLUtil.parseStringIntoDom(camtText))
+
println(jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(res))
+ }
+}
+
class Superuser : CliktCommand("Add superuser or change pw") {
private val dbName by option().default("libeufin-nexus.sqlite3")
private val username by argument()
@@ -85,6 +98,6 @@ class Superuser : CliktCommand("Add superuser or change pw") {
fun main(args: Array<String>) {
NexusCommand()
- .subcommands(Serve(), Superuser())
+ .subcommands(Serve(), Superuser(), ParseCamt())
.main(args)
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
index 4f8beaf..6958ea8 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
@@ -60,7 +60,16 @@ enum class CashManagementResponseType(@get:JsonValue val
jsonName: String) {
@JsonInclude(JsonInclude.Include.NON_NULL)
data class CamtReport(
+ val id: String,
+ val creationDateTime: String?,
+ val legalSequenceNumber: Int?,
+ val electronicSequenceNumber: Int?,
+ val fromDate: String?,
+ val toDate: String?,
+ val reportingSource: String?,
+ val proprietaryReportingSource: String?,
val account: CashAccount,
+ val balances: List<Balance>,
val entries: List<CamtBankAccountEntry>
)
@@ -80,6 +89,7 @@ data class CashAccount(
val otherId: GenericId?
)
+@JsonInclude(JsonInclude.Include.NON_NULL)
data class Balance(
val type: String?,
val subtype: String?,
@@ -97,7 +107,6 @@ data class CamtParseResult(
val messageType: CashManagementResponseType,
val messageId: String,
val creationDateTime: String,
- val balances: List<Balance>,
val reports: List<CamtReport>
)
@@ -127,6 +136,7 @@ data class PartyIdentification(
val countryOfResidence: String?,
val privateId: PrivateIdentification?,
val organizationId: OrganizationIdentification?,
+ val postalAddress: PostalAddress?,
/**
* Identification that applies to both private parties and organizations.
@@ -134,10 +144,48 @@ data class PartyIdentification(
val otherId: GenericId?
)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+data class PostalAddress(
+ val addressCode: String?,
+ val addressProprietaryId: String?,
+ val addressProprietarySchemeName: String?,
+ val addressProprietaryIssuer: String?,
+ val department: String?,
+ val subDepartment: String?,
+ val streetName: String?,
+ val buildingNumber: String?,
+ val buildingName: String?,
+ val floor: String?,
+ val postBox: String?,
+ val room: String?,
+ val postCode: String?,
+ val townName: String?,
+ val townLocationName: String?,
+ val districtName: String?,
+ val countrySubDivision: String?,
+ val country: String?,
+ val addressLines: List<String>
+)
+
@JsonInclude(JsonInclude.Include.NON_NULL)
data class AgentIdentification(
val name: String?,
+
val bic: String?,
+
+ /**
+ * Legal entity identification.
+ */
+ val lei: String?,
+
+ val clearingSystemMemberId: String?,
+
+ val clearingSystemCode: String?,
+
+ val proprietaryClearingSystemCode: String?,
+
+ val postalAddress: PostalAddress?,
+
val otherId: GenericId?
)
@@ -159,11 +207,16 @@ data class TransactionDetails(
val creditor: PartyIdentification?,
val creditorAccount: CashAccount?,
val creditorAgent: AgentIdentification?,
+ val ultimateCreditor: PartyIdentification?,
+ val ultimateDebtor: PartyIdentification?,
val endToEndId: String? = null,
val paymentInformationId: String? = null,
val messageId: String? = null,
+ val purpose: String?,
+ val proprietaryPurpose: String?,
+
/**
* Currency exchange information for the transaction's amount.
*/
@@ -219,11 +272,11 @@ data class BatchTransaction(
val details: TransactionDetails
)
-
+@JsonInclude(JsonInclude.Include.NON_NULL)
data class Batch(
- val batchTransactions: List<BatchTransaction>,
val messageId: String?,
- val paymentInformationId: String?
+ val paymentInformationId: String?,
+ val batchTransactions: List<BatchTransaction>
)
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -263,9 +316,16 @@ data class CamtBankAccountEntry(
/**
* Value before/after currency exchange before charges have been applied.
+ * Only present if currency exchange happened at the entry level.
*/
val counterValueAmount: CurrencyAmount?,
+ /**
+ * Instructed amount.
+ * Only present if currency exchange happens at the entry level.
+ */
+ val instructedAmount: CurrencyAmount?,
+
/**
* Details of the underlying transaction for type=Simple.
*/
@@ -419,19 +479,74 @@ private fun XmlElementDestructor.extractDateOrDateTime():
String {
}
}
+private fun XmlElementDestructor.extractInnerPostalAddress(): PostalAddress {
+ return PostalAddress(
+ addressCode = maybeUniqueChildNamed("AdrTp") {
maybeUniqueChildNamed("Cd") { it.textContent } },
+ addressProprietaryIssuer = maybeUniqueChildNamed("AdrTp") {
+ maybeUniqueChildNamed("Prtry") {
+ maybeUniqueChildNamed("Issr") { it.textContent }
+ }
+ },
+ addressProprietarySchemeName = maybeUniqueChildNamed("AdrTp") {
+ maybeUniqueChildNamed("Prtry") {
+ maybeUniqueChildNamed("SchmeNm") { it.textContent }
+ }
+ },
+ addressProprietaryId = maybeUniqueChildNamed("AdrTp") {
+ maybeUniqueChildNamed("Prtry") {
+ maybeUniqueChildNamed("Id") { it.textContent }
+ }
+ },
+ buildingName = maybeUniqueChildNamed("BldgNm") { it.textContent },
+ buildingNumber = maybeUniqueChildNamed("BldgNb") { it.textContent },
+ country = maybeUniqueChildNamed("Ctry") { it.textContent },
+ countrySubDivision = maybeUniqueChildNamed("CtrySubDvsn") {
it.textContent },
+ department = maybeUniqueChildNamed("Dept") { it.textContent },
+ districtName = maybeUniqueChildNamed("DstrctNm") { it.textContent },
+ floor = maybeUniqueChildNamed("Flr") { it.textContent },
+ postBox = maybeUniqueChildNamed("PstBx") { it.textContent },
+ postCode = maybeUniqueChildNamed("PstCd") { it.textContent },
+ room = maybeUniqueChildNamed("Room") { it.textContent },
+ streetName = maybeUniqueChildNamed("StrtNm") { it.textContent },
+ subDepartment = maybeUniqueChildNamed("SubDept") { it.textContent },
+ townLocationName = maybeUniqueChildNamed("TwnLctnNm") { it.textContent
},
+ townName = maybeUniqueChildNamed("TwnNm") { it.textContent },
+ addressLines = mapEachChildNamed("AdrLine") { it.textContent }
+ )
+}
+
private fun XmlElementDestructor.extractAgent(): AgentIdentification {
return AgentIdentification(
name = maybeUniqueChildNamed("FinInstnId") {
- maybeUniqueChildNamed("Nm") {
- it.textContent
- }
+ maybeUniqueChildNamed("Nm") { it.textContent }
},
bic = requireUniqueChildNamed("FinInstnId") {
- requireUniqueChildNamed("BIC") {
- it.textContent
+ maybeUniqueChildNamed("BIC") { it.textContent }
+ },
+ lei = requireUniqueChildNamed("FinInstnId") {
+ maybeUniqueChildNamed("LEI") { it.textContent }
+ },
+ clearingSystemCode = requireUniqueChildNamed("FinInstnId") {
+ maybeUniqueChildNamed("ClrSysMmbId") {
+ maybeUniqueChildNamed("ClrSysId") {
+ maybeUniqueChildNamed("Cd") { it.textContent }
+ }
}
},
- otherId = null
+ proprietaryClearingSystemCode = requireUniqueChildNamed("FinInstnId") {
+ maybeUniqueChildNamed("ClrSysMmbId") {
+ maybeUniqueChildNamed("ClrSysId") {
+ maybeUniqueChildNamed("Prtry") { it.textContent }
+ }
+ }
+ },
+ clearingSystemMemberId = requireUniqueChildNamed("FinInstnId") {
+ maybeUniqueChildNamed("ClrSysMmbId") {
+ maybeUniqueChildNamed("MmbId") { it.textContent }
+ }
+ },
+ otherId = requireUniqueChildNamed("FinInstnId") {
maybeUniqueChildNamed("Othr") { extractGenericId() } },
+ postalAddress = requireUniqueChildNamed("FinInstnId") {
maybeUniqueChildNamed("PstlAdr") { extractInnerPostalAddress() } }
)
}
@@ -506,7 +621,8 @@ private fun XmlElementDestructor.extractParty():
PartyIdentification {
otherId = otherId,
privateId = privateId,
organizationId = organizationId,
- countryOfResidence = maybeUniqueChildNamed("CtryOfRes") {
it.textContent }
+ countryOfResidence = maybeUniqueChildNamed("CtryOfRes") {
it.textContent },
+ postalAddress = maybeUniqueChildNamed("PstlAdr") {
extractInnerPostalAddress() }
)
}
@@ -548,10 +664,27 @@ private fun XmlElementDestructor.extractBatches(
var amount = maybeExtractCurrencyAmount()
var creditDebitIndicator = maybeExtractCreditDebitIndicator()
- if (amount == null && numDtls == 1) {
+ val ttlAmt = maybeUniqueChildNamed("Btch") {
+ maybeUniqueChildNamed("TtlAmt") {
+ CurrencyAmount(
+ value = BigDecimal(it.textContent),
+ currency = it.getAttribute("Ccy")
+ )
+ }
+ }
+
+ val ttlCreditDebitIndicator = maybeUniqueChildNamed("Btch") {
+ maybeExtractCreditDebitIndicator()
+ }
+
+ if (amount == null && ttlAmt != null && ttlCreditDebitIndicator !=
null) {
+ amount = ttlAmt
+ creditDebitIndicator = ttlCreditDebitIndicator
+ } else if (amount == null && numDtls == 1) {
amount = outerAmount
creditDebitIndicator = outerCreditDebitIndicator
}
+
if (amount == null || creditDebitIndicator == null) {
throw Error("no amount for inner transaction")
}
@@ -559,7 +692,7 @@ private fun XmlElementDestructor.extractBatches(
val details = extractTransactionDetails(outerAmount,
outerCreditDebitIndicator, false)
BatchTransaction(amount, creditDebitIndicator, details)
}
- Batch(txs, null, null)
+ Batch(null, null, txs)
}
}
@@ -581,8 +714,8 @@ private fun XmlElementDestructor.extractTransactionDetails(
val creditDebitIndicator = maybeExtractCreditDebitIndicator() ?:
outerCreditDebitIndicator
val currencyExchange = maybeUniqueChildNamed("AmtDtls") {
- val cxCntrVal = maybeUniqueChildNamed("CntrValAmt") {
extractMaybeCurrencyExchange() }
- val cxTx = maybeUniqueChildNamed("TxAmt") {
extractMaybeCurrencyExchange() }
+ val cxCntrVal = maybeUniqueChildNamed("CntrValAmt") {
extractMaybeCurrencyExchange() }
+ val cxTx = maybeUniqueChildNamed("TxAmt") {
extractMaybeCurrencyExchange() }
val cxInstr = maybeUniqueChildNamed("InstdAmt") {
extractMaybeCurrencyExchange() }
cxCntrVal ?: cxTx ?: cxInstr
}
@@ -605,7 +738,7 @@ private fun XmlElementDestructor.extractTransactionDetails(
maybeUniqueChildNamed("PmtInfId") { it.textContent }
},
unstructuredRemittanceInformation = maybeUniqueChildNamed("RmtInf") {
- val chunks = mapEachChildNamed("Ustrd", { it.textContent })
+ val chunks = mapEachChildNamed("Ustrd") { it.textContent }
if (chunks.isEmpty()) {
null
} else {
@@ -614,10 +747,14 @@ private fun
XmlElementDestructor.extractTransactionDetails(
} ?: "",
creditorAgent = maybeUniqueChildNamed("RltdAgts") {
maybeUniqueChildNamed("CdtrAgt") { extractAgent() } },
debtorAgent = maybeUniqueChildNamed("RltdAgts") {
maybeUniqueChildNamed("DbtrAgt") { extractAgent() } },
- debtorAccount = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("DbtrAgt") { extractAccount() } },
- creditorAccount = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("CdtrAgt") { extractAccount() } },
+ debtorAccount = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("DbtrAcct") { extractAccount() } },
+ creditorAccount = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("CdtrAcct") { extractAccount() } },
debtor = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("Dbtr") { extractParty() } },
creditor = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("Cdtr") { extractParty() } },
+ proprietaryPurpose = maybeUniqueChildNamed("Purp") {
maybeUniqueChildNamed("Prtry") { it.textContent } },
+ purpose = maybeUniqueChildNamed("Purp") { maybeUniqueChildNamed("Cd")
{ it.textContent } },
+ ultimateCreditor = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("UltmtCdtr") { extractParty() } },
+ ultimateDebtor = maybeUniqueChildNamed("RltdPties") {
maybeUniqueChildNamed("UltmtDbtr") { extractParty() } },
returnInfo = maybeUniqueChildNamed("RtrInf") {
ReturnInfo(
originalBankTransactionCode =
maybeUniqueChildNamed("OrgnlBkTxCd") {
@@ -637,7 +774,6 @@ private fun XmlElementDestructor.extractTransactionDetails(
)
}
-
private fun XmlElementDestructor.extractSingleDetails(
outerAmount: CurrencyAmount,
outerCreditDebitIndicator: CreditDebitIndicator
@@ -687,6 +823,33 @@ private fun
XmlElementDestructor.extractInnerBkTxCd(creditDebitIndicator: Credit
private fun XmlElementDestructor.extractInnerTransactions(): CamtReport {
val account = requireUniqueChildNamed("Acct") { extractAccount() }
+
+ val balances = mapEachChildNamed("Bal") {
+ Balance(
+ type = maybeUniqueChildNamed("Tp") {
+ maybeUniqueChildNamed("CdOrPrtry") {
+ maybeUniqueChildNamed("Cd") { it.textContent }
+ }
+ },
+ proprietaryType = maybeUniqueChildNamed("Tp") {
+ maybeUniqueChildNamed("CdOrPrtry") {
+ maybeUniqueChildNamed("Prtry") { it.textContent }
+ }
+ },
+ date = requireUniqueChildNamed("Dt") { extractDateOrDateTime() },
+ creditDebitIndicator = requireUniqueChildNamed("CdtDbtInd") {
it.textContent }.let {
+ CreditDebitIndicator.valueOf(it)
+ },
+ subtype = maybeUniqueChildNamed("Tp") {
+ maybeUniqueChildNamed("SubTp") { maybeUniqueChildNamed("Cd") {
it.textContent } }
+ },
+ proprietarySubtype = maybeUniqueChildNamed("Tp") {
+ maybeUniqueChildNamed("SubTp") {
maybeUniqueChildNamed("Prtry") { it.textContent } }
+ },
+ amount = extractCurrencyAmount()
+ )
+ }
+
val entries = mapEachChildNamed("Ntry") {
val amount = extractCurrencyAmount()
val status = requireUniqueChildNamed("Sts") { it.textContent }.let {
@@ -701,13 +864,19 @@ private fun
XmlElementDestructor.extractInnerTransactions(): CamtReport {
val acctSvcrRef = maybeUniqueChildNamed("AcctSvcrRef") {
it.textContent }
val entryRef = maybeUniqueChildNamed("NtryRef") { it.textContent }
- val numNtryDtls = mapEachChildNamed("NtryDtls") {
- Unit
- }.count()
+ val numInnerTxs = mapEachChildNamed("NtryDtls") {
+ mapEachChildNamed("TxDtls") { Unit }
+ }.flatten().count()
+
+ val numBatches = mapEachChildNamed("NtryDtls") {
+ mapEachChildNamed("Btch") { Unit }
+ }.flatten().count()
+
+ val isBatch = numBatches > 0 || numInnerTxs > 1
val currencyExchange = maybeUniqueChildNamed("AmtDtls") {
- val cxCntrVal = maybeUniqueChildNamed("CntrValAmt") {
extractMaybeCurrencyExchange() }
- val cxTx = maybeUniqueChildNamed("TxAmt") {
extractMaybeCurrencyExchange() }
+ val cxCntrVal = maybeUniqueChildNamed("CntrValAmt") {
extractMaybeCurrencyExchange() }
+ val cxTx = maybeUniqueChildNamed("TxAmt") {
extractMaybeCurrencyExchange() }
val cxInstr = maybeUniqueChildNamed("InstrAmt") {
extractMaybeCurrencyExchange() }
cxCntrVal ?: cxTx ?: cxInstr
}
@@ -716,6 +885,10 @@ private fun
XmlElementDestructor.extractInnerTransactions(): CamtReport {
maybeUniqueChildNamed("CntrValAmt") { extractCurrencyAmount() }
}
+ val instructedAmount = maybeUniqueChildNamed("AmtDtls") {
+ maybeUniqueChildNamed("InstdAmt") { extractCurrencyAmount() }
+ }
+
// For now, only support account servicer reference as id
CamtBankAccountEntry(
@@ -723,14 +896,15 @@ private fun
XmlElementDestructor.extractInnerTransactions(): CamtReport {
status = status,
currencyExchange = currencyExchange,
counterValueAmount = counterValueAmount,
+ instructedAmount = instructedAmount,
creditDebitIndicator = creditDebitIndicator,
bankTransactionCode = btc,
- details = if (numNtryDtls == 1) {
- extractSingleDetails(amount, creditDebitIndicator)
- } else {
+ details = if (isBatch) {
null
+ } else {
+ extractSingleDetails(amount, creditDebitIndicator)
},
- batches = if (numNtryDtls > 1) {
+ batches = if (isBatch) {
extractBatches(amount, creditDebitIndicator)
} else {
null
@@ -741,7 +915,19 @@ private fun
XmlElementDestructor.extractInnerTransactions(): CamtReport {
entryRef = entryRef
)
}
- return CamtReport(account, entries)
+ return CamtReport(
+ account = account,
+ entries = entries,
+ creationDateTime = maybeUniqueChildNamed("CreDtTm") { it.textContent },
+ balances = balances,
+ electronicSequenceNumber = maybeUniqueChildNamed("ElctrncSeqNb") {
it.textContent.toInt() },
+ legalSequenceNumber = maybeUniqueChildNamed("LglSeqNb") {
it.textContent.toInt() },
+ fromDate = maybeUniqueChildNamed("FrToDt") {
maybeUniqueChildNamed("FrDtTm") { it.textContent } },
+ toDate = maybeUniqueChildNamed("FrToDt") {
maybeUniqueChildNamed("ToDtTm") { it.textContent } },
+ id = requireUniqueChildNamed("Id") { it.textContent },
+ proprietaryReportingSource = maybeUniqueChildNamed("RptgSrc") {
maybeUniqueChildNamed("Prtry") { it.textContent } },
+ reportingSource = maybeUniqueChildNamed("RptgSrc") {
maybeUniqueChildNamed("Cd") { it.textContent } }
+ )
}
/**
@@ -769,22 +955,6 @@ fun parseCamtMessage(doc: Document): CamtParseResult {
}
}
- val balances = requireOnlyChild {
- mapEachChildNamed("Bal") {
- Balance(
- type = maybeUniqueChildNamed("Tp") {
maybeUniqueChildNamed("Cd") { it.textContent } },
- proprietaryType = maybeUniqueChildNamed("Tp") {
maybeUniqueChildNamed("Prtry") { it.textContent } },
- date = extractDateOrDateTime(),
- creditDebitIndicator =
requireUniqueChildNamed("CdtDbtInd") { it.textContent }.let {
- CreditDebitIndicator.valueOf(it)
- },
- subtype = maybeUniqueChildNamed("SubTp") {
maybeUniqueChildNamed("Cd") { it.textContent } },
- proprietarySubtype = maybeUniqueChildNamed("SubTp") {
maybeUniqueChildNamed("Prtry") { it.textContent } },
- amount = extractCurrencyAmount()
- )
- }
- }
-
val messageId = requireOnlyChild {
requireUniqueChildNamed("GrpHdr") {
requireUniqueChildNamed("MsgId") { it.textContent }
@@ -806,7 +976,6 @@ fun parseCamtMessage(doc: Document): CamtParseResult {
}
CamtParseResult(
reports = reports,
- balances = balances,
messageId = messageId,
messageType = messageType,
creationDateTime = creationDateTime
diff --git
a/nexus/src/test/resources/iso20022-samples/camt.053/de.camt.053.001.02.xml
b/nexus/src/test/resources/iso20022-samples/camt.053/de.camt.053.001.02.xml
index dc3511e..14a36eb 100644
--- a/nexus/src/test/resources/iso20022-samples/camt.053/de.camt.053.001.02.xml
+++ b/nexus/src/test/resources/iso20022-samples/camt.053/de.camt.053.001.02.xml
@@ -233,7 +233,7 @@
</Id>
</DbtrAcct>
<Cdtr>
- <Nm>Nonexistant Creditor</Nm>
+ <Nm>Nonexistent Creditor</Nm>
</Cdtr>
<CdtrAcct>
<Id>
@@ -330,7 +330,7 @@
</Chrgs>
<RltdPties>
<Dbtr>
- <Nm>Some US Bank</Nm>
+ <Nm>Mr USA</Nm>
<PstlAdr>
<Ctry>US</Ctry>
<AdrLine>42 Some Street</AdrLine>
@@ -359,6 +359,130 @@
</NtryDtls>
<AddtlNtryInf>AZV-UEBERWEISUNGSGUTSCHRIFT</AddtlNtryInf>
</Ntry>
+
+ <Ntry>
+ <Amt Ccy="EUR">48.42</Amt>
+ <CdtDbtInd>DBIT</CdtDbtInd>
+ <Sts>BOOK</Sts>
+ <BookgDt>
+ <Dt>2020-07-07</Dt>
+ </BookgDt>
+ <ValDt>
+ <Dt>2020-07-07</Dt>
+ </ValDt>
+ <AcctSvcrRef>acctsvcrref-005</AcctSvcrRef>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="CHF">46.3</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <NtryDtls>
+ <Btch>
+ <MsgId>UXC20070700006</MsgId>
+ <PmtInfId>UXC20070700006PI00001</PmtInfId>
+ <NbOfTxs>2</NbOfTxs>
+ <TtlAmt Ccy="EUR">46.3</TtlAmt>
+ <CdtDbtInd>DBIT</CdtDbtInd>
+ </Btch>
+ <TxDtls>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">23.1</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ </BkTxCd>
+ <RltdPties>
+ <Cdtr>
+ <Nm>Zahlungsempfaenger 23, ZA 5, DE</Nm>
+ <PstlAdr>
+ <Ctry>DE</Ctry>
+ <AdrLine>DE Adresszeile 1</AdrLine>
+ <AdrLine>DE Adresszeile 2</AdrLine>
+ </PstlAdr>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>DE32733516350012345678</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <CdtrAgt>
+ <FinInstnId>
+ <BIC>BYLADEM1ALR</BIC>
+ </FinInstnId>
+ </CdtrAgt>
+ </RltdAgts>
+ </TxDtls>
+ <TxDtls>
+ <Refs>
+ <MsgId>asdfasdf</MsgId>
+ <AcctSvcrRef>5j3k453k45</AcctSvcrRef>
+ <PmtInfId>6j564l56</PmtInfId>
+ <InstrId>6jl5lj65afasdf</InstrId>
+ <EndToEndId>jh45k34h5l</EndToEndId>
+ </Refs>
+ <AmtDtls>
+ <TxAmt>
+ <Amt Ccy="EUR">23.2</Amt>
+ </TxAmt>
+ </AmtDtls>
+ <BkTxCd>
+ <Domn>
+ <Cd>PMNT</Cd>
+ <Fmly>
+ <Cd>ICDT</Cd>
+ <SubFmlyCd>ESCT</SubFmlyCd>
+ </Fmly>
+ </Domn>
+ <Prtry>
+ <Cd>K25</Cd>
+ </Prtry>
+ </BkTxCd>
+ <RltdPties>
+ <Cdtr>
+ <Nm>Zahlungsempfaenger 23, ZA 5, AT</Nm>
+ <PstlAdr>
+ <Ctry>AT</Ctry>
+ <AdrLine>AT Adresszeile 1</AdrLine>
+ <AdrLine>AT Adresszeile 2</AdrLine>
+ </PstlAdr>
+ </Cdtr>
+ <CdtrAcct>
+ <Id>
+ <IBAN>AT071100000012345678</IBAN>
+ </Id>
+ </CdtrAcct>
+ </RltdPties>
+ <RltdAgts>
+ <CdtrAgt>
+ <FinInstnId>
+ <BIC>BKAUATWW</BIC>
+ </FinInstnId>
+ </CdtrAgt>
+ </RltdAgts>
+ </TxDtls>
+ </NtryDtls>
+ <AddtlNtryInf>Order</AddtlNtryInf>
+ </Ntry>
+
</Stmt>
</BkToCstmrStmt>
</Document>
diff --git a/util/src/main/kotlin/ISO20022.kt b/util/src/main/kotlin/ISO20022.kt
deleted file mode 100644
index 3f2cc8b..0000000
--- a/util/src/main/kotlin/ISO20022.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * This file is part of LibEuFin.
- * Copyright (C) 2020 Taler Systems S.A.
- *
- * LibEuFin is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3, or
- * (at your option) any later version.
- *
- * LibEuFin is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
- * Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with LibEuFin; see the file COPYING. If not, see
- * <http://www.gnu.org/licenses/>
- */
-
-package tech.libeufin.util
-
-import org.w3c.dom.Document
-
-/*
- * This file is part of LibEuFin.
- * Copyright (C) 2019 Stanisci and Dold.
-
- * LibEuFin is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3, or
- * (at your option) any later version.
-
- * LibEuFin is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
- * Public License for more details.
-
- * You should have received a copy of the GNU Affero General Public
- * License along with LibEuFin; see the file COPYING. If not, see
- * <http://www.gnu.org/licenses/>
- */
-
-data class CamtData(
- val bookingDate: Long,
- val subject: String,
- val txType: String, /* only "DBIT" / "CRDT" are admitted */
- val currency: String,
- val amount: String,
- val status: String, /* only "BOOK" is admitted */
- val counterpartIban: String,
- val counterpartBic: String,
- val counterpartName: String
-)
-
-fun parseCamt(camtDoc: Document): CamtData {
- val txType =
camtDoc.pickString("//*[local-name()='Ntry']//*[local-name()='CdtDbtInd']")
- val bd =
parseDashedDate(camtDoc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']"))
- return CamtData(
- txType = txType,
- bookingDate = bd.millis(),
- subject =
camtDoc.pickString("//*[local-name()='Ntry']//*[local-name()='Ustrd']"),
- currency =
camtDoc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy"),
- amount =
camtDoc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']"),
- status =
camtDoc.pickString("//*[local-name()='Ntry']//*[local-name()='Sts']"),
- counterpartBic =
camtDoc.pickString("//*[local-name()='RltdAgts']//*[local-name()='BIC']"),
- counterpartIban = camtDoc.pickString("//*[local-name()='${if (txType
== "DBIT") "CdtrAcct" else "DbtrAcct"}']//*[local-name()='IBAN']"),
- counterpartName =
camtDoc.pickString("//*[local-name()='RltdPties']//*[local-name()='${if (txType
== "DBIT") "Cdtr" else "Dbtr"}']//*[local-name()='Nm']")
- )
-}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.