gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: Implement balance accounting at Nexus.


From: gnunet
Subject: [libeufin] branch master updated: Implement balance accounting at Nexus.
Date: Thu, 02 Sep 2021 23:55:40 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new d61fa04  Implement balance accounting at Nexus.
d61fa04 is described below

commit d61fa04108b0a278b42877b6d9cf70f3d70f8cf3
Author: MS <ms@taler.net>
AuthorDate: Thu Sep 2 21:53:46 2021 +0000

    Implement balance accounting at Nexus.
    
    Nexus never calculates balances, but stores the amount as
    returned by the bank each time it downloads a "history" message.
    
    The API /bank-accounts/{my-acct} got extended to include the balance.
---
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    | 14 ++++----
 .../tech/libeufin/nexus/bankaccount/BankAccount.kt | 39 ++++++++++++++++++----
 .../kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt |  1 -
 .../tech/libeufin/nexus/iso20022/Iso20022.kt       |  4 +++
 .../main/kotlin/tech/libeufin/nexus/server/JSON.kt |  3 ++
 .../tech/libeufin/nexus/server/NexusServer.kt      | 12 +++++++
 6 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index c42edc9..3469cc9 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -113,7 +113,7 @@ class TalerIncomingPaymentEntity(id: EntityID<Long>) : 
LongEntity(id) {
 }
 
 /**
- * This table logs all the balances as returned by the bank for one particular 
bank account.
+ * This table logs all the balances as returned by the bank for all the bank 
accounts.
  */
 object NexusBankBalancesTable : LongIdTable() {
     /**
@@ -123,19 +123,16 @@ object NexusBankBalancesTable : LongIdTable() {
      */
     val balance = text("balance") // $currency:x.y
     val creditDebitIndicator = text("creditDebitIndicator") // CRDT or DBIT.
-    /**
-     * Message downloaded from the bank.  Must be of "history" type.
-     */
-    val bankMessage = reference("bankMessage", NexusBankMessagesTable)
     val bankAccount = reference("bankAccount", NexusBankAccountsTable)
+    val date = text("date") // in the YYYY-MM-DD format
 }
 
 class NexusBankBalanceEntity(id: EntityID<Long>) : LongEntity(id) {
     companion object : 
LongEntityClass<NexusBankBalanceEntity>(NexusBankBalancesTable)
     var balance by NexusBankBalancesTable.balance
     var creditDebitIndicator by NexusBankBalancesTable.creditDebitIndicator
-    var bankMessage by NexusBankMessageEntity referencedOn 
NexusBankBalancesTable.bankMessage
     var bankAccount by NexusBankAccountEntity referencedOn 
NexusBankBalancesTable.bankAccount
+    var date by NexusBankBalancesTable.date
 }
 
 /**
@@ -497,11 +494,13 @@ fun dbDropTables(dbConnectionString: String) {
             TalerInvalidIncomingPaymentsTable,
             NexusBankConnectionsTable,
             NexusBankMessagesTable,
+            NexusBankBalancesTable,
             FacadesTable,
             FacadeStateTable,
             NexusScheduledTasksTable,
             OfferedBankAccountsTable,
             NexusPermissionsTable,
+            AnastasisIncomingPaymentsTable
         )
     }
 }
@@ -516,6 +515,7 @@ fun dbCreateTables(dbConnectionString: String) {
             PaymentInitiationsTable,
             NexusEbicsSubscribersTable,
             NexusBankAccountsTable,
+            NexusBankBalancesTable,
             NexusBankTransactionsTable,
             AnastasisIncomingPaymentsTable,
             TalerIncomingPaymentsTable,
@@ -526,7 +526,7 @@ fun dbCreateTables(dbConnectionString: String) {
             NexusBankMessagesTable,
             FacadesTable,
             OfferedBankAccountsTable,
-            NexusPermissionsTable,
+            NexusPermissionsTable
         )
     }
 }
diff --git 
a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
index 2b7c3bd..8528161 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
@@ -33,6 +33,7 @@ import tech.libeufin.nexus.iso20022.parseCamtMessage
 import tech.libeufin.nexus.server.FetchSpecJson
 import tech.libeufin.nexus.server.Pain001Data
 import tech.libeufin.nexus.server.requireBankConnection
+import tech.libeufin.nexus.server.toPlainString
 import tech.libeufin.util.XMLUtil
 import java.time.Instant
 import java.time.ZonedDateTime
@@ -127,7 +128,7 @@ private fun findDuplicate(bankAccountId: String, 
acctSvcrRef: String): NexusBank
  * NOTE: this type can be used BOTH for one Camt document OR
  * for a set of those.
  */
