Implemented retrieving transactions of last 90 days when adding an account; switched from wasmJs to js as Ktor currently doesn't support wasmJs (due to CORS web sites cannot request banks servers directly anyway)

This commit is contained in:
dankito 2024-08-26 16:54:41 +02:00
parent 9ee82e9a5b
commit 2869544a1c
6 changed files with 75 additions and 6 deletions

View File

@ -15,8 +15,7 @@ plugins {
kotlin { kotlin {
@OptIn(ExperimentalWasmDsl::class) js {
wasmJs {
moduleName = "composeApp" moduleName = "composeApp"
browser { browser {
val projectDirPath = project.projectDir.path val projectDirPath = project.projectDir.path
@ -58,6 +57,7 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
implementation(libs.banking.client.model) implementation(libs.banking.client.model)
implementation(libs.fints4k.banking.client)
implementation(libs.kcsv) implementation(libs.kcsv)
implementation(libs.klf) implementation(libs.klf)

View File

@ -6,12 +6,16 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.codinux.banking.ui.forms.* import net.codinux.banking.ui.forms.*
import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.BankInfo
import net.codinux.banking.ui.service.Colors import net.codinux.banking.ui.service.Colors
@ -32,6 +36,11 @@ fun AddAccountDialog(
derivedStateOf { selectedBank != null && loginName.length > 3 && password.length > 3 } derivedStateOf { selectedBank != null && loginName.length > 3 && password.length > 3 }
} }
var isAddingAccount by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
Dialog(onDismissRequest = onDismiss) { Dialog(onDismissRequest = onDismiss) {
RoundedCornersCard { RoundedCornersCard {
Column(Modifier.background(Color.White).padding(8.dp)) { Column(Modifier.background(Color.White).padding(8.dp)) {
@ -93,10 +102,35 @@ fun AddAccountDialog(
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
TextButton( TextButton(
onClick = onDismiss, modifier = Modifier.width(150.dp),
enabled = isRequiredDataEntered enabled = isRequiredDataEntered && isAddingAccount == false,
onClick = {
selectedBank?.let {
isAddingAccount = true
coroutineScope.launch { // TODO: launch on Dispatchers.IO where it is available
val errorMessage = DI.bankingService.addAccount(selectedBank!!, loginName, password)
withContext(Dispatchers.Main) {
isAddingAccount = false
if (errorMessage == null) {
onDismiss()
} else {
// TODO: show error Message
}
}
}
}
}
) { ) {
Text("Hinzufügen") Row(verticalAlignment = Alignment.CenterVertically) {
if (isAddingAccount) {
CircularProgressIndicator(Modifier.padding(end = 6.dp))
}
Text("Hinzufügen")
}
} }
} }
} }

View File

@ -2,8 +2,12 @@ package net.codinux.banking.ui.service
import bankmeister.composeapp.generated.resources.Res import bankmeister.composeapp.generated.resources.Res
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import net.codinux.banking.client.SimpleBankingClientCallback
import net.codinux.banking.client.fints4k.FinTs4kBankingClientForCustomer
import net.codinux.banking.client.model.AccountTransaction import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.Amount import net.codinux.banking.client.model.Amount
import net.codinux.banking.fints.config.FinTsClientConfiguration
import net.codinux.banking.fints.config.FinTsClientOptions
import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.BankInfo
import net.codinux.csv.reader.CsvReader import net.codinux.csv.reader.CsvReader
import net.codinux.log.logger import net.codinux.log.logger
@ -33,6 +37,29 @@ class BankingService(
return transactions return transactions
} }
suspend fun addAccount(bank: BankInfo, loginName: String, password: String): String? {
try {
val config = FinTsClientConfiguration(FinTsClientOptions(true))
val client = FinTs4kBankingClientForCustomer(bank.bankCode, loginName, password, config, SimpleBankingClientCallback { tanChallenge, callback ->
// TODO: show EnterTanDialog
})
val response = client.getAccountDataAsync()
response.data?.let { accountData ->
if (cachedTransactions == null) {
cachedTransactions = accountData.bookedTransactions
} else {
cachedTransactions = (cachedTransactions!! + accountData.bookedTransactions).sortedByDescending { it.valueDate }
}
}
return response.error?.internalError ?: response.error?.errorMessagesFromBank?.joinToString("\n")
} catch (e: Throwable) {
log.error(e) { "Could not add account for ${bank.name} $loginName" }
return e.message
}
}
private suspend fun readTransactionsFromCsv(): List<AccountTransaction> { private suspend fun readTransactionsFromCsv(): List<AccountTransaction> {
val csv = Res.readBytes("files/transactions.csv").decodeToString() val csv = Res.readBytes("files/transactions.csv").decodeToString()
val csvReader = CsvReader(hasHeaderRow = true, reuseRowInstance = true, skipEmptyRows = true).read(csv) val csvReader = CsvReader(hasHeaderRow = true, reuseRowInstance = true, skipEmptyRows = true).read(csv)

View File

@ -9,3 +9,6 @@ android.useAndroidX=true
#Kotlin Multiplatform #Kotlin Multiplatform
kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.enableCInteropCommonization=true
# Compose Multiplatform Web
org.jetbrains.compose.experimental.jscanvas.enabled=true

View File

@ -26,6 +26,7 @@ junit = "4.13.2"
[libraries] [libraries]
banking-client-model = { group = "net.codinux.banking.client", name = "banking-client-model", version.ref = "banking-client" } banking-client-model = { group = "net.codinux.banking.client", name = "banking-client-model", version.ref = "banking-client" }
fints4k-banking-client = { group = "net.codinux.banking.client", name = "fints4k-banking-client", version.ref = "banking-client" }
kcsv = { group = "net.codinux.csv", name = "kcsv", version.ref = "kcsv" } kcsv = { group = "net.codinux.csv", name = "kcsv", version.ref = "kcsv" }
klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" } klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" }

View File

@ -25,6 +25,10 @@ dependencyResolutionManagement {
} }
} }
mavenCentral() mavenCentral()
mavenLocal()
maven {
setUrl("https://maven.dankito.net/api/packages/codinux/maven")
}
} }
} }