libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 73b6118bfe2bbcbf6d543f79361f2220af1899e3
parent 22954ebdd9fa33daaff6ea4cf4aa1a8cd9d01077
Author: Florian Dold <florian.dold@gmail.com>
Date:   Sat, 13 Jun 2020 13:37:17 +0530

improve camt modeling/parsing

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt | 59++++++++++++++++++++++++++++++++++-------------------------
Mutil/src/main/kotlin/XmlCombinators.kt | 2+-
2 files changed, 35 insertions(+), 26 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt @@ -74,8 +74,23 @@ data class TransactionDetails( val unstructuredRemittanceInformation: String ) +abstract class AccountIdentification(type: String) : TypedEntity(type) + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class AccountIdentificationIban( + val iban: String +) : AccountIdentification("account-identification-iban") + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class AccountIdentificationGeneric( + val identification: String, + val issuer: String?, + val code: String?, + val proprietary: String? +) : AccountIdentification("account-identification-generic") + data class BankTransaction( - val accountIdentifier: String, + val account: AccountIdentification, /** * Identifier for the transaction that should be unique within an account. * Prefix by the identifier scheme name followed by a colon. @@ -84,7 +99,6 @@ data class BankTransaction( /** * Scheme used for the account identifier. */ - val accountIdentifierScheme: String, val currency: String, val amount: String, /** @@ -124,10 +138,6 @@ class Party( val name: String? ) : TypedEntity("party") -@JsonInclude(JsonInclude.Include.NON_NULL) -class Account( - val iban: String? -) : TypedEntity("party") abstract class DateOrDateTime(type: String) : TypedEntity(type) @@ -176,10 +186,10 @@ data class References( @JsonInclude(JsonInclude.Include.NON_NULL) data class RelatedParties( val debtor: Party?, - val debtorAccount: Account?, + val debtorAccount: AccountIdentification?, val debtorAgent: Agent?, val creditor: Party?, - val creditorAccount: Account?, + val creditorAccount: AccountIdentification?, val creditorAgent: Agent? ) @@ -210,14 +220,21 @@ private fun XmlElementDestructor.extractAgent(): Agent { ) } -private fun XmlElementDestructor.extractAccount(): Account { - return Account( - iban = requireUniqueChildNamed("Id") { - maybeUniqueChildNamed("IBAN") { - it.textContent +private fun XmlElementDestructor.extractAccount(): AccountIdentification { + return requireUniqueChildNamed("Id") { + requireOnlyChild { + when (it.localName) { + "IBAN" -> AccountIdentificationIban(it.textContent) + "Othr" -> AccountIdentificationGeneric( + identification = requireUniqueChildNamed("Id") { it.textContent }, + proprietary = maybeUniqueChildNamed("Prtry") { it.textContent }, + code = maybeUniqueChildNamed("Cd") { it.textContent }, + issuer = maybeUniqueChildNamed("Issr") { it.textContent } + ) + else -> throw Error("invalid account identification") } } - ) + } } private fun XmlElementDestructor.extractParty(): Party { @@ -293,14 +310,7 @@ private fun XmlElementDestructor.extractTransactionDetails(): List<TransactionDe } private fun XmlElementDestructor.extractInnerTransactions(): List<BankTransaction> { - val iban = requireUniqueChildNamed("Acct") { - requireUniqueChildNamed("Id") { - requireUniqueChildNamed("IBAN") { - it.textContent - } - } - } - + val account = requireUniqueChildNamed("Acct") { extractAccount() } return mapEachChildNamed("Ntry") { val amount = requireUniqueChildNamed("Amt") { it.textContent } val currency = requireUniqueChildNamed("Amt") { it.getAttribute("Ccy") } @@ -315,7 +325,7 @@ private fun XmlElementDestructor.extractInnerTransactions(): List<BankTransactio proprietary = maybeUniqueChildNamed("Prtry") { val cd = requireUniqueChildNamed("Cd") { it.textContent } val issr = requireUniqueChildNamed("Issr") { it.textContent } - "$issr:$cd" + "$issr/$cd" }, iso = maybeUniqueChildNamed("Domn") { val cd = requireUniqueChildNamed("Cd") { it.textContent } @@ -334,8 +344,7 @@ private fun XmlElementDestructor.extractInnerTransactions(): List<BankTransactio val txId = "AcctSvcrRef:" + (acctSvcrRef ?: throw Exception("currently, AcctSvcrRef is mandatory in LibEuFin")) val details = extractTransactionDetails() BankTransaction( - accountIdentifier = iban, - accountIdentifierScheme = "iban", + account = account, amount = amount, currency = currency, status = status, diff --git a/util/src/main/kotlin/XmlCombinators.kt b/util/src/main/kotlin/XmlCombinators.kt @@ -130,7 +130,7 @@ class XmlElementDestructor internal constructor(val d: Document, val e: Element) fun <T> requireUniqueChildNamed(s: String, f: XmlElementDestructor.(e: Element) -> T): T { val cl = e.getChildElements("*", s) if (cl.size != 1) { - throw DestructionError("expected exactly one unique $s child, got ${cl.size} instead") + throw DestructionError("expected exactly one unique $s child, got ${cl.size} instead at ${e}") } val el = cl[0] val destr = XmlElementDestructor(d, el)