[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated (c8bcfc1 -> ae7897d)
From: |
gnunet |
Subject: |
[libeufin] branch master updated (c8bcfc1 -> ae7897d) |
Date: |
Fri, 04 Dec 2020 15:00:03 +0100 |
This is an automated email from the git hooks/post-receive script.
ms pushed a change to branch master
in repository libeufin.
from c8bcfc1 adapt unit test to new camt-parser policy
new ebaf98b evolving sandbox
new 5132e4e more abstraction at sandbox
new 338960b Refactoring Camt generation.
new 722e2cf sandbox payment API: ask the payment direction too
new d96b782 address validation issues
new 80415f7 Get unit tests from Sandbox to pass.
new d446014 prefer wrapping lists into JSON field
new ae7897d abstract over Camt type
The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
integration-tests/tests.py | 28 ++
.../src/main/kotlin/tech/libeufin/sandbox/DB.kt | 43 +-
.../tech/libeufin/sandbox/EbicsProtocolBackend.kt | 453 ++++++++++-----------
.../main/kotlin/tech/libeufin/sandbox/Helpers.kt | 4 +
.../src/main/kotlin/tech/libeufin/sandbox/JSON.kt | 5 +
.../src/main/kotlin/tech/libeufin/sandbox/Main.kt | 87 +---
.../kotlin/tech/libeufin/sandbox/bankAccount.kt | 52 +++
sandbox/src/test/kotlin/CamtTest.kt | 5 +-
sandbox/src/test/kotlin/DBTest.kt | 1 +
util/src/main/kotlin/JSON.kt | 10 +-
10 files changed, 328 insertions(+), 360 deletions(-)
create mode 100644 sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
diff --git a/integration-tests/tests.py b/integration-tests/tests.py
index 262c85f..27e2246 100755
--- a/integration-tests/tests.py
+++ b/integration-tests/tests.py
@@ -358,3 +358,31 @@ def test_ingestion_camt53():
with open("camt53-gls-style-0.json") as f:
expected_txs = f.read()
assert not dd(resp.json(), json.loads(expected_txs), ignore_order=True)
+
+def test_sandbox_camt():
+ payment_instruction = dict(
+ # NOTE: this format is very outdated in the docs repo.
+ creditorIban="GB33BUKB20201555555555",
+ creditorBic="BUKBGB22",
+ creditorName="Oliver Smith",
+ debitorIban="FR00000000000000000000",
+ debitorBic="BUKBGB22",
+ debitorName="Max Mustermann",
+ amount=5,
+ currency="EUR",
+ subject="Reimbursement",
+ direction="CRDT"
+ )
+ assertResponse(
+ post(
+ f"{S}/admin/payments/",
+ json=payment_instruction
+ )
+ )
+
+ assertResponse(
+ post(
+ f"{S}/admin/payments/camt",
+ json=dict(iban="GB33BUKB20201555555555", type=53)
+ )
+ )
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 2a34039..5d9a6ca 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -248,10 +248,10 @@ class EbicsUploadTransactionChunkEntity(id:
EntityID<String>) : Entity<String>(i
*/
object BankAccountTransactionsTable : Table() {
val creditorIban = text("creditorIban")
- val creditorBic = text("creditorBic").nullable()
+ val creditorBic = text("creditorBic")
val creditorName = text("creditorName")
val debitorIban = text("debitorIban")
- val debitorBic = text("debitorBic").nullable()
+ val debitorBic = text("debitorBic")
val debitorName = text("debitorName")
val subject = text("subject")
val amount = text("amount")
@@ -259,31 +259,12 @@ object BankAccountTransactionsTable : Table() {
val date = long("date")
val pmtInfId = text("pmtInfId")
val msgId = text("msgId")
+ val direction = text("direction")
val account = reference("account", BankAccountsTable)
override val primaryKey = PrimaryKey(pmtInfId, msgId)
}
-/*
-class BankAccountTransactionsEntity(id: EntityID<Int>) : IntEntity(id) {
- companion object :
IntEntityClass<BankAccountTransactionsEntity>(BankAccountTransactionsTable)
-
- var creditorIban by BankAccountTransactionsTable.creditorIban
- var creditorBic by BankAccountTransactionsTable.creditorBic
- var creditorName by BankAccountTransactionsTable.creditorName
- var debitorIban by BankAccountTransactionsTable.debitorIban
- var debitorBic by BankAccountTransactionsTable.debitorBic
- var debitorName by BankAccountTransactionsTable.debitorName
- var subject by BankAccountTransactionsTable.subject
- var amount by BankAccountTransactionsTable.amount
- var currency by BankAccountTransactionsTable.currency
- var date by BankAccountTransactionsTable.date
- var pmtInfId by BankAccountTransactionsTable.pmtInfId
- var msgId by BankAccountTransactionsTable.msgId
- var account by BankAccountEntity referencedOn
BankAccountTransactionsTable.account
-}
-*/
-
/**
* Table that keeps information about which bank accounts (iban+bic+name)
* are active in the system.
@@ -313,15 +294,6 @@ object BankAccountStatementsTable : IntIdTable() {
val bankAccount = reference("bankAccount", BankAccountsTable)
}
-class BankAccountStatementsEntity(id: EntityID<Int>) : IntEntity(id) {
- companion object :
IntEntityClass<BankAccountStatementsEntity>(BankAccountStatementsTable)
-
- var statementId by BankAccountStatementsTable.statementId
- var xmlMessage by BankAccountStatementsTable.xmlMessage
- var creationTime by BankAccountStatementsTable.creationTime
- var bankAccount by BankAccountEntity referencedOn
BankAccountStatementsTable.bankAccount
-}
-
object BankAccountReportsTable : IntIdTable() {
val reportId = text("reportId")
val creationTime = long("creationTime")
@@ -329,15 +301,6 @@ object BankAccountReportsTable : IntIdTable() {
val bankAccount = reference("bankAccount", BankAccountsTable)
}
-class BankAccountReportsTableEntity(id: EntityID<Int>) : IntEntity(id) {
- companion object :
IntEntityClass<BankAccountReportsTableEntity>(BankAccountReportsTable)
-
- var reportId by BankAccountReportsTable.reportId
- var xmlMessage by BankAccountReportsTable.xmlMessage
- var creationTime by BankAccountReportsTable.creationTime
- var bankAccount by BankAccountEntity referencedOn
BankAccountReportsTable.bankAccount
-}
-
fun dbCreateTables(dbName: String) {
Database.connect("jdbc:sqlite:${dbName}", "org.sqlite.JDBC")
TransactionManager.manager.defaultIsolationLevel =
Connection.TRANSACTION_SERIALIZABLE
diff --git
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index e93c79d..08c7242 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -42,7 +42,7 @@ import tech.libeufin.sandbox.BankAccountTransactionsTable.date
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorBic
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorIban
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorName
-import tech.libeufin.sandbox.BankAccountTransactionsTable.msgId
+import tech.libeufin.sandbox.BankAccountTransactionsTable.direction
import tech.libeufin.sandbox.BankAccountTransactionsTable.pmtInfId
import tech.libeufin.sandbox.BankAccountTransactionsTable.subject
import tech.libeufin.util.*
@@ -54,7 +54,6 @@ import tech.libeufin.util.ebics_s001.SignatureTypes
import tech.libeufin.util.ebics_s001.UserSignatureData
import java.security.interfaces.RSAPrivateCrtKey
import java.security.interfaces.RSAPublicKey
-import java.sql.SQLException
import java.time.Instant
import java.time.LocalDateTime
import java.util.*
@@ -167,13 +166,43 @@ fun <T> expectNonNull(x: T?): T {
return x;
}
-/**
- * Returns a list of camt strings. Note: each element in the
- * list accounts for only one payment in the history. In other
- * words, the camt constructor does create always only one "Ntry"
- * node.
- */
-fun buildCamtString(type: Int, subscriberIban: String, history:
MutableList<RawPayment>): MutableList<String> {
+private fun getRelatedParty(branch: XmlElementBuilder, payment: RawPayment) {
+ val otherParty = object {
+ var ibanPath = "CdtrAcct/Id/IBAN"
+ var namePath = "Cdtr/Nm"
+ var iban = payment.creditorIban
+ var name = payment.creditorName
+ var bicPath = "CdtrAgt/FinInstnId/BIC"
+ var bic = payment.creditorBic
+ }
+ if (payment.direction == "CRDT") {
+ otherParty.iban = payment.debitorIban
+ otherParty.ibanPath = "DbtrAcct/Id/IBAN"
+ otherParty.namePath = "Dbtr/Nm"
+ otherParty.name = payment.debitorName
+ otherParty.bic = payment.debitorBic
+ otherParty.bicPath = "DbtrAgt/FinInstnId/BIC"
+ }
+ branch.element("RltdPties") {
+ element(otherParty.namePath) {
+ text(otherParty.name)
+ }
+ element(otherParty.ibanPath) {
+ text(otherParty.iban)
+ }
+ }
+ branch.element("RltdAgts") {
+ element(otherParty.bicPath) {
+ text(otherParty.bic)
+ }
+ }
+}
+
+fun buildCamtString(
+ type: Int,
+ subscriberIban: String,
+ history: List<RawPayment>
+): String {
/**
* ID types required:
*
@@ -186,156 +215,190 @@ fun buildCamtString(type: Int, subscriberIban: String,
history: MutableList<RawP
* - Proprietary code of the bank transaction
* - Id of the servicer (Issuer and Code)
*/
- val ret = mutableListOf<String>()
- history.forEach {
- logger.debug(
- "Building CAMT over payment: ${it.debitorIban} =>
${it.creditorIban}, ${it.currency}:${it.amount}, ${it.subject}"
- )
- val dashedDate = expectNonNull(it.date)
- val now = LocalDateTime.now()
- val zonedDateTime = now.toZonedString()
- ret.add(
- constructXml(indent = true) {
- root("Document") {
- attribute("xmlns",
"urn:iso:std:iso:20022:tech:xsd:camt.053.001.02")
- attribute("xmlns:xsi",
"http://www.w3.org/2001/XMLSchema-instance";)
- attribute(
- "xsi:schemaLocation",
- "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02
camt.053.001.02.xsd"
- )
- element("BkToCstmrStmt") {
- element("GrpHdr") {
- element("MsgId") {
- text("sandbox-${now.millis()}")
- }
- element("CreDtTm") {
- text(zonedDateTime)
+ val now = LocalDateTime.now()
+ val dashedDate = now.toDashedDate()
+ val zonedDateTime = now.toZonedString()
+ return constructXml(indent = true) {
+ root("Document") {
+ attribute("xmlns",
"urn:iso:std:iso:20022:tech:xsd:camt.053.001.02")
+ attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance";)
+ attribute(
+ "xsi:schemaLocation",
+ "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02
camt.053.001.02.xsd"
+ )
+ element("BkToCstmrStmt") {
+ element("GrpHdr") {
+ element("MsgId") {
+ text("sandbox-${now.millis()}")
+ }
+ element("CreDtTm") {
+ text(zonedDateTime)
+ }
+ element("MsgPgntn") {
+ element("PgNb") {
+ text("001")
+ }
+ element("LastPgInd") {
+ text("true")
+ }
+ }
+ }
+ element(if (type == 52) "Rpt" else "Stmt") {
+ element("Id") {
+ text("0")
+ }
+ element("ElctrncSeqNb") {
+ text("0")
+ }
+ element("LglSeqNb") {
+ text("0")
+ }
+ element("CreDtTm") {
+ text(zonedDateTime)
+ }
+ element("Acct") {
+ // mandatory account identifier
+ element("Id/IBAN") {
+ text(subscriberIban)
+ }
+ element("Ccy") {
+ text("EUR")
+ }
+ element("Ownr/Nm") {
+ text("Debitor/Owner Name")
+ }
+ element("Svcr/FinInstnId") {
+ element("Nm") {
+ text("Libeufin Bank")
}
- element("MsgPgntn") {
- element("PgNb") {
- text("001")
+ element("Othr") {
+ element("Id") {
+ text("0")
}
- element("LastPgInd") {
- text("true")
+ element("Issr") {
+ text("XY")
}
}
}
- element(if (type == 52) "Rpt" else "Stmt") {
- element("Id") {
- text("0")
- }
- element("ElctrncSeqNb") {
- text("0")
+ }
+ element("Bal") {
+ element("Tp/CdOrPrtry/Cd") {
+ /* Balance type, in a coded format. PRCD stands
+ for "Previously closed booked" and shows the
+ balance at the time _before_ all the entries
+ reported in this document were posted to the
+ involved bank account. */
+ text("PRCD")
+ }
+ element("Amt") {
+ attribute("Ccy", "EUR")
+ text(Amount(0).toPlainString())
+ }
+ element("CdtDbtInd") {
+ // a temporary value to get the camt to validate.
+ // Should be fixed along #6269
+ text("CRDT")
+ }
+ element("Dt/Dt") {
+ // date of this balance
+ text(dashedDate)
+ }
+ }
+ element("Bal") {
+ element("Tp/CdOrPrtry/Cd") {
+ /* CLBD stands for "Closing booked balance", and it
+ is calculated by summing the PRCD with all the
+ entries reported in this document */
+ text("CLBD")
+ }
+ element("Amt") {
+ attribute("Ccy", "EUR")
+ text(Amount(0).toPlainString())
+ }
+ element("CdtDbtInd") {
+ // a temporary value to get the camt to validate.
+ // Should be fixed along #6269
+ text("DBIT")
+ }
+ element("Dt/Dt") {
+ text(dashedDate)
+ }
+ }
+ history.forEach {
+ this.element("Ntry") {
+ element("Amt") {
+ attribute("Ccy", it.currency)
+ text(it.amount)
}
- element("LglSeqNb") {
- text("0")
+ element("CdtDbtInd") {
+ text(
+ if (subscriberIban.equals(it.creditorIban))
+ "CRDT" else "DBIT"
+ )
}
- element("CreDtTm") {
- text(zonedDateTime)
+ element("Sts") {
+ /* Status of the entry (see 2.4.2.15.5 from
the ISO20022 reference document.)
+ * From the original text:
+ * "Status of an entry on the books of the
account servicer" */
+ text("BOOK")
}
- element("Acct") {
- // mandatory account identifier
- element("Id/IBAN") {
- text(subscriberIban)
- }
- element("Ccy") {
- text("EUR")
- }
- element("Ownr/Nm") {
- text("Debitor/Owner Name")
+ element("BookgDt/Dt") {
+ text(dashedDate)
+ } // date of the booking
+ element("ValDt/Dt") {
+ text(dashedDate)
+ } // date of assets' actual (un)availability
+ element("AcctSvcrRef") {
+ val uid = if (it.uid != null)
it.uid.toString() else {
+ throw EbicsRequestError(
+ errorCode = "091116",
+ errorText = "EBICS_PROCESSING_ERROR"
+ )
}
- element("Svcr/FinInstnId") {
- element("Nm") {
- text("Libeufin Bank")
+ text(uid)
+ }
+ element("BkTxCd") {
+ /* "Set of elements used to fully identify
the type of underlying
+ * transaction resulting in an entry". */
+ element("Domn") {
+ element("Cd") {
+ text("PMNT")
}
- element("Othr") {
- element("Id") {
- text("0")
+ element("Fmly") {
+ element("Cd") {
+ text("ICDT")
}
- element("Issr") {
- text("XY")
+ element("SubFmlyCd") {
+ text("ESCT")
}
}
}
- }
- element("Bal") {
- element("Tp/CdOrPrtry/Cd") {
- /* Balance type, in a coded format. PRCD
stands
- for "Previously closed booked" and
shows the
- balance at the time _before_ all the
entries
- reported in this document were posted
to the
- involved bank account. */
- text("PRCD")
- }
- element("Amt") {
- attribute("Ccy", "EUR")
- text(Amount(0).toPlainString())
- }
- element("CdtDbtInd") {
- // a temporary value to get the camt to
validate.
- // Should be fixed along #6269
- text("CRDT")
- }
- element("Dt/Dt") {
- // date of this balance
- text(dashedDate)
+ element("Prtry") {
+ element("Cd") {
+ text("0")
+ }
+ element("Issr") {
+ text("XY")
+ }
}
}
- element("Bal") {
- element("Tp/CdOrPrtry/Cd") {
- /* CLBD stands for "Closing booked
balance", and it
- is calculated by summing the PRCD with
all the
- entries reported in this document */
- text("CLBD")
+ element("NtryDtls/TxDtls") {
+ element("Refs") {
+ element("MsgId") {
+ text("0")
+ }
+ element("PmtInfId") {
+ text("0")
+ }
+ element("EndToEndId") {
+ text("NOTPROVIDED")
+ }
}
- element("Amt") {
+ element("AmtDtls/TxAmt/Amt") {
attribute("Ccy", "EUR")
- text(Amount(0).toPlainString())
- }
- element("CdtDbtInd") {
- // a temporary value to get the camt to
validate.
- // Should be fixed along #6269
- text("DBIT")
- }
- element("Dt/Dt") {
- text(dashedDate)
- }
- }
- element("Ntry") {
- element("Amt") {
- attribute("Ccy", it.currency)
text(it.amount)
}
- element("CdtDbtInd") {
- text(
- if
(subscriberIban.equals(it.creditorIban))
- "CRDT" else "DBIT"
- )
- }
- element("Sts") {
- /* Status of the entry (see 2.4.2.15.5
from the ISO20022 reference document.)
- * From the original text:
- * "Status of an entry on the books of
the account servicer" */
- text("BOOK")
- }
- element("BookgDt/Dt") {
- text(dashedDate)
- } // date of the booking
- element("ValDt/Dt") {
- text(dashedDate)
- } // date of assets' actual (un)availability
- element("AcctSvcrRef") {
- val uid = if (it.uid != null)
it.uid.toString() else {
- throw EbicsRequestError(
- errorCode = "091116",
- errorText =
"EBICS_PROCESSING_ERROR"
- )
- }
- text(uid)
- }
element("BkTxCd") {
- /* "Set of elements used to fully
identify the type of underlying
- * transaction resulting in an entry".
*/
element("Domn") {
element("Cd") {
text("PMNT")
@@ -358,88 +421,17 @@ fun buildCamtString(type: Int, subscriberIban: String,
history: MutableList<RawP
}
}
}
- element("NtryDtls/TxDtls") {
- element("Refs") {
- element("MsgId") {
- text("0")
- }
- element("PmtInfId") {
- text("0")
- }
- element("EndToEndId") {
- text("NOTPROVIDED")
- }
- }
- element("AmtDtls/TxAmt/Amt") {
- attribute("Ccy", "EUR")
- text(it.amount)
- }
- element("BkTxCd") {
- element("Domn") {
- element("Cd") {
- text("PMNT")
- }
- element("Fmly") {
- element("Cd") {
- text("ICDT")
- }
- element("SubFmlyCd") {
- text("ESCT")
- }
- }
- }
- element("Prtry") {
- element("Cd") {
- text("0")
- }
- element("Issr") {
- text("XY")
- }
- }
- }
- element("RltdPties") {
- element("Dbtr/Nm") {
- text(it.debitorName)
- }
- element("DbtrAcct/Id/IBAN") {
- text(it.debitorIban)
- }
- element("Cdtr/Nm") {
- text(it.creditorName)
- }
- element("CdtrAcct/Id/IBAN") {
- text(it.creditorIban)
- }
- }
-// element("RltdAgts") {
-// element("CdtrAgt/FinInstnId/BIC") {
-// // FIXME: explain this!
-// text(
-// if
(subscriberIban.equals(it.creditorIban))
-// it.debitorBic else
it.creditorBic
-// )
-// }
-// element("DbtrAgt/FinInstnId/BIC") {
-// // FIXME: explain this!
-// text(
-// if
(subscriberIban.equals(it.creditorIban))
-// it.creditorBic else
it.debitorBic
-// )
-// }
-//
-// }
- element("RmtInf/Ustrd") {
- text(it.subject)
- }
+ getRelatedParty(this, it)
+ element("RmtInf/Ustrd") {
+ text(it.subject)
}
}
}
}
}
}
- )
+ }
}
- return ret
}
/**
@@ -460,40 +452,8 @@ private fun constructCamtResponse(
importDateFromMillis(dateRange.end.toGregorianCalendar().timeInMillis)
)
} else Pair(parseDashedDate("1970-01-01"), LocalDateTime.now())
- val history = mutableListOf<RawPayment>()
val bankAccount = getBankAccountFromSubscriber(subscriber)
- transaction {
- logger.debug("Querying transactions involving: ${bankAccount.iban}")
- BankAccountTransactionsTable.select {
- BankAccountTransactionsTable.creditorIban eq bankAccount.iban or
- (BankAccountTransactionsTable.debitorIban eq
bankAccount.iban)
- /**
- FIXME: add the following condition too:
- and (BankAccountTransactionsTable.date.between(start.millis,
end.millis))
- */
- }.forEach {
- history.add(
- RawPayment(
- subject = it[subject],
- creditorIban = it[creditorIban],
- creditorBic = it[creditorBic],
- creditorName = it[creditorName],
- debitorIban = it[debitorIban],
- debitorBic = it[debitorBic],
- debitorName = it[debitorName],
- date = importDateFromMillis(it[date]).toDashedDate(),
- amount = it[amount],
- currency = it[currency],
- // The line below produces a value too long (>35 chars),
- // and it makes the document invalid!
- // uid = "${it[pmtInfId]}-${it[msgId]}"
- uid = "${it[pmtInfId]}"
- )
- )
- }
- history
- }
- return buildCamtString(type, bankAccount.iban, history)
+ return mutableListOf(buildCamtString(type, bankAccount.iban,
historyForAccount(bankAccount.iban)))
}
/**
@@ -585,6 +545,7 @@ private fun handleCct(paymentRequest: String,
initiatorName: String, ctx: Reques
it[date] = Instant.now().toEpochMilli()
it[pmtInfId] = parseResult.pmtInfId
it[msgId] = parseResult.msgId
+ it[direction] = "DBIT"
}
} catch (e: ExposedSQLException) {
logger.warn("Could not insert new payment into the database: ${e}")
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 67d17a9..0ea1927 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -24,6 +24,10 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
+fun SandboxAssert(condition: Boolean, reason: String) {
+ if (!condition) throw SandboxError(HttpStatusCode.InternalServerError,
reason)
+}
+
fun getOrderTypeFromTransactionId(transactionID: String): String {
val uploadTransaction = transaction {
EbicsUploadTransactionEntity.findById(transactionID)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 9716bc2..ad46123 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -77,6 +77,11 @@ data class DateRange(
val endDate: Long
)
+data class CamtParams(
+ val iban: String,
+ val type: Int
+)
+
data class BankAccountStatements(
var bankAccountStatements: MutableList<BankAccountStatement> =
mutableListOf()
)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 38469a9..6ea2a02 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -28,8 +28,6 @@ import io.ktor.features.ContentNegotiation
import io.ktor.features.StatusPages
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
-import io.ktor.request.receive
-import io.ktor.request.uri
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.get
@@ -61,6 +59,7 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
+import io.ktor.request.*
import io.ktor.util.AttributeKey
import tech.libeufin.sandbox.BankAccountTransactionsTable
import tech.libeufin.sandbox.BankAccountTransactionsTable.amount
@@ -72,6 +71,7 @@ import tech.libeufin.sandbox.BankAccountTransactionsTable.date
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorBic
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorIban
import tech.libeufin.sandbox.BankAccountTransactionsTable.debitorName
+import tech.libeufin.sandbox.BankAccountTransactionsTable.direction
import tech.libeufin.util.*
import tech.libeufin.util.ebics_h004.EbicsResponse
import tech.libeufin.util.ebics_h004.EbicsTypes
@@ -234,6 +234,15 @@ fun serverMain(dbName: String) {
get("/") {
call.respondText("Hello, this is Sandbox\n",
ContentType.Text.Plain)
}
+ // only reason for a post is to hide the iban (to some degree.)
+ post("/admin/payments/camt") {
+ val body = call.receive<CamtParams>()
+ val history = historyForAccount(body.iban)
+ SandboxAssert(body.type == 53, "Only Camt.053 is implemented")
+ val camt53 = buildCamtString(body.type, body.iban, history)
+ call.respondText(camt53, ContentType.Text.Xml,
HttpStatusCode.OK)
+ return@post
+ }
get("/admin/payments") {
val ret = PaymentsResponse()
transaction {
@@ -249,12 +258,17 @@ fun serverMain(dbName: String) {
creditorName = it[creditorName],
debitorBic = it[debitorBic],
debitorName = it[debitorName],
- currency = it[currency]
+ currency = it[currency],
+ direction = it[direction]
)
)
}
}
- call.respond(ret)
+ call.respond(
+ object {
+ val payments = ret
+ }
+ )
return@get
}
@@ -265,7 +279,7 @@ fun serverMain(dbName: String) {
val body = call.receive<RawPayment>()
val random = Random.nextLong()
transaction {
- val debitorBankAccount =
getBankAccountFromIban(body.debitorIban).id
+ val localIban = if (body.direction == "DBIT")
body.debitorIban else body.creditorIban
BankAccountTransactionsTable.insert {
it[creditorIban] = body.creditorIban
it[creditorBic] = body.creditorBic
@@ -279,7 +293,8 @@ fun serverMain(dbName: String) {
it[date] = Instant.now().toEpochMilli()
it[pmtInfId] = random.toString()
it[msgId] = random.toString()
- it[account] = debitorBankAccount
+ it[account] = getBankAccountFromIban(localIban).id
+ it[direction] = body.direction
}
}
call.respondText("Payment created")
@@ -388,66 +403,6 @@ fun serverMain(dbName: String) {
post("/ebicsweb") {
call.ebicsweb()
}
- /**
- * Shows all bank account statements.
- */
- /*
- FIXME: Heng Yeow.
-
- get("/admin/statements") {
- var ret = BankAccountStatement()
- ret.creationTime = Instant.now().toEpochMilli()
- ret.statementId = "C52-" +
Instant.now().toEpochMilli().toHttpDateString() + "-" +
UUID.randomUUID().toString()
- ret.message = mutableListOf<String>()
- transaction {
- BankAccountTransactionsTable.selectAll().forEach {
- ret.message.add(
- constructXml(indent = true) {
- root("Document") {
- attribute("xmlns",
"urn:iso:std:iso:20022:tech:xsd:camt.053.001.02")
- attribute("xmlns:xsi",
"http://www.w3.org/2001/XMLSchema-instance";)
- attribute(
- "xsi:schemaLocation",
-
"urn:iso:std:iso:20022:tech:xsd:camt.053.001.02 camt.053.001.02.xsd"
- )
- element("Ntry") {
- element("Amt") {
- attribute("Ccy", it.currency)
- text(it.amount)
- }
- }
- }
- }
- )
- }
- }
- transaction {
- BankAccountStatementsTable.insert {
- it[statementId] = ret.statementId
- it[creationTime] = ret.creationTime
- it[xmlMessage] = ret.message.toString()
- }
- }
- call.respond(ret)
- return@get
- }
-
- */
- /**
- * Shows all bank account reports.
- */
- /*
- FIXME: Heng Yeow.
-
- get("/admin/reports") {
- val body = call.receive<DateRange>()
- var ret = BankAccountReport()
-
- call.respond(ret)
- return@get
- }
-
- */
}
}
LOGGER.info("Up and running")
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
new file mode 100644
index 0000000..aff6fa5
--- /dev/null
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
@@ -0,0 +1,52 @@
+package tech.libeufin.sandbox
+
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
+import org.jetbrains.exposed.sql.or
+import org.jetbrains.exposed.sql.select
+import org.jetbrains.exposed.sql.transactions.transaction
+import tech.libeufin.util.RawPayment
+import tech.libeufin.util.importDateFromMillis
+import tech.libeufin.util.logger
+import tech.libeufin.util.toDashedDate
+
+fun historyForAccount(iban: String): List<RawPayment> {
+ val history = mutableListOf<RawPayment>()
+ transaction {
+ logger.debug("Querying transactions involving: ${iban}")
+ BankAccountTransactionsTable.select {
+ BankAccountTransactionsTable.creditorIban eq iban or
+ (BankAccountTransactionsTable.debitorIban eq iban)
+ /**
+ FIXME: add the following condition too:
+ and (BankAccountTransactionsTable.date.between(start.millis,
end.millis))
+ */
+ /**
+ FIXME: add the following condition too:
+ and (BankAccountTransactionsTable.date.between(start.millis,
end.millis))
+ */
+
+ }.forEach {
+ history.add(
+ RawPayment(
+ subject = it[BankAccountTransactionsTable.subject],
+ creditorIban =
it[BankAccountTransactionsTable.creditorIban],
+ creditorBic = it[BankAccountTransactionsTable.creditorBic],
+ creditorName =
it[BankAccountTransactionsTable.creditorName],
+ debitorIban = it[BankAccountTransactionsTable.debitorIban],
+ debitorBic = it[BankAccountTransactionsTable.debitorBic],
+ debitorName = it[BankAccountTransactionsTable.debitorName],
+ date =
importDateFromMillis(it[BankAccountTransactionsTable.date]).toDashedDate(),
+ amount = it[BankAccountTransactionsTable.amount],
+ currency = it[BankAccountTransactionsTable.currency],
+ // The line below produces a value too long (>35 chars),
+ // and it makes the document invalid!
+ // uid = "${it[pmtInfId]}-${it[msgId]}"
+ uid = "${it[BankAccountTransactionsTable.pmtInfId]}",
+ direction = it[BankAccountTransactionsTable.direction]
+ )
+ )
+ }
+
+ }
+ return history
+}
diff --git a/sandbox/src/test/kotlin/CamtTest.kt
b/sandbox/src/test/kotlin/CamtTest.kt
index 8c06558..4556868 100644
--- a/sandbox/src/test/kotlin/CamtTest.kt
+++ b/sandbox/src/test/kotlin/CamtTest.kt
@@ -19,7 +19,8 @@ class CamtTest {
currency = "EUR",
subject = "reimbursement",
date = "1000-02-02",
- uid = "0"
+ uid = "0",
+ direction = "DBIT"
)
val xml = buildCamtString(
53,
@@ -27,7 +28,7 @@ class CamtTest {
mutableListOf(payment)
)
assertTrue {
- XMLUtil.validateFromString(xml.get(0))
+ XMLUtil.validateFromString(xml)
}
}
}
\ No newline at end of file
diff --git a/sandbox/src/test/kotlin/DBTest.kt
b/sandbox/src/test/kotlin/DBTest.kt
index 23aea8b..b529936 100644
--- a/sandbox/src/test/kotlin/DBTest.kt
+++ b/sandbox/src/test/kotlin/DBTest.kt
@@ -82,6 +82,7 @@ class DBTest {
it[currency] = "EUR"
it[pmtInfId] = "0"
it[msgId] = "0"
+ it[direction] = "DBIT"
}
}
val result = transaction {
diff --git a/util/src/main/kotlin/JSON.kt b/util/src/main/kotlin/JSON.kt
index d7ddd89..834f4b0 100644
--- a/util/src/main/kotlin/JSON.kt
+++ b/util/src/main/kotlin/JSON.kt
@@ -26,17 +26,15 @@ package tech.libeufin.util
*/
data class RawPayment(
val creditorIban: String,
- val creditorBic: String? = null,
+ val creditorBic: String,
val creditorName: String,
val debitorIban: String,
- val debitorBic: String? = null,
+ val debitorBic: String,
val debitorName: String,
val amount: String,
val currency: String,
val subject: String,
val date: String? = null,
- // this (uid) field is null when RawPayment is a _requested_ payment
- // over the admin API, and it's not null when RawPayment represent
- // a database row of a settled payment.
- val uid: String? = null
+ val uid: String? = null,
+ val direction: String
)
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libeufin] branch master updated (c8bcfc1 -> ae7897d),
gnunet <=
- [libeufin] 02/08: more abstraction at sandbox, gnunet, 2020/12/04
- [libeufin] 01/08: evolving sandbox, gnunet, 2020/12/04
- [libeufin] 03/08: Refactoring Camt generation., gnunet, 2020/12/04
- [libeufin] 07/08: prefer wrapping lists into JSON field, gnunet, 2020/12/04
- [libeufin] 06/08: Get unit tests from Sandbox to pass., gnunet, 2020/12/04
- [libeufin] 08/08: abstract over Camt type, gnunet, 2020/12/04
- [libeufin] 04/08: sandbox payment API: ask the payment direction too, gnunet, 2020/12/04
- [libeufin] 05/08: address validation issues, gnunet, 2020/12/04