commit 96950a3989fa9013b91d773e1cdae3f071a76959
parent 01696776ed983a8985928640e59b0ab960b1ea84
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date: Thu, 9 Apr 2020 23:31:37 +0200
Store bank-provided IDs, and transaction statuses.
Diffstat:
5 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/cli/python/libeufin-cli b/cli/python/libeufin-cli
@@ -465,8 +465,8 @@ def refund(ctx, account_id, bank_account_id, nexus_base_url):
@click.argument(
"nexus-base-url"
)
-def digest_transactions(obj, account_id, nexus_base_url):
- url = urljoin(nexus_base_url, "/ebics/subscribers/{}/digest-incoming-transactions".format(account_id))
+def crunch_transactions(obj, account_id, nexus_base_url):
+ url = urljoin(nexus_base_url, "/ebics/subscribers/{}/crunch-incoming-transactions".format(account_id))
resp = post(url, json=dict())
print(resp.content.decode("utf-8"))
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -76,14 +76,9 @@ class TalerIncomingPaymentEntity(id: EntityID<Long>) : LongEntity(id) {
*/
object EbicsRawBankTransactionsTable : LongIdTable() {
val nexusSubscriber = reference("subscriber", EbicsSubscribersTable)
- // How did we learn about this transaction? C52 / C53 / C54
- val sourceType = text("sourceType")
- // Name of the ZIP entry
- val sourceFileName = text("sourceFileName")
- // "Subject" of the SEPA transaction
+ val sourceFileName = text("sourceFileName") /* ZIP entry's name */
val unstructuredRemittanceInformation = text("unstructuredRemittanceInformation")
- // Debit or credit
- val transactionType = text("transactionType")
+ val transactionType = text("transactionType") /* DBIT or CRDT */
val currency = text("currency")
val amount = text("amount")
val creditorIban = text("creditorIban")
@@ -92,11 +87,13 @@ object EbicsRawBankTransactionsTable : LongIdTable() {
val debitorName = text("debitorName")
val counterpartBic = text("counterpartBic")
val bookingDate = text("bookingDate")
+ val status = text("status") // BOOK, ..
+ val servicerCode = text("servicerCode").nullable() /* "internal" code given by the bank */
+ val proprietaryCode = text("proprietaryCode") /* code given by the DK */
}
class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<EbicsRawBankTransactionEntity>(EbicsRawBankTransactionsTable)
- var sourceType by EbicsRawBankTransactionsTable.sourceType // C52 or C53 or C54?
var sourceFileName by EbicsRawBankTransactionsTable.sourceFileName
var unstructuredRemittanceInformation by EbicsRawBankTransactionsTable.unstructuredRemittanceInformation
var transactionType by EbicsRawBankTransactionsTable.transactionType
@@ -109,6 +106,9 @@ class EbicsRawBankTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
var counterpartBic by EbicsRawBankTransactionsTable.counterpartBic
var bookingDate by EbicsRawBankTransactionsTable.bookingDate
var nexusSubscriber by EbicsSubscriberEntity referencedOn EbicsRawBankTransactionsTable.nexusSubscriber
+ var status by EbicsRawBankTransactionsTable.status
+ var servicerCode by EbicsRawBankTransactionsTable.servicerCode
+ var proprietaryCode by EbicsRawBankTransactionsTable.proprietaryCode
}
/**
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -658,8 +658,7 @@ fun main() {
val paramsJson = call.receive<EbicsStandardOrderParamsJson>()
val orderParams = paramsJson.toOrderParams()
val subscriberData = getSubscriberDetailsFromId(id)
- val response = doEbicsDownloadTransaction(client, subscriberData, "C53", orderParams)
- when (response) {
+ when (val response = doEbicsDownloadTransaction(client, subscriberData, "C53", orderParams)) {
is EbicsDownloadSuccessResult -> {
/**
* The current code is _heavily_ dependent on the way GLS returns
@@ -673,12 +672,14 @@ fun main() {
val camt53doc = XMLUtil.parseStringIntoDom(it.second)
transaction {
EbicsRawBankTransactionEntity.new {
- sourceType = "C53"
sourceFileName = fileName
unstructuredRemittanceInformation = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
transactionType = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='CdtDbtInd']")
currency = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']/@Ccy")
amount = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Amt']")
+ status = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='Sts']")
+ servicerCode = camt53doc.pickStringNullable("//*[local-name()='Ntry']//*[local-name()='AcctSvcrRef']")
+ proprietaryCode = camt53doc.pickString("//*[local-name()='Ntry']//*[local-name()='BkTxCd']/*[local-name()='Prtry']/*[local-name()='Cd']")
bookingDate = camt53doc.pickString("//*[local-name()='BookgDt']//*[local-name()='Dt']")
nexusSubscriber = getSubscriberEntityFromId(id)
creditorName =
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -287,7 +287,7 @@ class Taler(app: Route) {
* at separating the good payments (those that will lead to a new reserve
* being created), from the invalid payments (those with a invalid subject
* that will soon be refunded.) */
- app.post("/ebics/taler/{id}/digest-incoming-transactions") {
+ app.post("/ebics/taler/{id}/crunch-incoming-transactions") {
val id = expectId(call.parameters["id"])
// first find highest ID value of already processed rows.
transaction {
@@ -297,8 +297,8 @@ class Taler(app: Route) {
* information.
*
* This latestId value points at the latest id in the _raw transactions table_
- * that was last processed. On the other hand, the "row_id" value that the exchange
- * will get along each history element will be the id in the _digested entries table_.
+ * that was last processed here. Note, the "row_id" value that the exchange
+ * will get along each history element will be the id in the _crunched entries table_.
*/
val latestId: Long = TalerIncomingPaymentEntity.all().sortedByDescending {
it.payment.id
diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt
@@ -427,3 +427,12 @@ class XMLUtil private constructor() {
fun Document.pickString(xpath: String): String {
return XMLUtil.getStringFromXpath(this, xpath)
}
+
+fun Document.pickStringNullable(xpath: String): String? {
+ return try {
+ XMLUtil.getStringFromXpath(this, xpath)
+ } catch (e: UtilError) {
+ logger.info("Tolerating missing value: $xpath")
+ null
+ }
+}
+\ No newline at end of file