libeufin

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

commit f1e70b7e610e5aa4f7069f384b43495034eb0eda
parent bed2845678995fe64ebd4ad98b2b65ab416eec83
Author: ms <ms@taler.net>
Date:   Mon, 30 Aug 2021 16:02:46 +0200

Actually creating c53 documents upon each tick.

Diffstat:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt | 11+++++++++++
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 37+++++++++++++++++++++++++++----------
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt | 10+++++++---
4 files changed, 106 insertions(+), 23 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt @@ -380,6 +380,17 @@ object BankAccountStatementsTable : IntIdTable() { val creationTime = long("creationTime") val xmlMessage = text("xmlMessage") val bankAccount = reference("bankAccount", BankAccountsTable) + val balancePrcd = text("balancePrcd") // normally, a BigDecimal +} + +class BankAccountStatementEntity(id: EntityID<Int>) : IntEntity(id) { + companion object : IntEntityClass<BankAccountStatementEntity>(BankAccountStatementsTable) + + var statementId by BankAccountStatementsTable.id + var creationTime by BankAccountStatementsTable.creationTime + var xmlMessage by BankAccountStatementsTable.xmlMessage + var bankAccount by BankAccountEntity referencedOn BankAccountStatementsTable.bankAccount + var balancePrcd by BankAccountStatementsTable.balancePrcd } object BankAccountReportsTable : IntIdTable() { diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt @@ -44,6 +44,7 @@ import tech.libeufin.util.ebics_hev.SystemReturnCodeType import tech.libeufin.util.ebics_s001.SignatureTypes import tech.libeufin.util.ebics_s001.UserSignatureData import java.math.BigDecimal +import java.math.BigInteger import java.security.interfaces.RSAPrivateCrtKey import java.security.interfaces.RSAPublicKey import java.time.Instant @@ -197,7 +198,13 @@ private fun getRelatedParty(branch: XmlElementBuilder, payment: RawPayment) { } } -fun buildCamtString(type: Int, subscriberIban: String, history: List<RawPayment>): String { +fun buildCamtString( + type: Int, + subscriberIban: String, + freshHistory: MutableList<RawPayment>, + balancePrcd: BigDecimal, // Balance up to freshHistory (excluded). + balanceClbd: BigDecimal +): String { /** * ID types required: * @@ -213,7 +220,7 @@ fun buildCamtString(type: Int, subscriberIban: String, history: List<RawPayment> val now = LocalDateTime.now() val dashedDate = now.toDashedDate() val zonedDateTime = now.toZonedString() - val balance = balanceForAccount(history) + return constructXml(indent = true) { root("Document") { attribute("xmlns", "urn:iso:std:iso:20022:tech:xsd:camt.0${type}.001.02") @@ -288,7 +295,11 @@ fun buildCamtString(type: Int, subscriberIban: String, history: List<RawPayment> } element("Amt") { attribute("Ccy", "EUR") - text("0") + if (balancePrcd < BigDecimal.ZERO) { + text(balancePrcd.abs().toPlainString()) + } else { + text(balancePrcd.toPlainString()) + } } element("CdtDbtInd") { text("CRDT") @@ -309,17 +320,16 @@ fun buildCamtString(type: Int, subscriberIban: String, history: List<RawPayment> attribute("Ccy", "EUR") // FIXME: the balance computation still not working properly //text(balanceForAccount(subscriberIban).toString()) - if (balance < BigDecimal.ZERO) { - text(balance.abs().toPlainString()) + if (balanceClbd < BigDecimal.ZERO) { + text(balanceClbd.abs().toPlainString()) } else { - text(balance.toPlainString()) + text(balanceClbd.toPlainString()) } - } element("CdtDbtInd") { // a temporary value to get the camt to validate. // Should be fixed along #6269 - if (balance < BigDecimal.ZERO) { + if (balanceClbd < BigDecimal.ZERO) { text("DBIT") } else { text("CRDT") @@ -329,7 +339,7 @@ fun buildCamtString(type: Int, subscriberIban: String, history: List<RawPayment> text(dashedDate) } } - history.forEach { + freshHistory.forEach { this.element("Ntry") { element("Amt") { attribute("Ccy", it.currency) @@ -456,7 +466,14 @@ private fun constructCamtResponse(type: Int, subscriber: EbicsSubscriberEntity): val bankAccount = getBankAccountFromSubscriber(subscriber) logger.info("getting history for account with iban ${bankAccount.iban}") val history = historyForAccount(bankAccount) - return buildCamtString(type, bankAccount.iban, history) + val baseBalance = BigDecimal.ZERO + return buildCamtString( + type, + bankAccount.iban, + history, + balancePrcd = baseBalance, + balanceClbd = balanceForAccount(history, baseBalance) + ) } /** diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt @@ -135,16 +135,56 @@ class Camt053Tick : CliktCommand( /** * TBD: here the statements for each account need to be generated. */ - val accountRet = mutableListOf<String>() - BankAccountFreshTransactionEntity.all().forEach { freshTx -> - accountRet.add( - "${freshTx.transactionRef.subject}: " + - "${freshTx.transactionRef.amount} ${freshTx.transactionRef.currency} " + - freshTx.transactionRef.direction + + /** + * Map of 'account name' -> fresh history + */ + val histories = mutableMapOf< + String, + MutableList<RawPayment>>() + BankAccountFreshTransactionEntity.all().forEach { + val bankAccountLabel = it.transactionRef.account.label + histories.putIfAbsent(bankAccountLabel, mutableListOf()) + val historyIter = histories[bankAccountLabel] + historyIter?.add( + RawPayment( + subject = it.transactionRef.subject, + creditorIban = it.transactionRef.creditorIban, + creditorBic = it.transactionRef.creditorBic, + creditorName = it.transactionRef.creditorName, + debtorIban = it.transactionRef.debtorIban, + debtorBic = it.transactionRef.debtorBic, + debtorName = it.transactionRef.debtorName, + date = importDateFromMillis(it.transactionRef.date).toDashedDate(), + amount = it.transactionRef.amount, + currency = it.transactionRef.currency, + // The line below produces a value too long (>35 chars), + // and it makes the document invalid! + // uid = "${it.pmtInfId}-${it.msgId}" + uid = it.transactionRef.accountServicerReference, + direction = it.transactionRef.direction, + pmtInfId = it.transactionRef.pmtInfId + ) ) } - println("Bank account ${accountIter.label} found fresh transactions:") - accountRet.forEach { println(it) } + // still need lastBalance + val lastStatement = BankAccountStatementEntity.find { + BankAccountStatementsTable.bankAccount eq accountIter.id + }.firstOrNull() + val lastBalance = if (lastStatement == null) { + BigDecimal.ZERO } else { BigDecimal(lastStatement.balancePrcd) } + val balancePrcd = balanceForAccount( + history = histories[accountIter.label] ?: mutableListOf(), + baseBalance = lastBalance + ) + val camt53 = buildCamtString( + 53, + accountIter.iban, + histories[accountIter.label] ?: mutableListOf(), + balanceClbd = lastBalance, + balancePrcd = balancePrcd + ) + println(camt53) } BankAccountFreshTransactionsTable.deleteAll() } @@ -542,8 +582,19 @@ fun serverMain(dbName: String, port: Int) { val body = call.receiveJson<CamtParams>() val bankaccount = getAccountFromLabel(body.bankaccount) val history = historyForAccount(bankaccount) - SandboxAssert(body.type == 53, "Only Camt.053 is implemented") - val camt53 = buildCamtString(body.type, bankaccount.iban, history) + SandboxAssert(body.type == 53, + "Only Camt.053 is implemented" + ) + val camt53 = buildCamtString( + body.type, + bankaccount.iban, + history, + balancePrcd = BigDecimal.ZERO, + balanceClbd = balanceForAccount( + history, + baseBalance = BigDecimal.ZERO + ) + ) call.respondText(camt53, ContentType.Text.Xml, HttpStatusCode.OK) return@post } diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt @@ -25,8 +25,11 @@ fun getAccountFromLabel(accountLabel: String): BankAccountEntity { } } // Mainly useful inside the CAMT generator. -fun balanceForAccount(history: List<RawPayment>): BigDecimal { - var ret = BigDecimal.ZERO +fun balanceForAccount( + history: MutableList<RawPayment>, + baseBalance: BigDecimal +): BigDecimal { + var ret = baseBalance history.forEach direction@ { if (it.direction == "CRDT") { val amount = parseDecimal(it.amount) @@ -73,7 +76,8 @@ fun balanceForAccount(bankAccount: BankAccountEntity): BigDecimal { return balance } -fun historyForAccount(bankAccount: BankAccountEntity): List<RawPayment> { +// For now, returns everything. +fun historyForAccount(bankAccount: BankAccountEntity): MutableList<RawPayment> { val history = mutableListOf<RawPayment>() transaction { /**