summaryrefslogtreecommitdiff
path: root/sandbox/src/main/kotlin/tech/libeufin
diff options
context:
space:
mode:
authorMS <ms@taler.net>2023-07-18 13:49:55 +0200
committerMS <ms@taler.net>2023-07-18 13:49:55 +0200
commit5b56ad9ed803b3a1105e6333716dcfca7593a3f1 (patch)
tree05ab91aada2cb5cb496461519d28aee5fd9fd2d0 /sandbox/src/main/kotlin/tech/libeufin
parentf1b7567a31e2a3496fe9d1a6887d3ea599414895 (diff)
downloadlibeufin-5b56ad9ed803b3a1105e6333716dcfca7593a3f1.tar.gz
libeufin-5b56ad9ed803b3a1105e6333716dcfca7593a3f1.tar.bz2
libeufin-5b56ad9ed803b3a1105e6333716dcfca7593a3f1.zip
Getting the balance in constant time.
Diffstat (limited to 'sandbox/src/main/kotlin/tech/libeufin')
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt9
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt5
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt69
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt19
-rw-r--r--sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt39
5 files changed, 36 insertions, 105 deletions
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
index 956ccba7..ebd72d3c 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt
@@ -146,12 +146,6 @@ fun generateCashoutSubject(
" to ${amountCredit.currency}:${amountCredit.amount}"
}
-fun BigDecimal.roundToTwoDigits(): BigDecimal {
- // val twoDigitsRounding = MathContext(2)
- // return this.round(twoDigitsRounding)
- return this.setScale(2, RoundingMode.HALF_UP)
-}
-
/**
* By default, it takes the amount in the regional currency
* and applies ratio and fees to convert it to fiat. If the
@@ -522,8 +516,7 @@ fun circuitApi(circuitRoute: Route) {
// check that the balance is sufficient
val balance = getBalance(
user,
- demobank.name,
- withPending = true
+ demobank.name
)
val balanceCheck = balance - amountDebitAsNumber
if (balanceCheck < BigDecimal.ZERO && balanceCheck.abs() > BigDecimal(demobank.config.usersDebtLimit))
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index c71d1ee7..87db263e 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -34,6 +34,7 @@ import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.transactions.transactionManager
+import tech.libeufin.sandbox.CashoutSubmissionsTable.nullable
import tech.libeufin.util.*
import java.sql.Connection
import kotlin.reflect.*
@@ -498,6 +499,7 @@ class BankAccountTransactionEntity(id: EntityID<Long>) : LongEntity(id) {
* own multiple bank accounts.
*/
object BankAccountsTable : IntIdTable() {
+ val balance = text("balance").default("0")
val iban = text("iban")
val bic = text("bic").default("SANDBOXX")
val label = text("label").uniqueIndex("accountLabelIndex")
@@ -538,6 +540,7 @@ object BankAccountsTable : IntIdTable() {
class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<BankAccountEntity>(BankAccountsTable)
+ var balance by BankAccountsTable.balance
var iban by BankAccountsTable.iban
var bic by BankAccountsTable.bic
var label by BankAccountsTable.label
@@ -555,7 +558,7 @@ object BankAccountStatementsTable : IntIdTable() {
val xmlMessage = text("xmlMessage")
val bankAccount = reference("bankAccount", BankAccountsTable)
// Signed BigDecimal representing a Camt.053 CLBD field.
- val balanceClbd = text("balanceClbd")
+ val balanceClbd = text("balanceClbd").nullable()
}
class BankAccountStatementEntity(id: EntityID<Int>) : IntEntity(id) {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index fc240963..658d6373 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -282,8 +282,6 @@ fun buildCamtString(
type: Int,
subscriberIban: String,
history: MutableList<XLibeufinBankTransaction>,
- balancePrcd: BigDecimal, // Balance up to freshHistory (excluded).
- balanceClbd: BigDecimal,
currency: String
): SandboxCamt {
/**
@@ -359,45 +357,6 @@ fun buildCamtString(
}
}
}
- 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", currency)
- text(balancePrcd.abs().toPlainString())
- }
- element("CdtDbtInd") {
- text(getCreditDebitInd(balancePrcd))
- }
- 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", currency)
- text(balanceClbd.abs().toPlainString())
- }
- element("CdtDbtInd") {
- text(getCreditDebitInd(balanceClbd))
- }
- element("Dt/Dt") {
- text(dashedDate)
- }
- }
history.forEach {
this.element("Ntry") {
element("Amt") {
@@ -532,38 +491,10 @@ private fun constructCamtResponse(
}
}
if (history.size == 0) throw EbicsNoDownloadDataAvailable()
-
- /**
- * PRCD balance: balance mentioned in the last statement. This
- * will be normally zero, because statements need to be explicitly created.
- *
- * CLBD balance: PRCD + transactions accounted in the current C52.
- * Alternatively, that could be changed into: PRCD + all the pending
- * transactions. This way, the CLBD balance would closer reflect the
- * latest (pending) activities.
- */
- val prcdBalance = getBalance(bankAccount, withPending = false)
- val clbdBalance = run {
- var base = prcdBalance
- history.forEach { tx ->
- when (tx.direction) {
- XLibeufinBankDirection.DEBIT -> base -= parseDecimal(tx.amount)
- XLibeufinBankDirection.CREDIT -> base += parseDecimal(tx.amount)
- else -> {
- logger.error("Transaction with subject '${tx.subject}' is " +
- "inconsistent: neither DBIT nor CRDT")
- throw internalServerError("Transactions internal error.")
- }
- }
- }
- base
- }
val camtData = buildCamtString(
type,
bankAccount.iban,
history,
- balancePrcd = prcdBalance,
- balanceClbd = clbdBalance,
bankAccount.demoBank.config.currency
)
val paymentsList: String = if (logger.isDebugEnabled) {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index ad9417f1..fab1fca6 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -264,14 +264,10 @@ class Camt053Tick : CliktCommand(
* Resorting the closing (CLBD) balance of the last statement; will
* become the PRCD balance of the _new_ one.
*/
- val lastBalance = getBalance(accountIter, withPending = false)
- val balanceClbd = getBalance(accountIter, withPending = true)
val camtData = buildCamtString(
53,
accountIter.iban,
newStatements[accountIter.label]!!,
- balanceClbd = balanceClbd,
- balancePrcd = lastBalance,
currency = accountIter.demoBank.config.currency
)
BankAccountStatementEntity.new {
@@ -279,7 +275,6 @@ class Camt053Tick : CliktCommand(
creationTime = getUTCnow().toInstant().epochSecond
xmlMessage = camtData.camtMessage
bankAccount = accountIter
- this.balanceClbd = balanceClbd.toPlainString()
}
}
BankAccountFreshTransactionsTable.deleteAll()
@@ -802,7 +797,7 @@ val sandboxApp: Application.() -> Unit = {
val bankAccount = getBankAccountFromLabel(label, demobank)
if (!allowOwnerOrAdmin(username, label))
throw unauthorized("'${username}' has no rights over '$label'")
- val balance = getBalance(bankAccount, withPending = true)
+ val balance = getBalance(bankAccount)
object {
val balance = "${bankAccount.demoBank.config.currency}:${balance}"
val iban = bankAccount.iban
@@ -1453,10 +1448,11 @@ val sandboxApp: Application.() -> Unit = {
val authGranted = !WITH_AUTH || bankAccount.isPublic || username == "admin"
if (!authGranted && bankAccount.owner != username)
throw forbidden("Customer '$username' cannot access bank account '$accountAccessed'")
- val balance = getBalance(bankAccount, withPending = true)
+ val balance = getBalance(bankAccount)
+ logger.debug("Balance of '$username': ${balance.toPlainString()}")
call.respond(object {
val balance = object {
- val amount = "${demobank.config.currency}:${balance.abs(). toPlainString()}"
+ val amount = "${demobank.config.currency}:${balance.abs().toPlainString()}"
val credit_debit_indicator = if (balance < BigDecimal.ZERO) "debit" else "credit"
}
val paytoUri = buildIbanPaytoUri(
@@ -1574,10 +1570,7 @@ val sandboxApp: Application.() -> Unit = {
BankAccountsTable.demoBank eq demobank.id
)
}.forEach {
- val balanceIter = getBalance(
- it,
- withPending = true,
- )
+ val balanceIter = getBalance(it)
ret.publicAccounts.add(
PublicAccountInfo(
balance = "${demobank.config.currency}:$balanceIter",
@@ -1632,7 +1625,7 @@ val sandboxApp: Application.() -> Unit = {
demobank = demobank.name,
isPublic = req.isPublic
)
- val balance = getBalance(newAccount.bankAccount, withPending = true)
+ val balance = getBalance(newAccount.bankAccount)
call.respond(object {
val balance = getBalanceForJson(balance, demobank.config.currency)
val paytoUri = buildIbanPaytoUri(
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
index 748962d5..23c24dbf 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/bankAccount.kt
@@ -1,13 +1,13 @@
package tech.libeufin.sandbox
import io.ktor.http.*
+import org.jetbrains.exposed.sql.StdOutSqlLogger
+import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.util.*
import java.math.BigDecimal
-
-
/**
* Check whether the given bank account would surpass the
* debit threshold, in case the potential amount gets transferred.
@@ -21,7 +21,7 @@ fun maybeDebit(
"Demobank '${demobankName}' not found when trying to check the debit threshold" +
" for user $accountLabel"
)
- val balance = getBalance(accountLabel, demobankName, withPending = true)
+ val balance = getBalance(accountLabel, demobankName)
val maxDebt = if (accountLabel == "admin") {
demobank.config.bankDebtLimit
} else demobank.config.usersDebtLimit
@@ -52,16 +52,18 @@ fun getBalanceForJson(value: BigDecimal, currency: String): BalanceJson {
)
}
+fun getBalance(bankAccount: BankAccountEntity): BigDecimal {
+ return BigDecimal(bankAccount.balance)
+}
+
/**
- * The last balance is the one mentioned in the bank account's
- * last statement. If the bank account does not have any statement
- * yet, then zero is returned. When 'withPending' is true, it adds
- * the pending transactions to it.
- *
- * Note: because transactions are searched after the bank accounts
- * (numeric) id, the research in the database is not ambiguous.
+ * This function balances _in bank account statements_. A statement
+ * witnesses the bank account after a given business time slot. Therefore
+ * _this_ type of balance is not guaranteed to hold the _actual_ and
+ * more up-to-date bank account. It'll be used when Sandbox will support
+ * the issuing of bank statement.
*/
-fun getBalance(
+fun getBalanceForStatement(
bankAccount: BankAccountEntity,
withPending: Boolean = true
): BigDecimal {
@@ -104,8 +106,7 @@ fun getBalance(
// Gets the balance of 'accountLabel', which is hosted at 'demobankName'.
fun getBalance(accountLabel: String,
- demobankName: String = "default",
- withPending: Boolean = true
+ demobankName: String = "default"
): BigDecimal {
val demobank = getDemobank(demobankName) ?: throw SandboxError(
HttpStatusCode.InternalServerError,
@@ -124,7 +125,7 @@ fun getBalance(accountLabel: String,
demobank,
withBankFault = true
)
- return getBalance(account, withPending)
+ return getBalance(account)
}
/**
@@ -190,6 +191,7 @@ fun wireTransfer(
val timeStamp = getUTCnow().toInstant().toEpochMilli()
val transactionRef = getRandomString(8)
transaction {
+ // addLogger(StdOutSqlLogger)
BankAccountTransactionEntity.new {
creditorIban = creditAccount.iban
creditorBic = creditAccount.bic
@@ -224,6 +226,15 @@ fun wireTransfer(
this.demobank = demobank
this.pmtInfId = pmtInfId
}
+
+ // Adjusting the balances (acceptable debit conditions checked before).
+ // Debit:
+ val newDebitBalance = (BigDecimal(debitAccount.balance) - amountAsNumber).roundToTwoDigits()
+ debitAccount.balance = newDebitBalance.toPlainString() // FIXME: that's ignored!
+ // Credit:
+ val newCreditBalance = (BigDecimal(creditAccount.balance) + amountAsNumber).roundToTwoDigits()
+ creditAccount.balance = newCreditBalance.toPlainString()
+
// Signaling this wire transfer's event.
if (this.isPostgres()) {
val creditChannel = buildChannelName(