-data class CamtProcessingResult(
+data class CamtTransactionsCount(
     /**
      * Number of transactions that are new to the database.
      * Note that transaction T can be downloaded multiple times;
@@ -143,7 +144,10 @@ data class CamtProcessingResult(
      */
     val downloadedTransactions: Int
 )
-fun processCamtMessage(bankAccountId: String, camtDoc: Document, code: 
String): CamtProcessingResult {
+
+fun processCamtMessage(
+    bankAccountId: String, camtDoc: Document, code: String
+): CamtTransactionsCount {
     logger.info("processing CAMT message")
     var newTransactions = 0
     var downloadedTransactions = 0
@@ -159,6 +163,29 @@ fun processCamtMessage(bankAccountId: String, camtDoc: 
Document, code: String):
             newTransactions = -1
             return@transaction
         }
+        res.reports.forEach {
+            NexusAssert(
+                it.account.iban == acct.iban,
+                "Neuxs hit a report or statement of a wrong IBAN!"
+            )
+            it.balances.forEach { b ->
+                var clbdCount = 0
+                if (b.type == "CLBD") {
+                    clbdCount++
+                    NexusBankBalanceEntity.new {
+                        bankAccount = acct
+                        balance = b.amount.toPlainString()
+                        creditDebitIndicator = b.creditDebitIndicator.name
+                        date = b.date
+                    }
+                }
+                if (clbdCount == 0) {
+                    logger.warn("The bank didn't return ANY CLBD balances," +
+                            " in the message: ${res.messageId}.  Please 
clarify!")
+                }
+            }
+        }
+
         val stamp =
             ZonedDateTime.parse(res.creationDateTime, 
DateTimeFormatter.ISO_DATE_TIME).toInstant().toEpochMilli()
         when (code) {
@@ -226,7 +253,7 @@ fun processCamtMessage(bankAccountId: String, camtDoc: 
Document, code: String):
             }
         }
     }
-    return CamtProcessingResult(
+    return CamtTransactionsCount(
         newTransactions = newTransactions,
         downloadedTransactions = downloadedTransactions
     )
@@ -236,7 +263,7 @@ fun processCamtMessage(bankAccountId: String, camtDoc: 
Document, code: String):
  * Create new transactions for an account based on bank messages it
  * did not see before.
  */
-fun ingestBankMessagesIntoAccount(bankConnectionId: String, bankAccountId: 
String): CamtProcessingResult {
+fun ingestBankMessagesIntoAccount(bankConnectionId: String, bankAccountId: 
String): CamtTransactionsCount {
     var totalNew = 0
     var downloadedTransactions = 0
     transaction {
@@ -267,7 +294,7 @@ fun ingestBankMessagesIntoAccount(bankConnectionId: String, 
bankAccountId: Strin
         acct.highestSeenBankMessageSerialId = lastId
     }
     // return totalNew
-    return CamtProcessingResult(
+    return CamtTransactionsCount(
         newTransactions = totalNew,
         downloadedTransactions = downloadedTransactions
     )
@@ -318,7 +345,7 @@ fun addPaymentInitiation(paymentData: Pain001Data, 
debtorAccount: NexusBankAccou
 
 suspend fun fetchBankAccountTransactions(
     client: HttpClient, fetchSpec: FetchSpecJson, accountId: String
-): CamtProcessingResult {
+): CamtTransactionsCount {
     val res = transaction {
         val acct = NexusBankAccountEntity.findByName(accountId)
         if (acct == null) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
index 23abfd5..cdc2af9 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -70,7 +70,6 @@ private data class EbicsFetchSpec(
     val orderParams: EbicsOrderParams
 )
 
-// Moved eventually in a tucked "camt" file.
 fun storeCamt(bankConnectionId: String, camt: String, historyType: String) {
     val camt53doc = XMLUtil.parseStringIntoDom(camt)
     val msgId = 
camt53doc.pickStringWithRootNs("/*[1]/*[1]/root:GrpHdr/root:MsgId")
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 70bd620..592b7aa 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/Iso20022.kt
@@ -110,6 +110,10 @@ data class CamtParseResult(
     val messageType: CashManagementResponseType,
     val messageId: String,
     val creationDateTime: String,
+    /**
+     * One Camt document can contain multiple reports/statements
+     * for each account being owned by the requester.
+     */
     val reports: List<CamtReport>
 )
 
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/JSON.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/server/JSON.kt
index ef16db6..17db916 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/JSON.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/JSON.kt
@@ -441,6 +441,9 @@ data class CurrencyAmount(
     val currency: String,
     val value: BigDecimal // allows calculations
 )
+fun CurrencyAmount.toPlainString(): String {
+    return "${this.currency}:${this.value.toPlainString()}"
+}
 
 data class InitiatedPayments(
     val initiatedPayments: MutableList<PaymentStatus> = mutableListOf()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
index 5bfa62c..12fd7aa 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt
@@ -563,9 +563,21 @@ fun serverMain(host: String, port: Int) {
                         throw NexusError(HttpStatusCode.NotFound, "unknown 
bank account")
                     }
                     val holderEnc = 
URLEncoder.encode(bankAccount.accountHolder, "UTF-8")
+                    val lastSeenBalance = NexusBankBalanceEntity.find {
+                        NexusBankBalancesTable.bankAccount eq bankAccount.id
+                    }.lastOrNull()
                     return@transaction makeJsonObject {
                         prop("defaultBankConnection", 
bankAccount.defaultBankConnection?.id?.value)
                         prop("accountPaytoUri", 
"payto://iban/${bankAccount.iban}?receiver-name=$holderEnc")
+                        prop(
+                            "lastSeenBalance",
+                            if (lastSeenBalance != null) {
+                                val sign = if 
(lastSeenBalance.creditDebitIndicator == "DBIT") "-" else ""
+                                "${sign}${lastSeenBalance.balance}"
+                            } else {
+                                "not downloaded from the bank yet"
+                            }
+                        )
                     }
                 }
                 call.respond(res)

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]