Compare commits
16 Commits
41586b0148
...
3c3734d565
Author | SHA1 | Date |
---|---|---|
dankito | 3c3734d565 | |
dankito | af99608dd6 | |
dankito | a11c862ecc | |
dankito | f6dfada5bf | |
dankito | b4ea02bbd8 | |
dankito | 2d472d1683 | |
dankito | de36a403df | |
dankito | debae8b7ca | |
dankito | 09a1a41c20 | |
dankito | 75ccc648c5 | |
dankito | 34f2fca126 | |
dankito | e883713eba | |
dankito | 4cd727b5c0 | |
dankito | 96c8cf59cd | |
dankito | d7a9acbe56 | |
dankito | 9330b72726 |
|
@ -137,10 +137,10 @@ android {
|
||||||
sourceSets["main"].resources.srcDirs("src/commonMain/resources")
|
sourceSets["main"].resources.srcDirs("src/commonMain/resources")
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "net.codinux.banking.ui"
|
applicationId = "net.codinux.banking.android" // the appId of the old Bankmeister app to be able to use the old PlayStore entry
|
||||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||||
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||||
versionCode = 1
|
versionCode = 10
|
||||||
versionName = "1.0.0-Alpha-12"
|
versionName = "1.0.0-Alpha-12"
|
||||||
}
|
}
|
||||||
packaging {
|
packaging {
|
||||||
|
@ -148,18 +148,26 @@ android {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
getByName("release") {
|
named("debug") {
|
||||||
|
applicationIdSuffix = ".develop"
|
||||||
|
}
|
||||||
|
|
||||||
|
named("release") {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation(compose.uiTooling)
|
debugImplementation(compose.uiTooling)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Bankmeister Dev</string>
|
||||||
|
</resources>
|
|
@ -13,8 +13,8 @@ import net.codinux.banking.ui.model.TanChallengeReceived
|
||||||
@Composable
|
@Composable
|
||||||
fun EnterTanDialogPreview_EnterTan() {
|
fun EnterTanDialogPreview_EnterTan() {
|
||||||
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
||||||
val user = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
val bank = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, user = user)
|
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, bank = bank)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ fun EnterTanDialogPreview_TanImage() {
|
||||||
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
||||||
val tanImage = TanImage("image/png", tanImageBytes)
|
val tanImage = TanImage("image/png", tanImageBytes)
|
||||||
|
|
||||||
val user = BankViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank", BankingGroup.Comdirect)
|
val bank = BankViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank", BankingGroup.Comdirect)
|
||||||
|
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, user)
|
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, bank)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
||||||
|
@ -50,10 +50,10 @@ fun EnterTanDialogPreview_WithMultipleTanMedia() { // shows that dialog is reall
|
||||||
TanMedium(TanMediumType.TanGenerator, "SparkassenCard (Debitkarte)", TanMediumStatus.Used, TanGeneratorTanMedium("5432109876"))
|
TanMedium(TanMediumType.TanGenerator, "SparkassenCard (Debitkarte)", TanMediumStatus.Used, TanGeneratorTanMedium("5432109876"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val user = BankViewInfo("10010010", "Ihr krasser Login Name", "Eine ganz gewöhnliche Sparkasse", BankingGroup.Sparkasse)
|
val bank = BankViewInfo("10010010", "Ihr krasser Login Name", "Eine ganz gewöhnliche Sparkasse", BankingGroup.Sparkasse)
|
||||||
val account = BankAccountViewInfo("12345678", null, BankAccountType.CheckingAccount, null, "Giro Konto")
|
val account = BankAccountViewInfo("12345678", null, BankAccountType.CheckingAccount, null, "Giro Konto")
|
||||||
|
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetTransactions, "Sie möchten eine \"Umsatzabfrage\" freigeben: Bitte bestätigen Sie den \"Startcode 80061030\" mit der Taste \"OK\".", "913", tanMethods, "SparkassenCard (Debitkarte)", tanMedia, tanImage, null, user, account)
|
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetTransactions, "Sie möchten eine \"Umsatzabfrage\" freigeben: Bitte bestätigen Sie den \"Startcode 80061030\" mit der Taste \"OK\".", "913", tanMethods, "SparkassenCard (Debitkarte)", tanMedia, tanImage, null, bank, account)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,8 @@ fun EnterTanDialogPreview_WithMultipleTanMedia() { // shows that dialog is reall
|
||||||
@Composable
|
@Composable
|
||||||
fun EnterTanDialogPreview_Flickercode() {
|
fun EnterTanDialogPreview_Flickercode() {
|
||||||
val tanMethods = listOf(TanMethod("chipTAN Flickercode", TanMethodType.ChipTanFlickercode, "902"))
|
val tanMethods = listOf(TanMethod("chipTAN Flickercode", TanMethodType.ChipTanFlickercode, "902"))
|
||||||
val user = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
val bank = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Flickercode, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, user = user, flickerCode = FlickerCode("", ""))
|
val tanChallenge = TanChallenge(TanChallengeType.Flickercode, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, bank = bank, flickerCode = FlickerCode("", ""))
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
|
@ -1,19 +1,30 @@
|
||||||
package net.codinux.banking.dataaccess
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
import net.codinux.banking.client.model.securitiesaccount.Holding
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.settings.UiSettings
|
||||||
|
|
||||||
interface BankingRepository {
|
interface BankingRepository {
|
||||||
|
|
||||||
fun getAllUsers(): List<UserEntity>
|
fun getAppSettings(): AppSettings?
|
||||||
|
|
||||||
suspend fun persistUser(user: User): UserEntity
|
suspend fun saveAppSettings(settings: AppSettings)
|
||||||
|
|
||||||
|
fun getUiSettings(settings: UiSettings)
|
||||||
|
|
||||||
|
suspend fun saveUiSettings(settings: UiSettings)
|
||||||
|
|
||||||
|
|
||||||
|
fun getAllBanks(): List<BankAccessEntity>
|
||||||
|
|
||||||
|
suspend fun persistBank(bank: BankAccess): BankAccessEntity
|
||||||
|
|
||||||
suspend fun persistTransactions(bankAccount: BankAccountEntity, transactions: List<AccountTransaction>): List<AccountTransactionEntity>
|
suspend fun persistTransactions(bankAccount: BankAccountEntity, transactions: List<AccountTransaction>): List<AccountTransactionEntity>
|
||||||
|
|
||||||
|
@ -29,7 +40,7 @@ interface BankingRepository {
|
||||||
|
|
||||||
fun getAllAccountTransactions(): List<AccountTransactionEntity>
|
fun getAllAccountTransactions(): List<AccountTransactionEntity>
|
||||||
|
|
||||||
fun getAllTransactionsOfUser(user: UserEntity): List<AccountTransactionEntity>
|
fun getAllTransactionsForBank(bank: BankAccessEntity): List<AccountTransactionEntity>
|
||||||
|
|
||||||
fun getTransactionById(transactionId: Long): AccountTransactionEntity?
|
fun getTransactionById(transactionId: Long): AccountTransactionEntity?
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,51 @@
|
||||||
package net.codinux.banking.dataaccess
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
import net.codinux.banking.client.model.securitiesaccount.Holding
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.settings.UiSettings
|
||||||
|
|
||||||
class InMemoryBankingRepository(
|
class InMemoryBankingRepository(
|
||||||
users: Collection<User> = emptyList(),
|
banks: Collection<BankAccess> = emptyList(),
|
||||||
transactions: Collection<AccountTransaction> = emptyList()
|
transactions: Collection<AccountTransaction> = emptyList(),
|
||||||
|
private var appSettings: AppSettings = AppSettings()
|
||||||
) : BankingRepository {
|
) : BankingRepository {
|
||||||
|
|
||||||
private var nextId = 0L // TODO: make thread-safe
|
private var nextId = 0L // TODO: make thread-safe
|
||||||
|
|
||||||
private val users = users.map { map(it) }.toMutableList()
|
private val banks = banks.map { map(it) }.toMutableList()
|
||||||
|
|
||||||
private val transactions = transactions.map { map(it) }.toMutableList()
|
private val transactions = transactions.map { map(it) }.toMutableList()
|
||||||
|
|
||||||
|
private lateinit var uiSettings: UiSettings
|
||||||
|
|
||||||
override fun getAllUsers(): List<UserEntity> = users.toList()
|
|
||||||
|
|
||||||
override suspend fun persistUser(user: User): UserEntity {
|
override fun getAppSettings(): AppSettings? = appSettings
|
||||||
val entity = map(user) // TODO: may fix someday and add also BankAccounts and their id
|
|
||||||
this.users.add(entity)
|
override suspend fun saveAppSettings(settings: AppSettings) {
|
||||||
|
this.appSettings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUiSettings(settings: UiSettings) {
|
||||||
|
this.uiSettings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun saveUiSettings(settings: UiSettings) {
|
||||||
|
this.uiSettings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getAllBanks(): List<BankAccessEntity> = banks.toList()
|
||||||
|
|
||||||
|
override suspend fun persistBank(bank: BankAccess): BankAccessEntity {
|
||||||
|
val entity = map(bank) // TODO: may fix someday and add also BankAccounts and their id
|
||||||
|
this.banks.add(entity)
|
||||||
return entity
|
return entity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,27 +70,27 @@ class InMemoryBankingRepository(
|
||||||
|
|
||||||
override fun getAllAccountTransactions(): List<AccountTransactionEntity> = transactions.toList()
|
override fun getAllAccountTransactions(): List<AccountTransactionEntity> = transactions.toList()
|
||||||
|
|
||||||
override fun getAllTransactionsOfUser(user: UserEntity): List<AccountTransactionEntity> =
|
override fun getAllTransactionsForBank(bank: BankAccessEntity): List<AccountTransactionEntity> =
|
||||||
getAllAccountTransactions().filter { it.userId == user.id }
|
getAllAccountTransactions().filter { it.bankId == bank.id }
|
||||||
|
|
||||||
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
||||||
getAllAccountTransactions().firstOrNull { it.id == transactionId }
|
getAllAccountTransactions().firstOrNull { it.id == transactionId }
|
||||||
|
|
||||||
|
|
||||||
private fun map(account: User) = UserEntity(
|
private fun map(bank: BankAccess) = BankAccessEntity(
|
||||||
nextId++,
|
nextId++,
|
||||||
account.bankCode, account.loginName, account.password, account.bankName, account.bic, account.customerName, account.userId,
|
bank.domesticBankCode, bank.loginName, bank.password, bank.bankName, bank.bic, bank.customerName, bank.userId,
|
||||||
// TODO: may fix someday and also add BankAccounts
|
// TODO: may fix someday and also add BankAccounts
|
||||||
emptyList(), account.selectedTanMethodIdentifier, emptyList(), account.selectedTanMediumIdentifier, emptyList(),
|
emptyList(), bank.selectedTanMethodIdentifier, mutableListOf(), bank.selectedTanMediumIdentifier, mutableListOf(),
|
||||||
account.bankingGroup, account.serverAddress,
|
bank.bankingGroup, bank.serverAddress, bank.countryCode,
|
||||||
account.userSetDisplayName, account.displayIndex,
|
bank.userSetDisplayName, bank.displayIndex,
|
||||||
account.iconUrl, account.wrongCredentialsEntered,
|
bank.iconUrl, bank.wrongCredentialsEntered,
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: someday may fix and get userId and bankAccountId
|
// TODO: someday may fix and get bankId and accountId
|
||||||
private fun map(transaction: AccountTransaction, userId: Long = nextId++, bankAccountId: Long = nextId++) = AccountTransactionEntity(
|
private fun map(transaction: AccountTransaction, bankId: Long = nextId++, accountId: Long = nextId++) = AccountTransactionEntity(
|
||||||
nextId++,
|
nextId++,
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
transaction.amount, transaction.currency, transaction.reference,
|
transaction.amount, transaction.currency, transaction.reference,
|
||||||
transaction.bookingDate, transaction.valueDate,
|
transaction.bookingDate, transaction.valueDate,
|
||||||
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
|
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
|
||||||
|
|
|
@ -8,6 +8,10 @@ import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
import net.codinux.banking.client.model.tan.*
|
import net.codinux.banking.client.model.tan.*
|
||||||
import net.codinux.banking.dataaccess.entities.*
|
import net.codinux.banking.dataaccess.entities.*
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||||
|
import net.codinux.banking.ui.model.settings.AppAuthenticationMethod
|
||||||
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.settings.UiSettings
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
import kotlin.enums.EnumEntries
|
import kotlin.enums.EnumEntries
|
||||||
import kotlin.js.JsName
|
import kotlin.js.JsName
|
||||||
|
@ -19,53 +23,90 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
private val database = BankmeisterDb(sqlDriver)
|
private val database = BankmeisterDb(sqlDriver)
|
||||||
|
|
||||||
private val userQueries = database.userQueries
|
private val settingsQueries = database.settingsQueries
|
||||||
|
|
||||||
|
private val bankQueries = database.bankQueries
|
||||||
|
|
||||||
private val accountTransactionQueries = database.accountTransactionQueries
|
private val accountTransactionQueries = database.accountTransactionQueries
|
||||||
|
|
||||||
private val log by logger()
|
private val log by logger()
|
||||||
|
|
||||||
|
|
||||||
override fun getAllUsers(): List<UserEntity> {
|
override fun getAppSettings(): AppSettings? =
|
||||||
val bankAccounts = getAllBankAccounts().groupBy { it.userId }
|
settingsQueries.getAppSettings { _,
|
||||||
val tanMethods = getAllTanMethods().groupBy { it.userId }
|
authenticationMethod, hashedPassword, updateAccountsOnAppStart, updateAccountsIfNoUpdatedForHours,
|
||||||
val tanMedia = getAllTanMedia().groupBy { it.userId }
|
sideMenuWidth,
|
||||||
val holdings = getAllHoldings().groupBy { it.bankAccountId }
|
windowPositionX, windowPositionY, windowWidth, windowHeight,
|
||||||
|
windowState
|
||||||
|
->
|
||||||
|
AppSettings(mapToEnum(authenticationMethod, AppAuthenticationMethod.entries), hashedPassword, updateAccountsOnAppStart, mapToInt(updateAccountsIfNoUpdatedForHours))
|
||||||
|
}.executeAsOneOrNull()
|
||||||
|
|
||||||
return userQueries.selectAllUsers { id, bankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodIdentifier, selectedTanMediumIdentifier, bankingGroup, serverAddress, userSetDisplayName, clientData, displayIndex, iconUrl, wrongCredentialsEntered ->
|
override suspend fun saveAppSettings(settings: AppSettings) {
|
||||||
UserEntity(id, bankCode, loginName, password, bankName, bic, customerName, userId, getAccountsOfUser(id, bankAccounts, holdings), selectedTanMethodIdentifier, tanMethods[id] ?: emptyList(), selectedTanMediumIdentifier, tanMedia[id] ?: emptyList(),
|
settingsQueries.upsertAppSettings(
|
||||||
bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered)
|
mapEnum(settings.authenticationMethod), settings.hashedPassword, settings.updateAccountsOnAppStart, mapInt(settings.updateAccountsIfNoUpdatedForHours),
|
||||||
|
0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getUiSettings(settings: UiSettings) {
|
||||||
|
settingsQueries.getUiSettings { _, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors ->
|
||||||
|
settings.transactionsGrouping.value = mapToEnum(transactionsGrouping, TransactionsGrouping.entries)
|
||||||
|
settings.showBalance.value = showBalance
|
||||||
|
settings.showBankIcons.value = showBankIcons
|
||||||
|
settings.showColoredAmounts.value = showColoredAmounts
|
||||||
|
settings.showTransactionsInAlternatingColors.value = showTransactionsInAlternatingColors
|
||||||
|
}.executeAsOneOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun saveUiSettings(settings: UiSettings) {
|
||||||
|
settingsQueries.upsertUiSettings(mapEnum(settings.transactionsGrouping.value), settings.showBalance.value, settings.showBankIcons.value, settings.showColoredAmounts.value, settings.showTransactionsInAlternatingColors.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getAllBanks(): List<BankAccessEntity> {
|
||||||
|
val bankAccounts = getAllBankAccounts().groupBy { it.bankId }
|
||||||
|
val tanMethods = getAllTanMethods().groupBy { it.bankId }.mapValues { it.value.toMutableList() }
|
||||||
|
val tanMedia = getAllTanMedia().groupBy { it.bankId }.mapValues { it.value.toMutableList() }
|
||||||
|
val holdings = getAllHoldings().groupBy { it.accountId }
|
||||||
|
|
||||||
|
return bankQueries.getAllBanks { id, domesticBankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodIdentifier, selectedTanMediumIdentifier, bankingGroup, serverAddress, countryCode, userSetDisplayName, clientData, displayIndex, iconUrl, wrongCredentialsEntered ->
|
||||||
|
BankAccessEntity(id, domesticBankCode, loginName, password, bankName, bic, customerName, userId, getAccountsOfBank(id, bankAccounts, holdings), selectedTanMethodIdentifier, tanMethods[id] ?: mutableListOf(), selectedTanMediumIdentifier, tanMedia[id] ?: mutableListOf(),
|
||||||
|
bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, countryCode, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered)
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun getAccountsOfUser(userId: Long, bankAccounts: Map<Long, List<BankAccountEntity>>, holdings: Map<Long, List<HoldingEntity>>): List<BankAccountEntity> {
|
protected open fun getAccountsOfBank(bankId: Long, bankAccounts: Map<Long, List<BankAccountEntity>>, holdings: Map<Long, List<HoldingEntity>>): List<BankAccountEntity> {
|
||||||
return bankAccounts[userId].orEmpty().onEach {
|
return bankAccounts[bankId].orEmpty().onEach {
|
||||||
it.holdings = holdings[it.id].orEmpty()
|
it.addHoldings(holdings[it.id].orEmpty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun persistUser(user: User): UserEntity {
|
override suspend fun persistBank(bank: BankAccess): BankAccessEntity {
|
||||||
return userQueries.transactionWithResult {
|
return bankQueries.transactionWithResult {
|
||||||
userQueries.insertUser(user.bankCode, user.loginName, user.password, user.bankName, user.bic,
|
bankQueries.insertBank(bank.domesticBankCode, bank.loginName, bank.password, bank.bankName, bank.bic,
|
||||||
user.customerName, user.userId, user.selectedTanMethodIdentifier, user.selectedTanMediumIdentifier,
|
bank.customerName, bank.userId, bank.selectedTanMethodIdentifier, bank.selectedTanMediumIdentifier,
|
||||||
user.bankingGroup?.name, user.serverAddress, null, user.userSetDisplayName, user.displayIndex.toLong(), user.iconUrl, user.wrongCredentialsEntered
|
bank.bankingGroup?.name, bank.serverAddress, bank.countryCode, null, bank.userSetDisplayName, bank.displayIndex.toLong(), bank.iconUrl, bank.wrongCredentialsEntered
|
||||||
)
|
)
|
||||||
|
|
||||||
val userId = getLastInsertedId() // getLastInsertedId() / last_insert_rowid() has to be called in a transaction with the insert operation, otherwise it will not work
|
val bankId = getLastInsertedId() // getLastInsertedId() / last_insert_rowid() has to be called in a transaction with the insert operation, otherwise it will not work
|
||||||
|
|
||||||
val bankAccounts = persistBankAccounts(userId, user.accounts)
|
val bankAccounts = persistBankAccounts(bankId, bank.accounts)
|
||||||
|
|
||||||
val tanMethods = persistTanMethods(userId, user.tanMethods)
|
val tanMethods = persistTanMethods(bankId, bank.tanMethods)
|
||||||
val tanMedia = persistTanMedia(userId, user.tanMedia)
|
val tanMedia = persistTanMedia(bankId, bank.tanMedia)
|
||||||
|
|
||||||
UserEntity(userId, user, bankAccounts, tanMethods, tanMedia)
|
BankAccessEntity(bankId, bank, bankAccounts, tanMethods, tanMedia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getAllBankAccounts(): List<BankAccountEntity> = userQueries.selectAllBankAccounts { id, userId, identifier, subAccountNumber, iban, productName, accountHolderName, type, currency, accountLimit, isAccountTypeSupportedByApplication, features, balance, serverTransactionsRetentionDays, lastAccountUpdateTime, retrievedTransactionsFrom, userSetDisplayName, displayIndex, hideAccount, includeInAutomaticAccountsUpdate ->
|
fun getAllBankAccounts(): List<BankAccountEntity> = bankQueries.getAllBankAccounts { id, bankId, identifier, subAccountNumber, iban, productName, accountHolderName, type, currency, accountLimit, isAccountTypeSupportedByApplication, features, balance, serverTransactionsRetentionDays, lastAccountUpdateTime, retrievedTransactionsFrom, userSetDisplayName, displayIndex, hideAccount, includeInAutomaticAccountsUpdate ->
|
||||||
BankAccountEntity(
|
BankAccountEntity(
|
||||||
id, userId,
|
id, bankId,
|
||||||
|
|
||||||
identifier, subAccountNumber, iban, productName,
|
identifier, subAccountNumber, iban, productName,
|
||||||
|
|
||||||
|
@ -79,22 +120,22 @@ open class SqliteBankingRepository(
|
||||||
mapToInt(serverTransactionsRetentionDays),
|
mapToInt(serverTransactionsRetentionDays),
|
||||||
mapToInstant(lastAccountUpdateTime), mapToDate(retrievedTransactionsFrom),
|
mapToInstant(lastAccountUpdateTime), mapToDate(retrievedTransactionsFrom),
|
||||||
|
|
||||||
mutableListOf(), mutableListOf(), emptyList(),
|
mutableListOf(), mutableListOf(), mutableListOf(),
|
||||||
|
|
||||||
userSetDisplayName, mapToInt(displayIndex),
|
userSetDisplayName, mapToInt(displayIndex),
|
||||||
hideAccount, includeInAutomaticAccountsUpdate
|
hideAccount, includeInAutomaticAccountsUpdate
|
||||||
)
|
)
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
|
|
||||||
private suspend fun persistBankAccounts(userId: Long, bankAccounts: Collection<BankAccount>): List<BankAccountEntity> =
|
private suspend fun persistBankAccounts(bankId: Long, bankAccounts: Collection<BankAccount>): List<BankAccountEntity> =
|
||||||
bankAccounts.map { persistBankAccount(userId, it) }
|
bankAccounts.map { persistBankAccount(bankId, it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has to be executed in a transaction in order that getting persisted BankAccount's id works~
|
* Has to be executed in a transaction in order that getting persisted BankAccount's id works~
|
||||||
*/
|
*/
|
||||||
private suspend fun persistBankAccount(userId: Long, account: BankAccount): BankAccountEntity {
|
private suspend fun persistBankAccount(bankId: Long, account: BankAccount): BankAccountEntity {
|
||||||
userQueries.insertBankAccount(
|
bankQueries.insertBankAccount(
|
||||||
userId,
|
bankId,
|
||||||
account.identifier, account.accountHolderName, mapEnum(account.type),
|
account.identifier, account.accountHolderName, mapEnum(account.type),
|
||||||
account.iban, account.subAccountNumber, account.productName, account.currency, account.accountLimit,
|
account.iban, account.subAccountNumber, account.productName, account.currency, account.accountLimit,
|
||||||
|
|
||||||
|
@ -111,49 +152,53 @@ open class SqliteBankingRepository(
|
||||||
val accountId = getLastInsertedId()
|
val accountId = getLastInsertedId()
|
||||||
|
|
||||||
val accountTransactionEntities = account.bookedTransactions.map { transaction ->
|
val accountTransactionEntities = account.bookedTransactions.map { transaction ->
|
||||||
persistTransaction(userId, accountId, transaction)
|
persistTransaction(bankId, accountId, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
val holdings = account.holdings.map { holding -> persistHolding(userId, accountId, holding) }
|
val holdings = account.holdings.map { holding -> persistHolding(bankId, accountId, holding) }
|
||||||
|
|
||||||
return BankAccountEntity(accountId, userId, account, accountTransactionEntities, holdings)
|
return BankAccountEntity(accountId, bankId, account, accountTransactionEntities, holdings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getAllTanMethods(): List<TanMethodEntity> = userQueries.selectAllTanMethods { id, userId, displayName, type, identifier, maxTanInputLength, allowedTanFormat ->
|
private fun getAllTanMethods(): List<TanMethodEntity> = bankQueries.getAllTanMethods { id, bankId, displayName, type, identifier, maxTanInputLength, allowedTanFormat, userSetDisplayName ->
|
||||||
TanMethodEntity(
|
TanMethodEntity(
|
||||||
id,
|
id,
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
displayName,
|
displayName,
|
||||||
mapToEnum(type, TanMethodType.entries),
|
mapToEnum(type, TanMethodType.entries),
|
||||||
identifier,
|
identifier,
|
||||||
mapToInt(maxTanInputLength),
|
mapToInt(maxTanInputLength),
|
||||||
mapToEnum(allowedTanFormat, AllowedTanFormat.entries)
|
mapToEnum(allowedTanFormat, AllowedTanFormat.entries),
|
||||||
|
|
||||||
|
userSetDisplayName
|
||||||
)
|
)
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
|
|
||||||
private suspend fun persistTanMethods(userId: Long, tanMethods: List<TanMethod>): List<TanMethodEntity> =
|
private suspend fun persistTanMethods(bankId: Long, tanMethods: List<TanMethod>): List<TanMethodEntity> =
|
||||||
tanMethods.map { persistTanMethod(userId, it) }
|
tanMethods.map { persistTanMethod(bankId, it) }
|
||||||
|
|
||||||
private suspend fun persistTanMethod(userId: Long, tanMethod: TanMethod): TanMethodEntity {
|
private suspend fun persistTanMethod(bankId: Long, tanMethod: TanMethod): TanMethodEntity {
|
||||||
userQueries.insertTanMethod(
|
bankQueries.insertTanMethod(
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
tanMethod.displayName,
|
tanMethod.displayName,
|
||||||
mapEnum(tanMethod.type),
|
mapEnum(tanMethod.type),
|
||||||
tanMethod.identifier,
|
tanMethod.identifier,
|
||||||
mapInt(tanMethod.maxTanInputLength),
|
mapInt(tanMethod.maxTanInputLength),
|
||||||
mapEnum(tanMethod.allowedTanFormat)
|
mapEnum(tanMethod.allowedTanFormat),
|
||||||
|
|
||||||
|
tanMethod.userSetDisplayName
|
||||||
)
|
)
|
||||||
|
|
||||||
val tanMethodId = getLastInsertedId()
|
val tanMethodId = getLastInsertedId()
|
||||||
|
|
||||||
return TanMethodEntity(tanMethodId, userId, tanMethod)
|
return TanMethodEntity(tanMethodId, bankId, tanMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getAllTanMedia(): List<TanMediumEntity> = userQueries.selectAllTanMedia { id, userId, type, mediumName, status, phoneNumber, concealedPhoneNumber, cardNumber, cardSequenceNumber, cardType, validFrom, validTo ->
|
private fun getAllTanMedia(): List<TanMediumEntity> = bankQueries.getAllTanMedia { id, bankId, type, mediumName, status, phoneNumber, concealedPhoneNumber, cardNumber, cardSequenceNumber, cardType, validFrom, validTo, userSetDisplayName ->
|
||||||
val mobilePhone = if (phoneNumber != null || concealedPhoneNumber != null) {
|
val mobilePhone = if (phoneNumber != null || concealedPhoneNumber != null) {
|
||||||
MobilePhoneTanMedium(phoneNumber, concealedPhoneNumber)
|
MobilePhoneTanMedium(phoneNumber, concealedPhoneNumber)
|
||||||
} else {
|
} else {
|
||||||
|
@ -168,23 +213,25 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
TanMediumEntity(
|
TanMediumEntity(
|
||||||
id,
|
id,
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
mapToEnum(type, TanMediumType.entries),
|
mapToEnum(type, TanMediumType.entries),
|
||||||
mediumName,
|
mediumName,
|
||||||
mapToEnum(status, TanMediumStatus.entries),
|
mapToEnum(status, TanMediumStatus.entries),
|
||||||
|
|
||||||
tanGenerator,
|
tanGenerator,
|
||||||
mobilePhone
|
mobilePhone,
|
||||||
|
|
||||||
|
userSetDisplayName
|
||||||
)
|
)
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
|
|
||||||
private suspend fun persistTanMedia(userId: Long, tanMedia: List<TanMedium>): List<TanMediumEntity> =
|
private suspend fun persistTanMedia(bankId: Long, tanMedia: List<TanMedium>): List<TanMediumEntity> =
|
||||||
tanMedia.map { persistTanMedium(userId, it) }
|
tanMedia.map { persistTanMedium(bankId, it) }
|
||||||
|
|
||||||
private suspend fun persistTanMedium(userId: Long, medium: TanMedium): TanMediumEntity {
|
private suspend fun persistTanMedium(bankId: Long, medium: TanMedium): TanMediumEntity {
|
||||||
userQueries.insertTanMedium(
|
bankQueries.insertTanMedium(
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
mapEnum(medium.type),
|
mapEnum(medium.type),
|
||||||
medium.mediumName,
|
medium.mediumName,
|
||||||
|
@ -197,31 +244,33 @@ open class SqliteBankingRepository(
|
||||||
medium.tanGenerator?.cardSequenceNumber,
|
medium.tanGenerator?.cardSequenceNumber,
|
||||||
mapInt(medium.tanGenerator?.cardType),
|
mapInt(medium.tanGenerator?.cardType),
|
||||||
mapDate(medium.tanGenerator?.validFrom),
|
mapDate(medium.tanGenerator?.validFrom),
|
||||||
mapDate(medium.tanGenerator?.validTo)
|
mapDate(medium.tanGenerator?.validTo),
|
||||||
|
|
||||||
|
medium.userSetDisplayName
|
||||||
)
|
)
|
||||||
|
|
||||||
val tanMediumId = getLastInsertedId()
|
val tanMediumId = getLastInsertedId()
|
||||||
|
|
||||||
return TanMediumEntity(tanMediumId, userId, medium)
|
return TanMediumEntity(tanMediumId, bankId, medium)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected open fun getAllHoldings(): List<HoldingEntity> =
|
protected open fun getAllHoldings(): List<HoldingEntity> =
|
||||||
accountTransactionQueries.selectAllHoldings { id, userId, bankAccountId, name, isin, wkn, quantity, currency, totalBalance, marketValue, performancePercentage, totalCostPrice, averageCostPrice, pricingTime, buyingDate ->
|
accountTransactionQueries.selectAllHoldings { id, bankId, accountId, name, isin, wkn, quantity, currency, totalBalance, marketValue, performancePercentage, totalCostPrice, averageCostPrice, pricingTime, buyingDate ->
|
||||||
HoldingEntity(id, userId, bankAccountId, name, isin, wkn, mapToInt(quantity), currency, mapToAmount(totalBalance), mapToAmount(marketValue), performancePercentage?.toFloat(), mapToAmount(totalCostPrice), mapToAmount(averageCostPrice), mapToInstant(pricingTime), mapToDate(buyingDate))
|
HoldingEntity(id, bankId, accountId, name, isin, wkn, mapToInt(quantity), currency, mapToAmount(totalBalance), mapToAmount(marketValue), performancePercentage?.toFloat(), mapToAmount(totalCostPrice), mapToAmount(averageCostPrice), mapToInstant(pricingTime), mapToDate(buyingDate))
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
|
|
||||||
override suspend fun persistHoldings(bankAccount: BankAccountEntity, holdings: List<Holding>): List<HoldingEntity> =
|
override suspend fun persistHoldings(bankAccount: BankAccountEntity, holdings: List<Holding>): List<HoldingEntity> =
|
||||||
accountTransactionQueries.transactionWithResult {
|
accountTransactionQueries.transactionWithResult {
|
||||||
holdings.map { persistHolding(bankAccount.userId, bankAccount.id, it) }
|
holdings.map { persistHolding(bankAccount.bankId, bankAccount.id, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has to be executed in a transaction in order that getting persisted Holding's id works~
|
* Has to be executed in a transaction in order that getting persisted Holding's id works~
|
||||||
*/
|
*/
|
||||||
protected open suspend fun persistHolding(userId: Long, bankAccountId: Long, holding: Holding): HoldingEntity {
|
protected open suspend fun persistHolding(bankId: Long, accountId: Long, holding: Holding): HoldingEntity {
|
||||||
accountTransactionQueries.insertHolding(
|
accountTransactionQueries.insertHolding(
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
|
|
||||||
holding.name, holding.isin, holding.wkn,
|
holding.name, holding.isin, holding.wkn,
|
||||||
|
|
||||||
|
@ -234,7 +283,7 @@ open class SqliteBankingRepository(
|
||||||
mapInstant(holding.pricingTime), mapDate(holding.buyingDate)
|
mapInstant(holding.pricingTime), mapDate(holding.buyingDate)
|
||||||
)
|
)
|
||||||
|
|
||||||
return HoldingEntity(getLastInsertedId(), userId, bankAccountId, holding)
|
return HoldingEntity(getLastInsertedId(), bankId, accountId, holding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateHoldings(holdings: List<HoldingEntity>) {
|
override suspend fun updateHoldings(holdings: List<HoldingEntity>) {
|
||||||
|
@ -266,16 +315,16 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
|
|
||||||
override fun getAllAccountTransactionsAsViewModel(): List<AccountTransactionViewModel> =
|
override fun getAllAccountTransactionsAsViewModel(): List<AccountTransactionViewModel> =
|
||||||
accountTransactionQueries.selectAllTransactionsAsViewModel { id, userId, bankAccountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetDisplayName, category ->
|
accountTransactionQueries.getAllTransactionsAsViewModel { id, bankId, accountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetDisplayName, userSetOtherPartyName ->
|
||||||
AccountTransactionViewModel(id, userId, bankAccountId, mapToAmount(amount), currency, reference, mapToDate(valueDate), otherPartyName, postingText, userSetDisplayName, category)
|
AccountTransactionViewModel(id, bankId, accountId, mapToAmount(amount), currency, reference, mapToDate(valueDate), otherPartyName, postingText, userSetDisplayName, userSetOtherPartyName)
|
||||||
}.executeAsList()
|
}.executeAsList()
|
||||||
|
|
||||||
override fun getAllAccountTransactions(): List<AccountTransactionEntity> {
|
override fun getAllAccountTransactions(): List<AccountTransactionEntity> {
|
||||||
return accountTransactionQueries.selectAllTransactions(::mapTransaction).executeAsList()
|
return accountTransactionQueries.getAllTransactions(::mapTransaction).executeAsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllTransactionsOfUser(user: UserEntity): List<AccountTransactionEntity> {
|
override fun getAllTransactionsForBank(bank: BankAccessEntity): List<AccountTransactionEntity> {
|
||||||
return accountTransactionQueries.selectAllTransactionsOfUser(user.id, ::mapTransaction).executeAsList()
|
return accountTransactionQueries.getAllTransactionsForBank(bank.id, ::mapTransaction).executeAsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
override fun getTransactionById(transactionId: Long): AccountTransactionEntity? =
|
||||||
|
@ -285,7 +334,7 @@ open class SqliteBankingRepository(
|
||||||
override suspend fun persistTransactions(bankAccount: BankAccountEntity, transactions: List<AccountTransaction>): List<AccountTransactionEntity> {
|
override suspend fun persistTransactions(bankAccount: BankAccountEntity, transactions: List<AccountTransaction>): List<AccountTransactionEntity> {
|
||||||
return accountTransactionQueries.transactionWithResult {
|
return accountTransactionQueries.transactionWithResult {
|
||||||
transactions.map { transaction ->
|
transactions.map { transaction ->
|
||||||
persistTransaction(bankAccount.userId, bankAccount.id, transaction)
|
persistTransaction(bankAccount.bankId, bankAccount.id, transaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,9 +342,9 @@ open class SqliteBankingRepository(
|
||||||
/**
|
/**
|
||||||
* Has to be executed in a transaction in order that getting persisted AccountTransaction's id works~
|
* Has to be executed in a transaction in order that getting persisted AccountTransaction's id works~
|
||||||
*/
|
*/
|
||||||
protected open suspend fun persistTransaction(userId: Long, bankAccountId: Long, transaction: AccountTransaction): AccountTransactionEntity {
|
protected open suspend fun persistTransaction(bankId: Long, accountId: Long, transaction: AccountTransaction): AccountTransactionEntity {
|
||||||
accountTransactionQueries.insertTransaction(
|
accountTransactionQueries.insertTransaction(
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
|
|
||||||
mapAmount(transaction.amount), transaction.currency, transaction.reference,
|
mapAmount(transaction.amount), transaction.currency, transaction.reference,
|
||||||
mapDate(transaction.bookingDate), mapDate(transaction.valueDate),
|
mapDate(transaction.bookingDate), mapDate(transaction.valueDate),
|
||||||
|
@ -305,7 +354,8 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
mapAmount(transaction.openingBalance), mapAmount(transaction.closingBalance),
|
mapAmount(transaction.openingBalance), mapAmount(transaction.closingBalance),
|
||||||
|
|
||||||
transaction.userSetDisplayName, transaction.category, transaction.notes,
|
transaction.userSetReference, transaction.userSetReference,
|
||||||
|
transaction.category, transaction.notes,
|
||||||
|
|
||||||
transaction.statementNumber?.toLong(), transaction.sheetNumber?.toLong(),
|
transaction.statementNumber?.toLong(), transaction.sheetNumber?.toLong(),
|
||||||
|
|
||||||
|
@ -326,16 +376,16 @@ open class SqliteBankingRepository(
|
||||||
transaction.isReversal
|
transaction.isReversal
|
||||||
)
|
)
|
||||||
|
|
||||||
return AccountTransactionEntity(getLastInsertedId(), userId, bankAccountId, transaction)
|
return AccountTransactionEntity(getLastInsertedId(), bankId, accountId, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun getLastInsertedId(): Long =
|
private fun getLastInsertedId(): Long =
|
||||||
userQueries.getLastInsertedId().executeAsOne()
|
bankQueries.getLastInsertedId().executeAsOne()
|
||||||
|
|
||||||
|
|
||||||
private fun mapTransaction(
|
private fun mapTransaction(
|
||||||
id: Long, userId: Long, bankAccountId: Long,
|
id: Long, bankId: Long, accountId: Long,
|
||||||
|
|
||||||
amount: String, currency: String, reference: String?,
|
amount: String, currency: String, reference: String?,
|
||||||
bookingDate: String, valueDate: String,
|
bookingDate: String, valueDate: String,
|
||||||
|
@ -345,7 +395,8 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
openingBalance: String?, closingBalance: String?,
|
openingBalance: String?, closingBalance: String?,
|
||||||
|
|
||||||
userSetDisplayName: String?, category: String?, notes: String?,
|
userSetDisplayName: String?, userSetReference: String?,
|
||||||
|
category: String?, notes: String?,
|
||||||
|
|
||||||
statementNumber: Long?, sheetNumber: Long?,
|
statementNumber: Long?, sheetNumber: Long?,
|
||||||
|
|
||||||
|
@ -366,7 +417,7 @@ open class SqliteBankingRepository(
|
||||||
isReversal: Boolean
|
isReversal: Boolean
|
||||||
): AccountTransactionEntity = AccountTransactionEntity(
|
): AccountTransactionEntity = AccountTransactionEntity(
|
||||||
id,
|
id,
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
|
|
||||||
Amount(amount), currency, reference,
|
Amount(amount), currency, reference,
|
||||||
mapToDate(bookingDate), mapToDate(valueDate),
|
mapToDate(bookingDate), mapToDate(valueDate),
|
||||||
|
@ -375,7 +426,8 @@ open class SqliteBankingRepository(
|
||||||
|
|
||||||
mapToAmount(openingBalance), mapToAmount(closingBalance),
|
mapToAmount(openingBalance), mapToAmount(closingBalance),
|
||||||
|
|
||||||
userSetDisplayName, category, notes,
|
userSetDisplayName, userSetReference,
|
||||||
|
category, notes,
|
||||||
|
|
||||||
statementNumber?.toInt(), sheetNumber?.toInt(),
|
statementNumber?.toInt(), sheetNumber?.toInt(),
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import net.codinux.banking.client.model.Amount
|
||||||
|
|
||||||
class AccountTransactionEntity(
|
class AccountTransactionEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
val bankAccountId: Long,
|
val accountId: Long,
|
||||||
|
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
currency: String,
|
currency: String,
|
||||||
|
@ -25,7 +25,8 @@ class AccountTransactionEntity(
|
||||||
openingBalance: Amount? = null,
|
openingBalance: Amount? = null,
|
||||||
closingBalance: Amount? = null,
|
closingBalance: Amount? = null,
|
||||||
|
|
||||||
userSetDisplayName: String? = null,
|
userSetReference: String? = null,
|
||||||
|
userSetOtherPartyName: String? = null,
|
||||||
category: String? = null,
|
category: String? = null,
|
||||||
notes: String? = null,
|
notes: String? = null,
|
||||||
|
|
||||||
|
@ -80,10 +81,10 @@ class AccountTransactionEntity(
|
||||||
|
|
||||||
isReversal,
|
isReversal,
|
||||||
|
|
||||||
userSetDisplayName, category, notes
|
userSetReference, userSetOtherPartyName, category, notes
|
||||||
) {
|
) {
|
||||||
constructor(id: Long, userId: Long, bankAccountId: Long, transaction: AccountTransaction) : this(
|
constructor(id: Long, bankId: Long, accountId: Long, transaction: AccountTransaction) : this(
|
||||||
id, userId, bankAccountId,
|
id, bankId, accountId,
|
||||||
transaction.amount, transaction.currency, transaction.reference,
|
transaction.amount, transaction.currency, transaction.reference,
|
||||||
transaction.bookingDate, transaction.valueDate,
|
transaction.bookingDate, transaction.valueDate,
|
||||||
|
|
||||||
|
@ -93,7 +94,8 @@ class AccountTransactionEntity(
|
||||||
|
|
||||||
transaction.openingBalance, transaction.closingBalance,
|
transaction.openingBalance, transaction.closingBalance,
|
||||||
|
|
||||||
transaction.userSetDisplayName, transaction.category, transaction.notes,
|
transaction.userSetReference, transaction.userSetOtherPartyName,
|
||||||
|
transaction.category, transaction.notes,
|
||||||
|
|
||||||
transaction.statementNumber, transaction.sheetNumber,
|
transaction.statementNumber, transaction.sheetNumber,
|
||||||
|
|
||||||
|
@ -116,7 +118,7 @@ class AccountTransactionEntity(
|
||||||
|
|
||||||
|
|
||||||
override val identifier: String by lazy {
|
override val identifier: String by lazy {
|
||||||
"$userId ${super.identifier}"
|
"$bankId ${super.identifier}"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package net.codinux.banking.dataaccess.entities
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.BankAccess
|
||||||
|
import net.codinux.banking.client.model.BankingGroup
|
||||||
|
import net.codinux.banking.client.model.tan.TanMedium
|
||||||
|
|
||||||
|
class BankAccessEntity(
|
||||||
|
val id: Long,
|
||||||
|
|
||||||
|
domesticBankCode: String,
|
||||||
|
loginName: String,
|
||||||
|
password: String?,
|
||||||
|
|
||||||
|
bankName: String,
|
||||||
|
bic: String?,
|
||||||
|
|
||||||
|
customerName: String,
|
||||||
|
userId: String? = null,
|
||||||
|
|
||||||
|
override val accounts: List<BankAccountEntity> = emptyList(),
|
||||||
|
|
||||||
|
selectedTanMethodIdentifier: String? = null,
|
||||||
|
override val tanMethods: MutableList<TanMethodEntity> = mutableListOf(),
|
||||||
|
|
||||||
|
selectedTanMediumIdentifier: String? = null,
|
||||||
|
override val tanMedia: MutableList<TanMediumEntity> = mutableListOf(),
|
||||||
|
|
||||||
|
bankingGroup: BankingGroup? = null,
|
||||||
|
serverAddress: String? = null,
|
||||||
|
countryCode: String = "de",
|
||||||
|
|
||||||
|
userSetDisplayName: String? = null,
|
||||||
|
displayIndex: Int = 0,
|
||||||
|
|
||||||
|
iconUrl: String? = null,
|
||||||
|
wrongCredentialsEntered: Boolean = false
|
||||||
|
) : BankAccess(domesticBankCode, loginName, password, bankName, bic, customerName, userId, accounts, selectedTanMethodIdentifier, tanMethods, selectedTanMediumIdentifier, tanMedia, bankingGroup, serverAddress, countryCode) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.userSetDisplayName = userSetDisplayName
|
||||||
|
this.displayIndex = displayIndex
|
||||||
|
|
||||||
|
this.iconUrl = iconUrl
|
||||||
|
this.wrongCredentialsEntered = wrongCredentialsEntered
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(id: Long, bank: BankAccess, bankAccounts: List<BankAccountEntity>, tanMethods: List<TanMethodEntity>, tanMedia: List<TanMediumEntity>) : this(
|
||||||
|
id,
|
||||||
|
bank.domesticBankCode, bank.loginName, bank.password, bank.bankName, bank.bic, bank.customerName, bank.userId,
|
||||||
|
bankAccounts,
|
||||||
|
bank.selectedTanMethodIdentifier, tanMethods.toMutableList(), bank.selectedTanMediumIdentifier, tanMedia.toMutableList(),
|
||||||
|
bank.bankingGroup, bank.serverAddress, bank.countryCode,
|
||||||
|
bank.userSetDisplayName, bank.displayIndex,
|
||||||
|
bank.iconUrl, bank.wrongCredentialsEntered,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -3,11 +3,11 @@ package net.codinux.banking.dataaccess.entities
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.codinux.banking.client.model.*
|
import net.codinux.banking.client.model.*
|
||||||
import net.codinux.banking.client.model.securitiesaccount.Holding
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
class BankAccountEntity(
|
class BankAccountEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
|
|
||||||
identifier: String,
|
identifier: String,
|
||||||
subAccountNumber: String? = null,
|
subAccountNumber: String? = null,
|
||||||
|
@ -28,9 +28,9 @@ class BankAccountEntity(
|
||||||
lastAccountUpdateTime: Instant? = null,
|
lastAccountUpdateTime: Instant? = null,
|
||||||
retrievedTransactionsFrom: LocalDate? = null,
|
retrievedTransactionsFrom: LocalDate? = null,
|
||||||
|
|
||||||
bookedTransactions: MutableList<AccountTransactionEntity> = mutableListOf(),
|
override val bookedTransactions: MutableList<AccountTransactionEntity> = mutableListOf(),
|
||||||
prebookedTransactions: MutableList<PrebookedAccountTransaction> = mutableListOf(),
|
prebookedTransactions: MutableList<PrebookedAccountTransaction> = mutableListOf(),
|
||||||
override var holdings: List<HoldingEntity> = emptyList(),
|
override val holdings: MutableList<HoldingEntity> = mutableListOf(),
|
||||||
|
|
||||||
userSetDisplayName: String? = null,
|
userSetDisplayName: String? = null,
|
||||||
displayIndex: Int = 0,
|
displayIndex: Int = 0,
|
||||||
|
@ -48,14 +48,14 @@ class BankAccountEntity(
|
||||||
|
|
||||||
serverTransactionsRetentionDays, lastAccountUpdateTime, retrievedTransactionsFrom,
|
serverTransactionsRetentionDays, lastAccountUpdateTime, retrievedTransactionsFrom,
|
||||||
|
|
||||||
bookedTransactions as MutableList<AccountTransaction>, prebookedTransactions,
|
bookedTransactions, prebookedTransactions,
|
||||||
holdings,
|
holdings,
|
||||||
|
|
||||||
userSetDisplayName, displayIndex,
|
userSetDisplayName, displayIndex,
|
||||||
hideAccount, includeInAutomaticAccountsUpdate
|
hideAccount, includeInAutomaticAccountsUpdate
|
||||||
) {
|
) {
|
||||||
constructor(id: Long, userId: Long, account: BankAccount, transactions: List<AccountTransactionEntity> = emptyList(), holdings: List<HoldingEntity> = emptyList()) : this(
|
constructor(id: Long, bankId: Long, account: BankAccount, transactions: List<AccountTransactionEntity> = emptyList(), holdings: List<HoldingEntity> = emptyList()) : this(
|
||||||
id, userId,
|
id, bankId,
|
||||||
account.identifier, account.subAccountNumber, account.iban, account.productName,
|
account.identifier, account.subAccountNumber, account.iban, account.productName,
|
||||||
|
|
||||||
account.accountHolderName, account.type,
|
account.accountHolderName, account.type,
|
||||||
|
@ -68,12 +68,16 @@ class BankAccountEntity(
|
||||||
account.serverTransactionsRetentionDays,
|
account.serverTransactionsRetentionDays,
|
||||||
account.lastAccountUpdateTime, account.retrievedTransactionsFrom,
|
account.lastAccountUpdateTime, account.retrievedTransactionsFrom,
|
||||||
|
|
||||||
transactions.toMutableList(), mutableListOf(), holdings,
|
transactions.toMutableList(), mutableListOf(), holdings.toMutableList(),
|
||||||
|
|
||||||
account.userSetDisplayName, account.displayIndex,
|
account.userSetDisplayName, account.displayIndex,
|
||||||
account.hideAccount, account.includeInAutomaticAccountsUpdate
|
account.hideAccount, account.includeInAutomaticAccountsUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
val bookedTransactionsEntities: MutableList<AccountTransactionEntity> = bookedTransactions
|
|
||||||
|
@JvmName("addHoldingsEntities")
|
||||||
|
fun addHoldings(holdings: List<HoldingEntity>) {
|
||||||
|
this.holdings.addAll(holdings)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,8 +7,8 @@ import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
|
|
||||||
class HoldingEntity(
|
class HoldingEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
val bankAccountId: Long,
|
val accountId: Long,
|
||||||
|
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ class HoldingEntity(
|
||||||
buyingDate: LocalDate? = null
|
buyingDate: LocalDate? = null
|
||||||
) : Holding(name, isin, wkn, quantity, currency, totalBalance, marketValue, performancePercentage, totalCostPrice, averageCostPrice, pricingTime, buyingDate) {
|
) : Holding(name, isin, wkn, quantity, currency, totalBalance, marketValue, performancePercentage, totalCostPrice, averageCostPrice, pricingTime, buyingDate) {
|
||||||
|
|
||||||
constructor(id: Long, userId: Long, bankAccountId: Long, holding: Holding) : this(
|
constructor(id: Long, bankId: Long, accountId: Long, holding: Holding) : this(
|
||||||
id, userId, bankAccountId,
|
id, bankId, accountId,
|
||||||
|
|
||||||
holding.name, holding.isin, holding.wkn,
|
holding.name, holding.isin, holding.wkn,
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,19 @@ import net.codinux.banking.client.model.tan.*
|
||||||
|
|
||||||
class TanMediumEntity(
|
class TanMediumEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
|
|
||||||
type: TanMediumType,
|
type: TanMediumType,
|
||||||
mediumName: String?,
|
mediumName: String?,
|
||||||
status: TanMediumStatus,
|
status: TanMediumStatus,
|
||||||
|
|
||||||
tanGenerator: TanGeneratorTanMedium? = null,
|
tanGenerator: TanGeneratorTanMedium? = null,
|
||||||
mobilePhone: MobilePhoneTanMedium? = null
|
mobilePhone: MobilePhoneTanMedium? = null,
|
||||||
) : TanMedium(type, mediumName, status, tanGenerator, mobilePhone) {
|
|
||||||
|
|
||||||
constructor(id: Long, userId: Long, medium: TanMedium)
|
userSetDisplayName: String? = null
|
||||||
: this(id, userId, medium.type, medium.mediumName, medium.status, medium.tanGenerator, medium.mobilePhone)
|
) : TanMedium(type, mediumName, status, tanGenerator, mobilePhone, userSetDisplayName) {
|
||||||
|
|
||||||
|
constructor(id: Long, bankId: Long, medium: TanMedium)
|
||||||
|
: this(id, bankId, medium.type, medium.mediumName, medium.status, medium.tanGenerator, medium.mobilePhone, medium.userSetDisplayName)
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,16 +6,18 @@ import net.codinux.banking.client.model.tan.TanMethodType
|
||||||
|
|
||||||
class TanMethodEntity(
|
class TanMethodEntity(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
|
|
||||||
displayName: String,
|
displayName: String,
|
||||||
type: TanMethodType,
|
type: TanMethodType,
|
||||||
identifier: String,
|
identifier: String,
|
||||||
maxTanInputLength: Int? = null,
|
maxTanInputLength: Int? = null,
|
||||||
allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
|
allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
|
||||||
) : TanMethod(displayName, type, identifier, maxTanInputLength, allowedTanFormat) {
|
|
||||||
|
|
||||||
constructor(id: Long, userId: Long, tanMethod: TanMethod)
|
userSetDisplayName: String? = null,
|
||||||
: this(id, userId, tanMethod.displayName, tanMethod.type, tanMethod.identifier, tanMethod.maxTanInputLength, tanMethod.allowedTanFormat)
|
) : TanMethod(displayName, type, identifier, maxTanInputLength, allowedTanFormat, userSetDisplayName) {
|
||||||
|
|
||||||
|
constructor(id: Long, bankId: Long, tanMethod: TanMethod)
|
||||||
|
: this(id, bankId, tanMethod.displayName, tanMethod.type, tanMethod.identifier, tanMethod.maxTanInputLength, tanMethod.allowedTanFormat, tanMethod.userSetDisplayName)
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
package net.codinux.banking.dataaccess.entities
|
|
||||||
|
|
||||||
import net.codinux.banking.client.model.BankingGroup
|
|
||||||
import net.codinux.banking.client.model.User
|
|
||||||
import net.codinux.banking.client.model.tan.TanMedium
|
|
||||||
import net.codinux.banking.client.model.tan.TanMethod
|
|
||||||
|
|
||||||
class UserEntity(
|
|
||||||
val id: Long,
|
|
||||||
|
|
||||||
bankCode: String,
|
|
||||||
loginName: String,
|
|
||||||
password: String?,
|
|
||||||
|
|
||||||
bankName: String,
|
|
||||||
bic: String,
|
|
||||||
|
|
||||||
customerName: String,
|
|
||||||
userId: String? = null,
|
|
||||||
|
|
||||||
override val accounts: List<BankAccountEntity> = emptyList(),
|
|
||||||
|
|
||||||
selectedTanMethodIdentifier: String? = null,
|
|
||||||
override val tanMethods: List<TanMethodEntity> = listOf(),
|
|
||||||
|
|
||||||
selectedTanMediumIdentifier: String? = null,
|
|
||||||
tanMedia: List<TanMedium> = listOf(),
|
|
||||||
|
|
||||||
bankingGroup: BankingGroup? = null,
|
|
||||||
serverAddress: String? = null,
|
|
||||||
|
|
||||||
userSetDisplayName: String? = null,
|
|
||||||
displayIndex: Int = 0,
|
|
||||||
|
|
||||||
iconUrl: String? = null,
|
|
||||||
wrongCredentialsEntered: Boolean = false
|
|
||||||
) : User(bankCode, loginName, password, bankName, bic, customerName, userId, accounts, selectedTanMethodIdentifier, tanMethods, selectedTanMediumIdentifier, tanMedia, bankingGroup, serverAddress) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.userSetDisplayName = userSetDisplayName
|
|
||||||
this.displayIndex = displayIndex
|
|
||||||
|
|
||||||
this.iconUrl = iconUrl
|
|
||||||
this.wrongCredentialsEntered = wrongCredentialsEntered
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
constructor(id: Long, user: User, bankAccounts: List<BankAccountEntity>, tanMethods: List<TanMethodEntity>, tanMedia: List<TanMediumEntity>) : this(
|
|
||||||
id,
|
|
||||||
user.bankCode, user.loginName, user.password, user.bankName, user.bic, user.customerName, user.userId,
|
|
||||||
bankAccounts,
|
|
||||||
user.selectedTanMethodIdentifier, tanMethods, user.selectedTanMediumIdentifier, tanMedia,
|
|
||||||
user.bankingGroup, user.serverAddress,
|
|
||||||
user.userSetDisplayName, user.displayIndex,
|
|
||||||
user.iconUrl, user.wrongCredentialsEntered,
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
|
@ -29,7 +29,7 @@ private val IconWidth = 48.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomBar(showMenuDrawer: Boolean = true) {
|
fun BottomBar(showMenuDrawer: Boolean = true) {
|
||||||
val users by uiState.users.collectAsState()
|
val banks by uiState.banks.collectAsState()
|
||||||
|
|
||||||
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ fun BottomBar(showMenuDrawer: Boolean = true) {
|
||||||
} else if (selectedAccount.bankAccount != null) {
|
} else if (selectedAccount.bankAccount != null) {
|
||||||
selectedAccount.bankAccount.displayName
|
selectedAccount.bankAccount.displayName
|
||||||
} else {
|
} else {
|
||||||
selectedAccount.user.displayName
|
selectedAccount.bank.displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(title, color = color, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text(title, color = color, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
|
@ -122,7 +122,7 @@ fun BottomBar(showMenuDrawer: Boolean = true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (users.isNotEmpty()) {
|
if (banks.isNotEmpty()) {
|
||||||
if (showSearchbar == false) {
|
if (showSearchbar == false) {
|
||||||
Row(Modifier.fillMaxHeight().widthIn(IconWidth, IconWidth), verticalAlignment = Alignment.CenterVertically) {
|
Row(Modifier.fillMaxHeight().widthIn(IconWidth, IconWidth), verticalAlignment = Alignment.CenterVertically) {
|
||||||
IconButton({ showSearchbar = true }, Modifier.width(IconWidth)) {
|
IconButton({ showSearchbar = true }, Modifier.width(IconWidth)) {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package net.codinux.banking.ui.appskeleton
|
||||||
|
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.Divider
|
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
@ -55,7 +54,7 @@ private val VerticalSpacing = 8.dp
|
||||||
fun SideMenuContent() {
|
fun SideMenuContent() {
|
||||||
val drawerState = uiState.drawerState.collectAsState().value
|
val drawerState = uiState.drawerState.collectAsState().value
|
||||||
|
|
||||||
val accounts = uiState.users.collectAsState().value
|
val accounts = uiState.banks.collectAsState().value
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
import net.codinux.banking.client.model.BankViewInfo
|
import net.codinux.banking.client.model.BankViewInfo
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.model.BankInfo
|
import net.codinux.banking.ui.model.BankInfo
|
||||||
|
@ -21,8 +21,8 @@ private val bankIconService = DI.bankIconService
|
||||||
private val DefaultIconModifier: Modifier = Modifier.size(16.dp)
|
private val DefaultIconModifier: Modifier = Modifier.size(16.dp)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankIcon(user: User?, modifier: Modifier = Modifier, iconModifier: Modifier = DefaultIconModifier, fallbackIcon: ImageVector? = null, fallbackIconTintColor: Color? = null) {
|
fun BankIcon(bank: BankAccess?, modifier: Modifier = Modifier, iconModifier: Modifier = DefaultIconModifier, fallbackIcon: ImageVector? = null, fallbackIconTintColor: Color? = null) {
|
||||||
val iconUrl by remember(user?.bic) { mutableStateOf(user?.let { bankIconService.findIconForBank(it) }) }
|
val iconUrl by remember(bank?.bic) { mutableStateOf(bank?.let { bankIconService.findIconForBank(it) }) }
|
||||||
|
|
||||||
BankIcon(iconUrl, modifier, iconModifier, fallbackIcon = fallbackIcon, fallbackIconTintColor = fallbackIconTintColor)
|
BankIcon(iconUrl, modifier, iconModifier, fallbackIcon = fallbackIcon, fallbackIconTintColor = fallbackIconTintColor)
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ fun BankIcon(bank: BankInfo, modifier: Modifier = Modifier, iconModifier: Modifi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BankIcon(user: BankViewInfo?, modifier: Modifier = Modifier, iconModifier: Modifier = DefaultIconModifier, fallbackIcon: ImageVector? = null) {
|
fun BankIcon(bank: BankViewInfo?, modifier: Modifier = Modifier, iconModifier: Modifier = DefaultIconModifier, fallbackIcon: ImageVector? = null) {
|
||||||
val iconUrl = user?.let { bankIconService.findIconForBank(it.bankName, null, it.bankingGroup) }
|
val iconUrl = bank?.let { bankIconService.findIconForBank(it.bankName, null, it.bankingGroup) }
|
||||||
|
|
||||||
BankIcon(iconUrl, modifier, iconModifier, fallbackIcon = fallbackIcon)
|
BankIcon(iconUrl, modifier, iconModifier, fallbackIcon = fallbackIcon)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
private val uiState = DI.uiState
|
private val uiState = DI.uiState
|
||||||
|
@ -27,9 +27,9 @@ fun BanksList(
|
||||||
textColor: Color = Color.White,
|
textColor: Color = Color.White,
|
||||||
itemModifier: Modifier = Modifier.height(48.dp).widthIn(min = 300.dp),
|
itemModifier: Modifier = Modifier.height(48.dp).widthIn(min = 300.dp),
|
||||||
itemHorizontalPadding: Dp = 8.dp,
|
itemHorizontalPadding: Dp = 8.dp,
|
||||||
accountSelected: ((UserEntity?, BankAccountEntity?) -> Unit)? = null
|
accountSelected: ((BankAccessEntity?, BankAccountEntity?) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val users = uiState.users.collectAsState()
|
val banks = uiState.banks.collectAsState()
|
||||||
|
|
||||||
|
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
|
@ -37,16 +37,16 @@ fun BanksList(
|
||||||
accountSelected?.invoke(null, null)
|
accountSelected?.invoke(null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
users.value.sortedBy { it.displayIndex }.forEach { user ->
|
banks.value.sortedBy { it.displayIndex }.forEach { bank ->
|
||||||
Spacer(Modifier.fillMaxWidth().height(12.dp))
|
Spacer(Modifier.fillMaxWidth().height(12.dp))
|
||||||
|
|
||||||
NavigationMenuItem(itemModifier, user.displayName, textColor, iconSize, IconTextSpacing, itemHorizontalPadding, user, fallbackIcon = defaultBankIcon) {
|
NavigationMenuItem(itemModifier, bank.displayName, textColor, iconSize, IconTextSpacing, itemHorizontalPadding, bank, fallbackIcon = defaultBankIcon) {
|
||||||
accountSelected?.invoke(user, null)
|
accountSelected?.invoke(bank, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.accounts.sortedBy { it.displayIndex }.forEach { account ->
|
bank.accounts.sortedBy { it.displayIndex }.forEach { account ->
|
||||||
NavigationMenuItem(itemModifier, account.displayName, textColor, iconSize, IconTextSpacing, itemHorizontalPadding, bankAccount = account) {
|
NavigationMenuItem(itemModifier, account.displayName, textColor, iconSize, IconTextSpacing, itemHorizontalPadding, bankAccount = account) {
|
||||||
accountSelected?.invoke(user, account)
|
accountSelected?.invoke(bank, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.ContentAlpha
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
@ -19,7 +20,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ fun NavigationMenuItem(
|
||||||
iconSize: Dp = 24.dp,
|
iconSize: Dp = 24.dp,
|
||||||
iconTextSpacing: Dp = 24.dp,
|
iconTextSpacing: Dp = 24.dp,
|
||||||
horizontalPadding: Dp = 8.dp,
|
horizontalPadding: Dp = 8.dp,
|
||||||
user: UserEntity? = null,
|
bank: BankAccessEntity? = null,
|
||||||
bankAccount: BankAccountEntity? = null,
|
bankAccount: BankAccountEntity? = null,
|
||||||
fallbackIcon: ImageVector? = null,
|
fallbackIcon: ImageVector? = null,
|
||||||
icon: (@Composable () -> Unit)? = null,
|
icon: (@Composable () -> Unit)? = null,
|
||||||
|
@ -49,12 +50,26 @@ fun NavigationMenuItem(
|
||||||
|
|
||||||
val showColoredAmounts by DI.uiSettings.showColoredAmounts.collectAsState()
|
val showColoredAmounts by DI.uiSettings.showColoredAmounts.collectAsState()
|
||||||
|
|
||||||
|
val isUnsupportedAccountType = bankAccount?.isAccountTypeSupportedByApplication == false
|
||||||
|
|
||||||
|
val effectiveText = if (isUnsupportedAccountType) "$text (nicht unterstützt)" else text
|
||||||
|
|
||||||
|
val effectiveTextColor = if (isUnsupportedAccountType) textColor.copy(ContentAlpha.disabled) else textColor
|
||||||
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = modifier
|
modifier = modifier.let {
|
||||||
.clickable { onClick?.invoke() }
|
if (isUnsupportedAccountType == false) {
|
||||||
|
it.clickable {
|
||||||
|
onClick?.invoke()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
.let {
|
.let {
|
||||||
if (user != null && filterService.isSelected(user, transactionsFilter)
|
if (bank != null && filterService.isSelected(bank, transactionsFilter)
|
||||||
|| bankAccount != null && filterService.isSelected(bankAccount, transactionsFilter)) {
|
|| bankAccount != null && filterService.isSelected(bankAccount, transactionsFilter)) {
|
||||||
it.background(Colors.AccentAsSelectionBackground, shape = RoundedCornerShape(4.dp))
|
it.background(Colors.AccentAsSelectionBackground, shape = RoundedCornerShape(4.dp))
|
||||||
} else {
|
} else {
|
||||||
|
@ -68,17 +83,17 @@ fun NavigationMenuItem(
|
||||||
icon()
|
icon()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BankIcon(user, Modifier.padding(end = iconTextSpacing), Modifier.size(iconSize), fallbackIcon = fallbackIcon, fallbackIconTintColor = textColor)
|
BankIcon(bank, Modifier.padding(end = iconTextSpacing), Modifier.size(iconSize), fallbackIcon = fallbackIcon, fallbackIconTintColor = effectiveTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(text, color = textColor, modifier = Modifier.weight(1f), maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text(effectiveText, color = effectiveTextColor, modifier = Modifier.weight(1f), maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
|
|
||||||
val balance = if (showBalance == false) {
|
val balance = if (showBalance == false || isUnsupportedAccountType) {
|
||||||
null
|
null
|
||||||
} else if (bankAccount != null) {
|
} else if (bankAccount != null) {
|
||||||
bankAccount.balance
|
bankAccount.balance
|
||||||
} else if (user != null) {
|
} else if (bank != null) {
|
||||||
calculator.calculateBalanceOfUser(user)
|
calculator.calculateBalanceOfBankAccess(bank)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = "$messagePrefix für ${event.user.displayName} ${event.account.displayName}",
|
message = "$messagePrefix für ${event.bank.displayName} ${event.account.displayName}",
|
||||||
actionLabel = actionLabel,
|
actionLabel = actionLabel,
|
||||||
duration = SnackbarDuration.Long
|
duration = SnackbarDuration.Long
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,7 +23,7 @@ fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {
|
||||||
|
|
||||||
val transactionsGrouping by uiSettings.transactionsGrouping.collectAsState()
|
val transactionsGrouping by uiSettings.transactionsGrouping.collectAsState()
|
||||||
|
|
||||||
val zebraStripes by uiSettings.zebraStripes.collectAsState()
|
val showTransactionsInAlternatingColors by uiSettings.showTransactionsInAlternatingColors.collectAsState()
|
||||||
|
|
||||||
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ fun UiSettings(modifier: Modifier, textColor: Color = Color.Unspecified) {
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
BooleanOption("Kontostand anzeigen", showBalance, textColor = textColor) { uiSettings.showBalance.value = it }
|
BooleanOption("Kontostand anzeigen", showBalance, textColor = textColor) { uiSettings.showBalance.value = it }
|
||||||
|
|
||||||
BooleanOption("Zebra Stripes", zebraStripes, textColor = textColor) { uiSettings.zebraStripes.value = it }
|
BooleanOption("Umsätze in alternierenden Farben anzeigen", showTransactionsInAlternatingColors, textColor = textColor) { uiSettings.showTransactionsInAlternatingColors.value = it }
|
||||||
|
|
||||||
BooleanOption("Bank Icons anzeigen", showBankIcons, textColor = textColor) { uiSettings.showBankIcons.value = it }
|
BooleanOption("Bank Icons anzeigen", showBankIcons, textColor = textColor) { uiSettings.showBankIcons.value = it }
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.Amount
|
||||||
import net.codinux.banking.client.model.securitiesaccount.Holding
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
|
import net.codinux.banking.ui.config.Style
|
||||||
import net.codinux.banking.ui.forms.RoundedCornersCard
|
import net.codinux.banking.ui.forms.RoundedCornersCard
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
import net.codinux.banking.ui.model.TransactionsGrouping
|
import net.codinux.banking.ui.model.TransactionsGrouping
|
||||||
|
@ -31,7 +32,7 @@ fun GroupedTransactionsListItems(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
transactionsToDisplay: List<AccountTransactionViewModel>,
|
transactionsToDisplay: List<AccountTransactionViewModel>,
|
||||||
holdingsToDisplay: List<Holding>,
|
holdingsToDisplay: List<Holding>,
|
||||||
usersById: Map<Long, UserEntity>,
|
banksById: Map<Long, BankAccessEntity>,
|
||||||
transactionsGrouping: TransactionsGrouping
|
transactionsGrouping: TransactionsGrouping
|
||||||
) {
|
) {
|
||||||
val groupingService = remember { TransactionsGroupingService() }
|
val groupingService = remember { TransactionsGroupingService() }
|
||||||
|
@ -49,6 +50,7 @@ fun GroupedTransactionsListItems(
|
||||||
Column(Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 16.dp)) {
|
Column(Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 16.dp)) {
|
||||||
Text(
|
Text(
|
||||||
text = "Depotwerte",
|
text = "Depotwerte",
|
||||||
|
color = Style.ListItemHeaderTextColor,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
modifier = Modifier.padding(bottom = 2.dp),
|
modifier = Modifier.padding(bottom = 2.dp),
|
||||||
|
@ -77,6 +79,7 @@ fun GroupedTransactionsListItems(
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Text(
|
Text(
|
||||||
text = DI.formatUtil.formatGroupingDate(groupingDate, transactionsGrouping),
|
text = DI.formatUtil.formatGroupingDate(groupingDate, transactionsGrouping),
|
||||||
|
color = Style.ListItemHeaderTextColor,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
modifier = Modifier.padding(top = 8.dp, bottom = 2.dp),
|
modifier = Modifier.padding(top = 8.dp, bottom = 2.dp),
|
||||||
|
@ -90,7 +93,7 @@ fun GroupedTransactionsListItems(
|
||||||
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
|
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
|
||||||
monthTransactions.forEachIndexed { index, transaction ->
|
monthTransactions.forEachIndexed { index, transaction ->
|
||||||
key(transaction.id) {
|
key(transaction.id) {
|
||||||
TransactionListItem(usersById[transaction.userId], transaction, index, monthTransactions.size)
|
TransactionListItem(banksById[transaction.bankId], transaction, index, monthTransactions.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
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.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.Amount
|
||||||
|
@ -32,11 +31,11 @@ fun HoldingListItem(holding: Holding, isOddItem: Boolean = false, isNotLastItem:
|
||||||
// TODO: also regard showBalance?
|
// TODO: also regard showBalance?
|
||||||
val showColoredAmounts by uiSettings.showColoredAmounts.collectAsState()
|
val showColoredAmounts by uiSettings.showColoredAmounts.collectAsState()
|
||||||
|
|
||||||
val zebraStripes by uiSettings.zebraStripes.collectAsState()
|
val showTransactionsInAlternatingColors by uiSettings.showTransactionsInAlternatingColors.collectAsState()
|
||||||
|
|
||||||
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
||||||
|
|
||||||
val backgroundColor = if (zebraStripes && isOddItem) Colors.ZebraStripesColor else Color.White
|
val backgroundColor = if (showTransactionsInAlternatingColors && isOddItem) Colors.ZebraStripesColor else Color.White
|
||||||
|
|
||||||
val currency = holding.currency ?: fallbackCurrency
|
val currency = holding.currency ?: fallbackCurrency
|
||||||
|
|
||||||
|
@ -49,6 +48,7 @@ fun HoldingListItem(holding: Holding, isOddItem: Boolean = false, isNotLastItem:
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
holding.name,
|
holding.name,
|
||||||
|
color = Style.ListItemHeaderTextColor,
|
||||||
fontWeight = Style.ListItemHeaderWeight,
|
fontWeight = Style.ListItemHeaderWeight,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
|
|
@ -9,12 +9,11 @@ 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.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.DpOffset
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
import net.codinux.banking.ui.composables.BankIcon
|
import net.codinux.banking.ui.composables.BankIcon
|
||||||
import net.codinux.banking.ui.composables.text.ItemDivider
|
import net.codinux.banking.ui.composables.text.ItemDivider
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
|
@ -28,14 +27,14 @@ private val uiSettings = DI.uiSettings
|
||||||
private val formatUtil = DI.formatUtil
|
private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionListItem(user: User?, transaction: AccountTransactionViewModel, itemIndex: Int, countItems: Int) {
|
fun TransactionListItem(bank: BankAccess?, transaction: AccountTransactionViewModel, itemIndex: Int, countItems: Int) {
|
||||||
val zebraStripes by uiSettings.zebraStripes.collectAsState()
|
val showTransactionsInAlternatingColors by uiSettings.showTransactionsInAlternatingColors.collectAsState()
|
||||||
|
|
||||||
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
val showBankIcons by uiSettings.showBankIcons.collectAsState()
|
||||||
|
|
||||||
val showColoredAmounts by uiSettings.showColoredAmounts.collectAsState()
|
val showColoredAmounts by uiSettings.showColoredAmounts.collectAsState()
|
||||||
|
|
||||||
val backgroundColor = if (zebraStripes && itemIndex % 2 == 1) Colors.ZebraStripesColor else Color.White
|
val backgroundColor = if (showTransactionsInAlternatingColors && itemIndex % 2 == 1) Colors.ZebraStripesColor else Color.White
|
||||||
|
|
||||||
val bottomPadding = 56.dp
|
val bottomPadding = 56.dp
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ fun TransactionListItem(user: User?, transaction: AccountTransactionViewModel, i
|
||||||
val transactionEntity = DI.bankingService.getTransaction(transaction.id)
|
val transactionEntity = DI.bankingService.getTransaction(transaction.id)
|
||||||
|
|
||||||
DI.uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData(
|
DI.uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData(
|
||||||
DI.uiState.users.value.firstNotNullOf { it.accounts.firstOrNull { it.id == transaction.bankAccountId } },
|
DI.uiState.banks.value.firstNotNullOf { it.accounts.firstOrNull { it.id == transaction.accountId } },
|
||||||
transaction.otherPartyName,
|
transaction.otherPartyName,
|
||||||
transactionEntity?.otherPartyBankId,
|
transactionEntity?.otherPartyBankId,
|
||||||
transactionEntity?.otherPartyAccountId,
|
transactionEntity?.otherPartyAccountId,
|
||||||
|
@ -79,12 +78,13 @@ fun TransactionListItem(user: User?, transaction: AccountTransactionViewModel, i
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
Row {
|
Row {
|
||||||
if (showBankIcons) {
|
if (showBankIcons) {
|
||||||
BankIcon(user, Modifier.padding(end = 6.dp))
|
BankIcon(bank, Modifier.padding(end = 6.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = transaction.otherPartyName ?: transaction.postingText ?: "",
|
text = transaction.otherPartyName ?: transaction.postingText ?: "",
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
|
color = Style.ListItemHeaderTextColor,
|
||||||
fontWeight = Style.ListItemHeaderWeight,
|
fontWeight = Style.ListItemHeaderWeight,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|
|
@ -25,9 +25,9 @@ private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean = true) {
|
fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean = true) {
|
||||||
val users by uiState.users.collectAsState()
|
val banks by uiState.banks.collectAsState()
|
||||||
val usersById by remember(users) {
|
val banksById by remember(banks) {
|
||||||
derivedStateOf { users.associateBy { it.id } }
|
derivedStateOf { banks.associateBy { it.id } }
|
||||||
}
|
}
|
||||||
|
|
||||||
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
val transactionsFilter by uiState.transactionsFilter.collectAsState()
|
||||||
|
@ -59,13 +59,13 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
if (showBalance) {
|
if (showBalance) {
|
||||||
val balance = calculator.calculateBalanceOfDisplayedTransactions(transactionsToDisplay, users, transactionsFilter)
|
val balance = calculator.calculateBalanceOfDisplayedTransactions(transactionsToDisplay, banks, transactionsFilter)
|
||||||
Text(formatUtil.formatAmount(balance, "EUR"), color = formatUtil.getColorForAmount(balance, showColoredAmounts))
|
Text(formatUtil.formatAmount(balance, "EUR"), color = formatUtil.getColorForAmount(balance, showColoredAmounts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionsGrouping != TransactionsGrouping.None) {
|
if (transactionsGrouping != TransactionsGrouping.None) {
|
||||||
GroupedTransactionsListItems(transactionsListModifier, transactionsToDisplay, holdingsToDisplay, usersById, transactionsGrouping)
|
GroupedTransactionsListItems(transactionsListModifier, transactionsToDisplay, holdingsToDisplay, banksById, transactionsGrouping)
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(transactionsListModifier, contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp)) {
|
LazyColumn(transactionsListModifier, contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp)) {
|
||||||
itemsIndexed(holdingsToDisplay) { index, holding ->
|
itemsIndexed(holdingsToDisplay) { index, holding ->
|
||||||
|
@ -76,7 +76,7 @@ fun TransactionsList(uiState: UiState, uiSettings: UiSettings, isMobile: Boolean
|
||||||
|
|
||||||
itemsIndexed(transactionsToDisplay) { index, transaction ->
|
itemsIndexed(transactionsToDisplay) { index, transaction ->
|
||||||
key(transaction.id) {
|
key(transaction.id) {
|
||||||
TransactionListItem(usersById[transaction.userId], transaction, index, transactionsToDisplay.size)
|
TransactionListItem(banksById[transaction.bankId], transaction, index, transactionsToDisplay.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ object Colors {
|
||||||
|
|
||||||
val Zinc200 = Color(228, 228, 231)
|
val Zinc200 = Color(228, 228, 231)
|
||||||
|
|
||||||
|
val Zinc500 = Color(0xFF71717a)
|
||||||
|
|
||||||
val Zinc700 = Color(63, 63, 70)
|
val Zinc700 = Color(63, 63, 70)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ object DI {
|
||||||
|
|
||||||
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
||||||
|
|
||||||
val bankingService by lazy { BankingService(uiState, bankingRepository, bankFinder) }
|
val bankingService by lazy { BankingService(uiState, uiSettings, bankingRepository, bankFinder) }
|
||||||
|
|
||||||
|
|
||||||
fun setRepository(sqlDriver: SqlDriver) = setRepository(SqliteBankingRepository(sqlDriver))
|
fun setRepository(sqlDriver: SqlDriver) = setRepository(SqliteBankingRepository(sqlDriver))
|
||||||
|
|
|
@ -13,6 +13,9 @@ object Style {
|
||||||
|
|
||||||
val HeaderFontWeight: FontWeight = FontWeight.Bold
|
val HeaderFontWeight: FontWeight = FontWeight.Bold
|
||||||
|
|
||||||
|
|
||||||
|
val ListItemHeaderTextColor: Color = Colors.Zinc500
|
||||||
|
|
||||||
val ListItemHeaderWeight = FontWeight.Medium // couldn't believe it, the FontWeights look different on Desktop and Android
|
val ListItemHeaderWeight = FontWeight.Medium // couldn't believe it, the FontWeights look different on Desktop and Android
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ fun AddAccountDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(Modifier.fillMaxWidth().padding(top = 6.dp)) {
|
Row(Modifier.fillMaxWidth().padding(top = 6.dp)) {
|
||||||
Text(bank.bankCode, color = textColor)
|
Text(bank.domesticBankCode, color = textColor)
|
||||||
|
|
||||||
Text("${bank.postalCode} ${bank.city}", Modifier.weight(1f).padding(start = 8.dp), color = if (supportsFinTs) Color.Gray else textColor)
|
Text("${bank.postalCode} ${bank.city}", Modifier.weight(1f).padding(start = 8.dp), color = if (supportsFinTs) Color.Gray else textColor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,9 +94,9 @@ fun EnterTanDialog(tanChallengeReceived: TanChallengeReceived, onDismiss: () ->
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Column(Modifier.fillMaxWidth()) {
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Row {
|
Row {
|
||||||
BankIcon(challenge.user, Modifier.padding(end = 6.dp))
|
BankIcon(challenge.bank, Modifier.padding(end = 6.dp))
|
||||||
|
|
||||||
Text("${challenge.user.bankName}, Nutzer ${challenge.user.loginName}${challenge.account?.let { ", Konto ${it.productName ?: it.identifier}" } ?: ""}")
|
Text("${challenge.bank.bankName}, Nutzer ${challenge.bank.loginName}${challenge.account?.let { ", Konto ${it.productName ?: it.identifier}" } ?: ""}")
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
"TAN benötigt ${Internationalization.getTextForActionRequiringTan(challenge.forAction)}",
|
"TAN benötigt ${Internationalization.getTextForActionRequiringTan(challenge.forAction)}",
|
||||||
|
|
|
@ -38,11 +38,11 @@ fun TransferMoneyDialog(
|
||||||
data: ShowTransferMoneyDialogData,
|
data: ShowTransferMoneyDialogData,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val users = uiState.users.value
|
val banks = uiState.banks.value
|
||||||
val accountsToUser = users.sortedBy { it.displayIndex }
|
val accountsToBank = banks.sortedBy { it.displayIndex }
|
||||||
.flatMap { user -> user.accounts.sortedBy { it.displayIndex }.map { it to user } }.toMap()
|
.flatMap { bank -> bank.accounts.sortedBy { it.displayIndex }.map { it to bank } }.toMap()
|
||||||
|
|
||||||
val accountsSupportingTransferringMoney = users.flatMap { it.accounts }
|
val accountsSupportingTransferringMoney = banks.flatMap { it.accounts }
|
||||||
.filter { it.supportsMoneyTransfer }
|
.filter { it.supportsMoneyTransfer }
|
||||||
|
|
||||||
if (accountsSupportingTransferringMoney.isEmpty()) {
|
if (accountsSupportingTransferringMoney.isEmpty()) {
|
||||||
|
@ -98,7 +98,7 @@ fun TransferMoneyDialog(
|
||||||
|
|
||||||
transferMoneyJob = coroutineScope.launch(Dispatchers.IOorDefault) {
|
transferMoneyJob = coroutineScope.launch(Dispatchers.IOorDefault) {
|
||||||
val successful = bankingService.transferMoney(
|
val successful = bankingService.transferMoney(
|
||||||
accountsToUser[senderAccount]!!, senderAccount,
|
accountsToBank[senderAccount]!!, senderAccount,
|
||||||
recipientName, recipientAccountIdentifier,
|
recipientName, recipientAccountIdentifier,
|
||||||
Amount(amount), // TODO: verify entered amount is valid
|
Amount(amount), // TODO: verify entered amount is valid
|
||||||
"EUR", // TODO: add input field for currency
|
"EUR", // TODO: add input field for currency
|
||||||
|
@ -132,13 +132,13 @@ fun TransferMoneyDialog(
|
||||||
Select(
|
Select(
|
||||||
"Konto",
|
"Konto",
|
||||||
accountsSupportingTransferringMoney, senderAccount, { senderAccount = it },
|
accountsSupportingTransferringMoney, senderAccount, { senderAccount = it },
|
||||||
{ account -> "${accountsToUser[account]?.displayName} ${account.displayName}" },
|
{ account -> "${accountsToBank[account]?.displayName} ${account.displayName}" },
|
||||||
leadingIcon = { BankIcon(accountsToUser[senderAccount]) }
|
leadingIcon = { BankIcon(accountsToBank[senderAccount]) }
|
||||||
) { account ->
|
) { account ->
|
||||||
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||||
BankIcon(accountsToUser[account], Modifier.padding(end = 6.dp))
|
BankIcon(accountsToBank[account], Modifier.padding(end = 6.dp))
|
||||||
|
|
||||||
Text("${accountsToUser[account]?.displayName} ${account.displayName}")
|
Text("${accountsToBank[account]?.displayName} ${account.displayName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
|
|
||||||
data class AccountTransactionViewModel(
|
data class AccountTransactionViewModel(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val userId: Long,
|
val bankId: Long,
|
||||||
val bankAccountId: Long,
|
val accountId: Long,
|
||||||
|
|
||||||
val amount: Amount,
|
val amount: Amount,
|
||||||
val currency: String,
|
val currency: String,
|
||||||
|
@ -17,11 +17,11 @@ data class AccountTransactionViewModel(
|
||||||
val otherPartyName: String? = null,
|
val otherPartyName: String? = null,
|
||||||
|
|
||||||
val postingText: String? = null,
|
val postingText: String? = null,
|
||||||
val userSetDisplayName: String? = null,
|
val userSetReference: String? = null,
|
||||||
val category: String? = null
|
val userSetOtherPartyName: String? = null
|
||||||
) {
|
) {
|
||||||
constructor(entity: AccountTransactionEntity) : this(entity.id, entity.userId, entity.bankAccountId, entity)
|
constructor(entity: AccountTransactionEntity) : this(entity.id, entity.bankId, entity.accountId, entity)
|
||||||
|
|
||||||
constructor(id: Long, userId: Long, bankAccountId: Long, transaction: AccountTransaction)
|
constructor(id: Long, bankId: Long, accountId: Long, transaction: AccountTransaction)
|
||||||
: this(id, userId, bankAccountId, transaction.amount, transaction.currency, transaction.reference, transaction.valueDate, transaction.otherPartyName, transaction.postingText)
|
: this(id, bankId, accountId, transaction.amount, transaction.currency, transaction.reference, transaction.valueDate, transaction.otherPartyName, transaction.postingText, transaction.userSetReference, transaction.userSetOtherPartyName)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package net.codinux.banking.ui.model
|
||||||
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
|
|
||||||
class AccountTransactionsFilter {
|
class AccountTransactionsFilter {
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ class AccountTransactionsFilter {
|
||||||
val selectedAccount: BankAccountFilter?
|
val selectedAccount: BankAccountFilter?
|
||||||
get() = selectedAccounts.value.firstOrNull()
|
get() = selectedAccounts.value.firstOrNull()
|
||||||
|
|
||||||
fun selectedAccountChanged(user: UserEntity?, bankAccount: BankAccountEntity?) {
|
fun selectedAccountChanged(user: BankAccessEntity?, bankAccount: BankAccountEntity?) {
|
||||||
selectedAccounts.value = if (user == null) {
|
selectedAccounts.value = if (user == null) {
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package net.codinux.banking.ui.model
|
package net.codinux.banking.ui.model
|
||||||
|
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
|
|
||||||
data class BankAccountFilter(
|
data class BankAccountFilter(
|
||||||
val user: UserEntity,
|
val bank: BankAccessEntity,
|
||||||
val bankAccount: BankAccountEntity? = null
|
val bankAccount: BankAccountEntity? = null
|
||||||
)
|
)
|
|
@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
class BankInfo(
|
class BankInfo(
|
||||||
val name: String,
|
val name: String,
|
||||||
val bankCode: String,
|
val domesticBankCode: String,
|
||||||
val bic: String = "",
|
val bic: String = "",
|
||||||
val postalCode: String,
|
val postalCode: String,
|
||||||
val city: String,
|
val city: String,
|
||||||
|
@ -23,5 +23,5 @@ class BankInfo(
|
||||||
get() = pinTanVersion == "FinTS V3.0"
|
get() = pinTanVersion == "FinTS V3.0"
|
||||||
|
|
||||||
|
|
||||||
override fun toString() = "$bankCode $name $city"
|
override fun toString() = "$domesticBankCode $name $city"
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
package net.codinux.banking.ui.model.events
|
package net.codinux.banking.ui.model.events
|
||||||
|
|
||||||
import net.codinux.banking.client.model.BankAccount
|
import net.codinux.banking.client.model.BankAccount
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
import net.codinux.banking.client.model.securitiesaccount.Holding
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
|
||||||
data class AccountTransactionsRetrievedEvent(
|
data class AccountTransactionsRetrievedEvent(
|
||||||
val user: User,
|
val bank: BankAccess,
|
||||||
val account: BankAccount,
|
val account: BankAccount,
|
||||||
val newTransactions: List<AccountTransactionViewModel>,
|
val newTransactions: List<AccountTransactionViewModel>,
|
||||||
val updatedHoldings: List<Holding> = emptyList()
|
val updatedHoldings: List<Holding> = emptyList()
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.codinux.banking.ui.model.settings
|
||||||
|
|
||||||
|
enum class AppAuthenticationMethod {
|
||||||
|
None,
|
||||||
|
Password,
|
||||||
|
Biometric
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package net.codinux.banking.ui.model.settings
|
||||||
|
|
||||||
|
class AppSettings(
|
||||||
|
var authenticationMethod: AppAuthenticationMethod = AppAuthenticationMethod.None,
|
||||||
|
var hashedPassword: String? = null,
|
||||||
|
|
||||||
|
var updateAccountsOnAppStart: Boolean = false,
|
||||||
|
var updateAccountsIfNoUpdatedForHours: Int = 6
|
||||||
|
) {
|
||||||
|
override fun toString() = "$authenticationMethod"
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package net.codinux.banking.ui.service
|
||||||
|
|
||||||
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
import net.codinux.banking.ui.model.AccountTransactionsFilter
|
import net.codinux.banking.ui.model.AccountTransactionsFilter
|
||||||
import net.codinux.banking.ui.model.BankAccountFilter
|
import net.codinux.banking.ui.model.BankAccountFilter
|
||||||
|
@ -38,9 +38,9 @@ class AccountTransactionsFilterService {
|
||||||
private fun matchesFilter(transaction: AccountTransactionViewModel, accountsFilter: List<BankAccountFilter>): Boolean =
|
private fun matchesFilter(transaction: AccountTransactionViewModel, accountsFilter: List<BankAccountFilter>): Boolean =
|
||||||
accountsFilter.any { (user, bankAccount) ->
|
accountsFilter.any { (user, bankAccount) ->
|
||||||
if (bankAccount != null) {
|
if (bankAccount != null) {
|
||||||
transaction.bankAccountId == bankAccount.id
|
transaction.accountId == bankAccount.id
|
||||||
} else {
|
} else {
|
||||||
transaction.userId == user.id
|
transaction.bankId == user.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +71,9 @@ class AccountTransactionsFilterService {
|
||||||
private fun matchesFilter(holding: HoldingEntity, filter: List<BankAccountFilter>): Boolean =
|
private fun matchesFilter(holding: HoldingEntity, filter: List<BankAccountFilter>): Boolean =
|
||||||
filter.any { (user, bankAccount) ->
|
filter.any { (user, bankAccount) ->
|
||||||
if (bankAccount != null) {
|
if (bankAccount != null) {
|
||||||
holding.bankAccountId == bankAccount.id
|
holding.accountId == bankAccount.id
|
||||||
} else {
|
} else {
|
||||||
holding.userId == user.id
|
holding.bankId == user.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,14 +83,14 @@ class AccountTransactionsFilterService {
|
||||||
|| holding.wkn?.contains(searchTerm, true) == true
|
|| holding.wkn?.contains(searchTerm, true) == true
|
||||||
|
|
||||||
|
|
||||||
fun isSelected(user: UserEntity, transactionsFilter: AccountTransactionsFilter): Boolean {
|
fun isSelected(user: BankAccessEntity, transactionsFilter: AccountTransactionsFilter): Boolean {
|
||||||
if (transactionsFilter.showAllAccounts) {
|
if (transactionsFilter.showAllAccounts) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val filter = transactionsFilter.selectedAccount
|
val filter = transactionsFilter.selectedAccount
|
||||||
|
|
||||||
return filter?.user == user && filter.bankAccount == null
|
return filter?.bank == user && filter.bankAccount == null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSelected(bankAccount: BankAccountEntity, transactionsFilter: AccountTransactionsFilter): Boolean {
|
fun isSelected(bankAccount: BankAccountEntity, transactionsFilter: AccountTransactionsFilter): Boolean {
|
||||||
|
|
|
@ -36,7 +36,7 @@ class BankFinder {
|
||||||
return getBankList(maxItems)
|
return getBankList(maxItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBankList().asSequence().filter { it.bankCode.startsWith(query) }
|
return getBankList().asSequence().filter { it.domesticBankCode.startsWith(query) }
|
||||||
.max(maxItems)
|
.max(maxItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class BankFinder {
|
||||||
|
|
||||||
val bankCode = iban.substring(4) // first two letters are the country code, third and fourth char are the checksum, bank code starts at 5th char
|
val bankCode = iban.substring(4) // first two letters are the country code, third and fourth char are the checksum, bank code starts at 5th char
|
||||||
|
|
||||||
val result = getBankList().asSequence().filter { it.bankCode.startsWith(bankCode) }.max(2)
|
val result = getBankList().asSequence().filter { it.domesticBankCode.startsWith(bankCode) }.max(2)
|
||||||
|
|
||||||
return if (result.size > 1) { // non unique result, but should actually never happen for BICs
|
return if (result.size > 1) { // non unique result, but should actually never happen for BICs
|
||||||
null
|
null
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package net.codinux.banking.ui.service
|
package net.codinux.banking.ui.service
|
||||||
|
|
||||||
import net.codinux.banking.client.model.BankingGroup
|
import net.codinux.banking.client.model.BankingGroup
|
||||||
import net.codinux.banking.client.model.User
|
import net.codinux.banking.client.model.BankAccess
|
||||||
|
|
||||||
class BankIconService { // TODO: extract to a common library
|
class BankIconService { // TODO: extract to a common library
|
||||||
|
|
||||||
fun findIconForBank(user: User) = findIconForBank(user.bankName, user.bic, user.bankingGroup)
|
fun findIconForBank(bank: BankAccess) = findIconForBank(bank.bankName, bank.bic, bank.bankingGroup)
|
||||||
|
|
||||||
fun findIconForBank(bankName: String, bic: String? = null, bankingGroup: BankingGroup? = null): String? = when (bankingGroup) {
|
fun findIconForBank(bankName: String, bic: String? = null, bankingGroup: BankingGroup? = null): String? = when (bankingGroup) {
|
||||||
BankingGroup.Sparkasse -> "https://sparkasse.de/favicon-32x32.png"
|
BankingGroup.Sparkasse -> "https://sparkasse.de/favicon-32x32.png"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package net.codinux.banking.ui.service
|
package net.codinux.banking.ui.service
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import bankmeister.composeapp.generated.resources.Res
|
import bankmeister.composeapp.generated.resources.Res
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import net.codinux.banking.client.SimpleBankingClientCallback
|
import net.codinux.banking.client.SimpleBankingClientCallback
|
||||||
import net.codinux.banking.client.fints4k.FinTs4kBankingClient
|
import net.codinux.banking.client.fints4k.FinTs4kBankingClient
|
||||||
|
@ -22,6 +24,8 @@ import net.codinux.banking.ui.model.BankInfo
|
||||||
import net.codinux.banking.ui.model.error.*
|
import net.codinux.banking.ui.model.error.*
|
||||||
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
||||||
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
|
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
|
||||||
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
import net.codinux.banking.ui.settings.UiSettings
|
||||||
import net.codinux.banking.ui.state.UiState
|
import net.codinux.banking.ui.state.UiState
|
||||||
import net.codinux.csv.reader.CsvReader
|
import net.codinux.csv.reader.CsvReader
|
||||||
import net.codinux.log.logger
|
import net.codinux.log.logger
|
||||||
|
@ -30,6 +34,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi
|
||||||
@OptIn(ExperimentalResourceApi::class)
|
@OptIn(ExperimentalResourceApi::class)
|
||||||
class BankingService(
|
class BankingService(
|
||||||
private val uiState: UiState,
|
private val uiState: UiState,
|
||||||
|
private val uiSettings: UiSettings,
|
||||||
private val bankingRepository: BankingRepository,
|
private val bankingRepository: BankingRepository,
|
||||||
private val bankFinder: BankFinder
|
private val bankFinder: BankFinder
|
||||||
) {
|
) {
|
||||||
|
@ -45,21 +50,40 @@ class BankingService(
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
try {
|
try {
|
||||||
uiState.users.value = getAllUsers()
|
var appSettings = getAppSettings()
|
||||||
|
if (appSettings == null) {
|
||||||
|
appSettings = AppSettings()
|
||||||
|
saveAppSettings(appSettings)
|
||||||
|
}
|
||||||
|
uiState.appSettings.value = appSettings
|
||||||
|
|
||||||
|
bankingRepository.getUiSettings(uiSettings)
|
||||||
|
|
||||||
|
updateOnChanges(uiSettings)
|
||||||
|
|
||||||
|
|
||||||
|
uiState.banks.value = getAllBanks()
|
||||||
|
|
||||||
uiState.transactions.value = getAllAccountTransactionsAsViewModel()
|
uiState.transactions.value = getAllAccountTransactionsAsViewModel()
|
||||||
uiState.holdings.value = uiState.users.value.flatMap { it.accounts }.flatMap { it.holdings }
|
uiState.holdings.value = uiState.banks.value.flatMap { it.accounts }.flatMap { it.holdings }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error(e) { "Could not read all user accounts and account transactions from repository" }
|
log.error(e) { "Could not read all banks and account transactions from repository" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getAllUsers() = bankingRepository.getAllUsers()
|
fun getAppSettings() = bankingRepository.getAppSettings()
|
||||||
|
|
||||||
|
suspend fun saveAppSettings(settings: AppSettings) = bankingRepository.saveAppSettings(settings)
|
||||||
|
|
||||||
|
suspend fun saveUiSettings(settings: UiSettings) = bankingRepository.saveUiSettings(settings)
|
||||||
|
|
||||||
|
|
||||||
|
fun getAllBanks() = bankingRepository.getAllBanks()
|
||||||
|
|
||||||
fun getAllAccountTransactions() = bankingRepository.getAllAccountTransactions()
|
fun getAllAccountTransactions() = bankingRepository.getAllAccountTransactions()
|
||||||
|
|
||||||
fun getAllTransactionsOfUser(user: UserEntity) = bankingRepository.getAllTransactionsOfUser(user)
|
fun getAllTransactionsForBank(bank: BankAccessEntity) = bankingRepository.getAllTransactionsForBank(bank)
|
||||||
|
|
||||||
fun getAllAccountTransactionsAsViewModel() = bankingRepository.getAllAccountTransactionsAsViewModel()
|
fun getAllAccountTransactionsAsViewModel() = bankingRepository.getAllAccountTransactionsAsViewModel()
|
||||||
|
|
||||||
|
@ -72,7 +96,7 @@ class BankingService(
|
||||||
suspend fun addAccount(bank: BankInfo, loginName: String, password: String, retrieveAllTransactions: Boolean = false): Boolean {
|
suspend fun addAccount(bank: BankInfo, loginName: String, password: String, retrieveAllTransactions: Boolean = false): Boolean {
|
||||||
try {
|
try {
|
||||||
val retrieveTransactions = if (retrieveAllTransactions) RetrieveTransactions.All else RetrieveTransactions.OfLast90Days
|
val retrieveTransactions = if (retrieveAllTransactions) RetrieveTransactions.All else RetrieveTransactions.OfLast90Days
|
||||||
val response = client.getAccountDataAsync(GetAccountDataRequest(bank.bankCode, loginName, password, GetAccountDataOptions(retrieveTransactions), mapBankInfo(bank)))
|
val response = client.getAccountDataAsync(GetAccountDataRequest(bank.domesticBankCode, loginName, password, GetAccountDataOptions(retrieveTransactions), mapBankInfo(bank)))
|
||||||
|
|
||||||
if (response.type == ResponseType.Success && response.data != null) {
|
if (response.type == ResponseType.Success && response.data != null) {
|
||||||
handleSuccessfulGetAccountDataResponse(response.data!!)
|
handleSuccessfulGetAccountDataResponse(response.data!!)
|
||||||
|
@ -101,21 +125,21 @@ class BankingService(
|
||||||
|
|
||||||
private suspend fun handleSuccessfulGetAccountDataResponse(response: GetAccountDataResponse) {
|
private suspend fun handleSuccessfulGetAccountDataResponse(response: GetAccountDataResponse) {
|
||||||
try {
|
try {
|
||||||
val newUser = response.user
|
val newBank = response.bank
|
||||||
newUser.displayIndex = uiState.users.value.size
|
newBank.displayIndex = uiState.banks.value.size
|
||||||
|
|
||||||
val newUserEntity = bankingRepository.persistUser(newUser)
|
val newBankEntity = bankingRepository.persistBank(newBank)
|
||||||
|
|
||||||
log.info { "Saved user account $newUserEntity with ${newUserEntity.accounts.flatMap { it.bookedTransactionsEntities }.size} transactions" }
|
log.info { "Saved bank $newBankEntity with ${newBankEntity.accounts.flatMap { it.bookedTransactions }.size} transactions" }
|
||||||
|
|
||||||
val users = uiState.users.value.toMutableList()
|
val banks = uiState.banks.value.toMutableList()
|
||||||
users.add(newUserEntity)
|
banks.add(newBankEntity)
|
||||||
uiState.users.value = users
|
uiState.banks.value = banks
|
||||||
|
|
||||||
updateTransactionsInUi(newUserEntity.accounts.flatMap { it.bookedTransactionsEntities })
|
updateTransactionsInUi(newBankEntity.accounts.flatMap { it.bookedTransactions })
|
||||||
updateHoldingsInUi(newUserEntity.accounts.flatMap { it.holdings }, emptyList())
|
updateHoldingsInUi(newBankEntity.accounts.flatMap { it.holdings }, emptyList())
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error(e) { "Could not save user account ${response.user}" }
|
log.error(e) { "Could not save bank ${response.bank}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,45 +147,45 @@ class BankingService(
|
||||||
suspend fun updateAccountTransactions() {
|
suspend fun updateAccountTransactions() {
|
||||||
val selectedAccount = uiState.transactionsFilter.value.selectedAccount
|
val selectedAccount = uiState.transactionsFilter.value.selectedAccount
|
||||||
if (selectedAccount != null) {
|
if (selectedAccount != null) {
|
||||||
updateAccountTransactions(selectedAccount.user, selectedAccount.bankAccount)
|
updateAccountTransactions(selectedAccount.bank, selectedAccount.bankAccount)
|
||||||
} else {
|
} else {
|
||||||
uiState.users.value.forEach { user ->
|
uiState.banks.value.forEach { bank ->
|
||||||
updateAccountTransactions(user)
|
updateAccountTransactions(bank)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateAccountTransactions(user: UserEntity, bankAccount: BankAccountEntity? = null) {
|
private suspend fun updateAccountTransactions(bank: BankAccessEntity, bankAccount: BankAccountEntity? = null) {
|
||||||
withContext(Dispatchers.IOorDefault) {
|
withContext(Dispatchers.IOorDefault) {
|
||||||
try {
|
try {
|
||||||
val response = client.updateAccountTransactionsAsync(user, bankAccount?.let { listOf(it) })
|
val response = client.updateAccountTransactionsAsync(bank, bankAccount?.let { listOf(it) })
|
||||||
if (response.type == ResponseType.Success && response.data != null) {
|
if (response.type == ResponseType.Success && response.data != null) {
|
||||||
handleSuccessfulUpdateAccountTransactionsResponse(user, response.data!!)
|
handleSuccessfulUpdateAccountTransactionsResponse(bank, response.data!!)
|
||||||
} else {
|
} else {
|
||||||
handleUnsuccessfulBankingClientResponse(BankingClientAction.UpdateAccountTransactions, response)
|
handleUnsuccessfulBankingClientResponse(BankingClientAction.UpdateAccountTransactions, response)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error(e) { "Could not update account transactions for $user" }
|
log.error(e) { "Could not update account transactions for $bank" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleSuccessfulUpdateAccountTransactionsResponse(user: UserEntity, responses: List<GetTransactionsResponse>) {
|
private suspend fun handleSuccessfulUpdateAccountTransactionsResponse(bank: BankAccessEntity, responses: List<GetTransactionsResponse>) {
|
||||||
try {
|
try {
|
||||||
// TODO: when user gets updated by BankingClient, also update user in database
|
// TODO: when bank gets updated by BankingClient, also update bank in database
|
||||||
// val newUser = response.user
|
// val newUser = response.bank
|
||||||
// val newUserEntity = bankingRepository.persistUser(newUser)
|
// val newUserEntity = bankingRepository.persistUser(newUser)
|
||||||
//
|
//
|
||||||
// log.info { "Saved user account $newUserEntity with ${newUserEntity.accounts.flatMap { it.bookedTransactionsEntities }.size} transactions" }
|
// log.info { "Saved user account $newUserEntity with ${newUserEntity.accounts.flatMap { it.bookedTransactionsEntities }.size} transactions" }
|
||||||
|
|
||||||
val userTransactions = getAllTransactionsOfUser(user)
|
val transactionsForBank = getAllTransactionsForBank(bank)
|
||||||
|
|
||||||
responses.forEach { response ->
|
responses.forEach { response ->
|
||||||
val account = (response.account as? BankAccountEntity) ?: user.accounts.first { it.identifier == response.account.identifier && it.subAccountNumber == response.account.subAccountNumber }
|
val account = (response.account as? BankAccountEntity) ?: bank.accounts.first { it.identifier == response.account.identifier && it.subAccountNumber == response.account.subAccountNumber }
|
||||||
|
|
||||||
// TODO: update BankAccount and may updated Transactions in database
|
// TODO: update BankAccount and may updated Transactions in database
|
||||||
|
|
||||||
val existingAccountTransactions = userTransactions.filter { it.bankAccountId == account.id }
|
val existingAccountTransactions = transactionsForBank.filter { it.accountId == account.id }
|
||||||
|
|
||||||
val newTransactions = modelService.findNewTransactions(response.bookedTransactions, existingAccountTransactions)
|
val newTransactions = modelService.findNewTransactions(response.bookedTransactions, existingAccountTransactions)
|
||||||
|
|
||||||
|
@ -181,18 +205,16 @@ class BankingService(
|
||||||
bankingRepository.updateHoldings(updateHoldings(updatedExistingHoldings, updatedRetrievedHoldings))
|
bankingRepository.updateHoldings(updateHoldings(updatedExistingHoldings, updatedRetrievedHoldings))
|
||||||
bankingRepository.deleteHoldings(deletedHoldings)
|
bankingRepository.deleteHoldings(deletedHoldings)
|
||||||
|
|
||||||
account.holdings = account.holdings.toMutableList().apply {
|
account.holdings.removeAll(deletedHoldings)
|
||||||
addAll(persistedNewHoldings)
|
account.addHoldings(persistedNewHoldings)
|
||||||
removeAll(deletedHoldings)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHoldingsInUi(persistedNewHoldings, deletedHoldings)
|
updateHoldingsInUi(persistedNewHoldings, deletedHoldings)
|
||||||
|
|
||||||
val transactionsViewModel = updateTransactionsInUi(newTransactionsEntities)
|
val transactionsViewModel = updateTransactionsInUi(newTransactionsEntities)
|
||||||
uiState.dispatchNewTransactionsRetrieved(AccountTransactionsRetrievedEvent(user, account, transactionsViewModel, response.holdings))
|
uiState.dispatchNewTransactionsRetrieved(AccountTransactionsRetrievedEvent(bank, account, transactionsViewModel, response.holdings))
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error(e) { "Could not save updated account transactions for user $user" }
|
log.error(e) { "Could not save updated account transactions for bank $bank" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,11 +257,11 @@ class BankingService(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun transferMoney(user: UserEntity, account: BankAccountEntity,
|
suspend fun transferMoney(bank: BankAccessEntity, account: BankAccountEntity,
|
||||||
recipientName: String, recipientAccountIdentifier: String, amount: Amount, currency: String,
|
recipientName: String, recipientAccountIdentifier: String, amount: Amount, currency: String,
|
||||||
paymentReference: String? = null, instantTransfer: Boolean = false, recipientBankIdentifier: String? = null): Boolean {
|
paymentReference: String? = null, instantTransfer: Boolean = false, recipientBankIdentifier: String? = null): Boolean {
|
||||||
val response = client.transferMoneyAsync(TransferMoneyRequestForUser(
|
val response = client.transferMoneyAsync(TransferMoneyRequestForUser(
|
||||||
user.bankCode, user.loginName, user.password!!,
|
bank.domesticBankCode, bank.loginName, bank.password!!,
|
||||||
BankAccountIdentifier(account.identifier, account.subAccountNumber, account.iban), // TODO: use BankingClient's one
|
BankAccountIdentifier(account.identifier, account.subAccountNumber, account.iban), // TODO: use BankingClient's one
|
||||||
recipientName, recipientAccountIdentifier, recipientBankIdentifier,
|
recipientName, recipientAccountIdentifier, recipientBankIdentifier,
|
||||||
amount, "EUR",
|
amount, "EUR",
|
||||||
|
@ -251,7 +273,7 @@ class BankingService(
|
||||||
} else if (response.type == ResponseType.Success) {
|
} else if (response.type == ResponseType.Success) {
|
||||||
uiState.dispatchTransferredMoney(TransferredMoneyEvent(recipientName, amount, currency))
|
uiState.dispatchTransferredMoney(TransferredMoneyEvent(recipientName, amount, currency))
|
||||||
|
|
||||||
updateAccountTransactions(user, account)
|
updateAccountTransactions(bank, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.type == ResponseType.Success
|
return response.type == ResponseType.Success
|
||||||
|
@ -269,6 +291,24 @@ class BankingService(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun updateOnChanges(uiSettings: UiSettings) {
|
||||||
|
updateOnChanges(uiSettings, uiSettings.transactionsGrouping)
|
||||||
|
|
||||||
|
updateOnChanges(uiSettings, uiSettings.showBalance)
|
||||||
|
updateOnChanges(uiSettings, uiSettings.showBankIcons)
|
||||||
|
updateOnChanges(uiSettings, uiSettings.showColoredAmounts)
|
||||||
|
updateOnChanges(uiSettings, uiSettings.showTransactionsInAlternatingColors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateOnChanges(uiSettings: UiSettings, state: MutableStateFlow<*>) {
|
||||||
|
uiSettings.viewModelScope.launch(Dispatchers.Unconfined) {
|
||||||
|
state.collect {
|
||||||
|
saveUiSettings(uiSettings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package net.codinux.banking.ui.service
|
package net.codinux.banking.ui.service
|
||||||
|
|
||||||
import net.codinux.banking.client.model.*
|
import net.codinux.banking.client.model.*
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
import net.codinux.banking.ui.model.AccountTransactionsFilter
|
import net.codinux.banking.ui.model.AccountTransactionsFilter
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ class CalculatorService {
|
||||||
fun sumTransactions(transactions: Collection<AccountTransactionViewModel>): Amount =
|
fun sumTransactions(transactions: Collection<AccountTransactionViewModel>): Amount =
|
||||||
transactions.map { it.amount }.sum()
|
transactions.map { it.amount }.sum()
|
||||||
|
|
||||||
fun calculateBalanceOfUser(user: User): Amount =
|
fun calculateBalanceOfBankAccess(bank: BankAccess): Amount =
|
||||||
sumAmounts(user.accounts.map { it.balance })
|
sumAmounts(bank.accounts.map { it.balance })
|
||||||
|
|
||||||
fun sumAmounts(amounts: Collection<Amount>): Amount =
|
fun sumAmounts(amounts: Collection<Amount>): Amount =
|
||||||
amounts.sum()
|
amounts.sum()
|
||||||
|
@ -22,9 +22,9 @@ class CalculatorService {
|
||||||
fun sumExpenses(transactions: Collection<AccountTransactionViewModel>): Amount =
|
fun sumExpenses(transactions: Collection<AccountTransactionViewModel>): Amount =
|
||||||
sumAmounts(transactions.map { it.amount }.filter { it.isNegative })
|
sumAmounts(transactions.map { it.amount }.filter { it.isNegative })
|
||||||
|
|
||||||
fun calculateBalanceOfDisplayedTransactions(transactions: Collection<AccountTransactionViewModel>, users: Collection<UserEntity>, filter: AccountTransactionsFilter): Amount {
|
fun calculateBalanceOfDisplayedTransactions(transactions: Collection<AccountTransactionViewModel>, banks: Collection<BankAccessEntity>, filter: AccountTransactionsFilter): Amount {
|
||||||
if (filter.noFiltersApplied) {
|
if (filter.noFiltersApplied) {
|
||||||
return sumAmounts(users.flatMap { it.accounts.map { it.balance } })
|
return sumAmounts(banks.flatMap { it.accounts.map { it.balance } })
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedAccount = filter.selectedAccount
|
val selectedAccount = filter.selectedAccount
|
||||||
|
@ -33,7 +33,7 @@ class CalculatorService {
|
||||||
if (selectedAccount.bankAccount != null) {
|
if (selectedAccount.bankAccount != null) {
|
||||||
selectedAccount.bankAccount.balance
|
selectedAccount.bankAccount.balance
|
||||||
} else {
|
} else {
|
||||||
calculateBalanceOfUser(selectedAccount.user)
|
calculateBalanceOfBankAccess(selectedAccount.bank)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sumTransactions(transactions)
|
sumTransactions(transactions)
|
||||||
|
|
|
@ -10,7 +10,7 @@ class UiSettings : ViewModel() {
|
||||||
|
|
||||||
val transactionsGrouping = MutableStateFlow(TransactionsGrouping.Month)
|
val transactionsGrouping = MutableStateFlow(TransactionsGrouping.Month)
|
||||||
|
|
||||||
val zebraStripes = MutableStateFlow(true)
|
val showTransactionsInAlternatingColors = MutableStateFlow(true)
|
||||||
|
|
||||||
val showBankIcons = MutableStateFlow(true)
|
val showBankIcons = MutableStateFlow(true)
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import net.codinux.banking.client.model.tan.EnterTanResult
|
import net.codinux.banking.client.model.tan.EnterTanResult
|
||||||
import net.codinux.banking.client.model.tan.TanChallenge
|
import net.codinux.banking.client.model.tan.TanChallenge
|
||||||
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
import net.codinux.banking.dataaccess.entities.HoldingEntity
|
||||||
import net.codinux.banking.dataaccess.entities.UserEntity
|
import net.codinux.banking.dataaccess.entities.BankAccessEntity
|
||||||
import net.codinux.banking.ui.model.*
|
import net.codinux.banking.ui.model.*
|
||||||
import net.codinux.banking.ui.model.error.ApplicationError
|
import net.codinux.banking.ui.model.error.ApplicationError
|
||||||
import net.codinux.banking.ui.model.error.BankingClientError
|
import net.codinux.banking.ui.model.error.BankingClientError
|
||||||
import net.codinux.banking.ui.model.error.ErroneousAction
|
import net.codinux.banking.ui.model.error.ErroneousAction
|
||||||
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent
|
||||||
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
|
import net.codinux.banking.ui.model.events.TransferredMoneyEvent
|
||||||
|
import net.codinux.banking.ui.model.settings.AppSettings
|
||||||
|
|
||||||
class UiState : ViewModel() {
|
class UiState : ViewModel() {
|
||||||
|
|
||||||
val users = MutableStateFlow<List<UserEntity>>(emptyList())
|
val appSettings = MutableStateFlow(AppSettings())
|
||||||
|
|
||||||
|
|
||||||
|
val banks = MutableStateFlow<List<BankAccessEntity>>(emptyList())
|
||||||
|
|
||||||
val transactions = MutableStateFlow<List<AccountTransactionViewModel>>(emptyList())
|
val transactions = MutableStateFlow<List<AccountTransactionViewModel>>(emptyList())
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ import kotlin.Boolean;
|
||||||
CREATE TABLE IF NOT EXISTS AccountTransaction (
|
CREATE TABLE IF NOT EXISTS AccountTransaction (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
userId INTEGER NOT NULL,
|
bankId INTEGER NOT NULL,
|
||||||
bankAccountId INTEGER NOT NULL,
|
accountId INTEGER NOT NULL,
|
||||||
|
|
||||||
amount TEXT NOT NULL,
|
amount TEXT NOT NULL,
|
||||||
currency TEXT NOT NULL,
|
currency TEXT NOT NULL,
|
||||||
|
@ -22,7 +22,8 @@ CREATE TABLE IF NOT EXISTS AccountTransaction (
|
||||||
openingBalance TEXT,
|
openingBalance TEXT,
|
||||||
closingBalance TEXT,
|
closingBalance TEXT,
|
||||||
|
|
||||||
userSetDisplayName TEXT,
|
userSetReference TEXT,
|
||||||
|
userSetOtherPartyName TEXT,
|
||||||
category TEXT,
|
category TEXT,
|
||||||
notes TEXT,
|
notes TEXT,
|
||||||
|
|
||||||
|
@ -53,10 +54,16 @@ CREATE TABLE IF NOT EXISTS AccountTransaction (
|
||||||
isReversal INTEGER AS Boolean NOT NULL
|
isReversal INTEGER AS Boolean NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_AccountTransaction_bankId
|
||||||
|
ON AccountTransaction (bankId);
|
||||||
|
|
||||||
|
CREATE INDEX idx_AccountTransaction_accountId
|
||||||
|
ON AccountTransaction (accountId);
|
||||||
|
|
||||||
|
|
||||||
insertTransaction:
|
insertTransaction:
|
||||||
INSERT INTO AccountTransaction(
|
INSERT INTO AccountTransaction(
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
|
|
||||||
amount, currency, reference,
|
amount, currency, reference,
|
||||||
bookingDate, valueDate,
|
bookingDate, valueDate,
|
||||||
|
@ -66,7 +73,8 @@ INSERT INTO AccountTransaction(
|
||||||
|
|
||||||
openingBalance, closingBalance,
|
openingBalance, closingBalance,
|
||||||
|
|
||||||
userSetDisplayName, category, notes,
|
userSetReference, userSetOtherPartyName,
|
||||||
|
category, notes,
|
||||||
|
|
||||||
statementNumber, sheetNumber,
|
statementNumber, sheetNumber,
|
||||||
|
|
||||||
|
@ -96,7 +104,8 @@ VALUES(
|
||||||
|
|
||||||
?, ?,
|
?, ?,
|
||||||
|
|
||||||
?, ?, ?,
|
?, ?,
|
||||||
|
?, ?,
|
||||||
|
|
||||||
?, ?,
|
?, ?,
|
||||||
|
|
||||||
|
@ -118,18 +127,18 @@ VALUES(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
selectAllTransactions:
|
getAllTransactions:
|
||||||
SELECT AccountTransaction.*
|
SELECT AccountTransaction.*
|
||||||
FROM AccountTransaction;
|
FROM AccountTransaction;
|
||||||
|
|
||||||
selectAllTransactionsAsViewModel:
|
getAllTransactionsAsViewModel:
|
||||||
SELECT id, userId, bankAccountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetDisplayName, category
|
SELECT id, bankId, accountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetReference, userSetOtherPartyName
|
||||||
FROM AccountTransaction;
|
FROM AccountTransaction;
|
||||||
|
|
||||||
|
|
||||||
selectAllTransactionsOfUser:
|
getAllTransactionsForBank:
|
||||||
SELECT AccountTransaction.*
|
SELECT AccountTransaction.*
|
||||||
FROM AccountTransaction WHERE userId = ?;
|
FROM AccountTransaction WHERE bankId = ?;
|
||||||
|
|
||||||
getTransactionWithId:
|
getTransactionWithId:
|
||||||
SELECT AccountTransaction.*
|
SELECT AccountTransaction.*
|
||||||
|
@ -141,8 +150,8 @@ FROM AccountTransaction WHERE id = ?;
|
||||||
CREATE TABLE IF NOT EXISTS Holding (
|
CREATE TABLE IF NOT EXISTS Holding (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
userId INTEGER NOT NULL,
|
bankId INTEGER NOT NULL,
|
||||||
bankAccountId INTEGER NOT NULL,
|
accountId INTEGER NOT NULL,
|
||||||
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
isin TEXT,
|
isin TEXT,
|
||||||
|
@ -163,10 +172,16 @@ CREATE TABLE IF NOT EXISTS Holding (
|
||||||
buyingDate TEXT
|
buyingDate TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_Holding_bankId
|
||||||
|
ON Holding (bankId);
|
||||||
|
|
||||||
|
CREATE INDEX idx_Holding_accountId
|
||||||
|
ON Holding (accountId);
|
||||||
|
|
||||||
|
|
||||||
insertHolding:
|
insertHolding:
|
||||||
INSERT INTO Holding(
|
INSERT INTO Holding(
|
||||||
userId, bankAccountId,
|
bankId, accountId,
|
||||||
|
|
||||||
name, isin, wkn,
|
name, isin, wkn,
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
import kotlin.Boolean;
|
import kotlin.Boolean;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS User (
|
CREATE TABLE IF NOT EXISTS BankAccess (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
bankCode TEXT NOT NULL,
|
domesticBankCode TEXT NOT NULL,
|
||||||
loginName TEXT NOT NULL,
|
loginName TEXT NOT NULL,
|
||||||
password TEXT,
|
password TEXT,
|
||||||
|
|
||||||
bankName TEXT NOT NULL,
|
bankName TEXT NOT NULL,
|
||||||
bic TEXT NOT NULL,
|
bic TEXT,
|
||||||
|
|
||||||
customerName TEXT NOT NULL,
|
customerName TEXT NOT NULL,
|
||||||
userId TEXT,
|
userId TEXT,
|
||||||
|
|
||||||
selectedTanMethodId TEXT,
|
selectedTanMethodIdentifier TEXT,
|
||||||
|
|
||||||
selectedTanMediumName TEXT,
|
selectedTanMediumIdentifier TEXT,
|
||||||
|
|
||||||
bankingGroup TEXT,
|
bankingGroup TEXT,
|
||||||
serverAddress TEXT,
|
serverAddress TEXT,
|
||||||
|
countryCode TEXT NOT NULL,
|
||||||
|
|
||||||
clientData TEXT,
|
clientData TEXT,
|
||||||
|
|
||||||
|
@ -31,20 +32,21 @@ CREATE TABLE IF NOT EXISTS User (
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
insertUser:
|
insertBank:
|
||||||
INSERT INTO User(
|
INSERT INTO BankAccess(
|
||||||
bankCode, loginName, password,
|
domesticBankCode, loginName, password,
|
||||||
|
|
||||||
bankName, bic,
|
bankName, bic,
|
||||||
|
|
||||||
customerName, userId,
|
customerName, userId,
|
||||||
|
|
||||||
selectedTanMethodId,
|
selectedTanMethodIdentifier,
|
||||||
|
|
||||||
selectedTanMediumName,
|
selectedTanMediumIdentifier,
|
||||||
|
|
||||||
bankingGroup,
|
bankingGroup,
|
||||||
serverAddress,
|
serverAddress,
|
||||||
|
countryCode,
|
||||||
|
|
||||||
clientData,
|
clientData,
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ VALUES(
|
||||||
|
|
||||||
?,
|
?,
|
||||||
|
|
||||||
?, ?,
|
?, ?, ?,
|
||||||
|
|
||||||
?,
|
?,
|
||||||
|
|
||||||
|
@ -72,16 +74,16 @@ VALUES(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
selectAllUsers:
|
getAllBanks:
|
||||||
SELECT User.*
|
SELECT BankAccess.*
|
||||||
FROM User;
|
FROM BankAccess;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS BankAccount (
|
CREATE TABLE IF NOT EXISTS BankAccount (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
userId INTEGER NOT NULL,
|
bankId INTEGER NOT NULL,
|
||||||
|
|
||||||
identifier TEXT NOT NULL,
|
identifier TEXT NOT NULL,
|
||||||
subAccountNumber TEXT,
|
subAccountNumber TEXT,
|
||||||
|
@ -109,10 +111,13 @@ CREATE TABLE IF NOT EXISTS BankAccount (
|
||||||
includeInAutomaticAccountsUpdate INTEGER AS Boolean NOT NULL
|
includeInAutomaticAccountsUpdate INTEGER AS Boolean NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_BankAccount_bankId
|
||||||
|
ON BankAccount (bankId);
|
||||||
|
|
||||||
|
|
||||||
insertBankAccount:
|
insertBankAccount:
|
||||||
INSERT INTO BankAccount(
|
INSERT INTO BankAccount(
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
identifier, accountHolderName, type,
|
identifier, accountHolderName, type,
|
||||||
iban, subAccountNumber, productName, currency, accountLimit,
|
iban, subAccountNumber, productName, currency, accountLimit,
|
||||||
|
@ -145,7 +150,7 @@ VALUES(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
selectAllBankAccounts:
|
getAllBankAccounts:
|
||||||
SELECT BankAccount.*
|
SELECT BankAccount.*
|
||||||
FROM BankAccount;
|
FROM BankAccount;
|
||||||
|
|
||||||
|
@ -154,25 +159,32 @@ FROM BankAccount;
|
||||||
CREATE TABLE IF NOT EXISTS TanMethod (
|
CREATE TABLE IF NOT EXISTS TanMethod (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
userId INTEGER NOT NULL,
|
bankId INTEGER NOT NULL,
|
||||||
|
|
||||||
displayName TEXT NOT NULL,
|
displayName TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
identifier TEXT NOT NULL,
|
identifier TEXT NOT NULL,
|
||||||
maxTanInputLength INTEGER ,
|
maxTanInputLength INTEGER ,
|
||||||
allowedTanFormat TEXT NOT NULL
|
allowedTanFormat TEXT NOT NULL,
|
||||||
|
|
||||||
|
userSetDisplayName TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_TanMethod_bankId
|
||||||
|
ON TanMethod (bankId);
|
||||||
|
|
||||||
|
|
||||||
insertTanMethod:
|
insertTanMethod:
|
||||||
INSERT INTO TanMethod(
|
INSERT INTO TanMethod(
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
displayName,
|
displayName,
|
||||||
type,
|
type,
|
||||||
identifier,
|
identifier,
|
||||||
maxTanInputLength,
|
maxTanInputLength,
|
||||||
allowedTanFormat
|
allowedTanFormat,
|
||||||
|
|
||||||
|
userSetDisplayName
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
?,
|
?,
|
||||||
|
@ -181,11 +193,13 @@ VALUES (
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
?,
|
||||||
|
|
||||||
?
|
?
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
selectAllTanMethods:
|
getAllTanMethods:
|
||||||
SELECT TanMethod.*
|
SELECT TanMethod.*
|
||||||
FROM TanMethod;
|
FROM TanMethod;
|
||||||
|
|
||||||
|
@ -194,7 +208,7 @@ FROM TanMethod;
|
||||||
CREATE TABLE IF NOT EXISTS TanMedium (
|
CREATE TABLE IF NOT EXISTS TanMedium (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
userId INTEGER NOT NULL,
|
bankId INTEGER NOT NULL,
|
||||||
|
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
mediumName TEXT,
|
mediumName TEXT,
|
||||||
|
@ -209,13 +223,18 @@ CREATE TABLE IF NOT EXISTS TanMedium (
|
||||||
cardSequenceNumber TEXT,
|
cardSequenceNumber TEXT,
|
||||||
cardType INTEGER,
|
cardType INTEGER,
|
||||||
validFrom TEXT,
|
validFrom TEXT,
|
||||||
validTo TEXT
|
validTo TEXT,
|
||||||
|
|
||||||
|
userSetDisplayName TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_TanMedium_bankId
|
||||||
|
ON TanMedium (bankId);
|
||||||
|
|
||||||
|
|
||||||
insertTanMedium:
|
insertTanMedium:
|
||||||
INSERT INTO TanMedium(
|
INSERT INTO TanMedium(
|
||||||
userId,
|
bankId,
|
||||||
|
|
||||||
type,
|
type,
|
||||||
mediumName,
|
mediumName,
|
||||||
|
@ -228,7 +247,9 @@ INSERT INTO TanMedium(
|
||||||
cardSequenceNumber,
|
cardSequenceNumber,
|
||||||
cardType,
|
cardType,
|
||||||
validFrom,
|
validFrom,
|
||||||
validTo
|
validTo,
|
||||||
|
|
||||||
|
userSetDisplayName
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
?,
|
?,
|
||||||
|
@ -244,11 +265,13 @@ VALUES (
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
|
?,
|
||||||
|
|
||||||
?
|
?
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
selectAllTanMedia:
|
getAllTanMedia:
|
||||||
SELECT TanMedium.*
|
SELECT TanMedium.*
|
||||||
FROM TanMedium;
|
FROM TanMedium;
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import kotlin.Boolean;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS AppSettings (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
|
||||||
|
authenticationMethod TEXT NOT NULL,
|
||||||
|
hashedPassword TEXT,
|
||||||
|
|
||||||
|
updateAccountsOnAppStart INTEGER AS Boolean NOT NULL,
|
||||||
|
updateAccountsIfNoUpdatedForHours INTEGER NOT NULL,
|
||||||
|
|
||||||
|
sideMenuWidth INTEGER NOT NULL,
|
||||||
|
|
||||||
|
windowPositionX INTEGER NOT NULL,
|
||||||
|
windowPositionY INTEGER NOT NULL,
|
||||||
|
|
||||||
|
windowWidth INTEGER NOT NULL,
|
||||||
|
windowHeight INTEGER NOT NULL,
|
||||||
|
|
||||||
|
windowState TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
getAppSettings:
|
||||||
|
SELECT * FROM AppSettings WHERE id = 1;
|
||||||
|
|
||||||
|
upsertAppSettings:
|
||||||
|
INSERT OR REPLACE INTO AppSettings(
|
||||||
|
id,
|
||||||
|
|
||||||
|
authenticationMethod, hashedPassword, updateAccountsOnAppStart, updateAccountsIfNoUpdatedForHours,
|
||||||
|
|
||||||
|
sideMenuWidth,
|
||||||
|
|
||||||
|
windowPositionX, windowPositionY, windowWidth, windowHeight,
|
||||||
|
|
||||||
|
windowState
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
1,
|
||||||
|
|
||||||
|
?, ?, ?, ?,
|
||||||
|
|
||||||
|
?,
|
||||||
|
|
||||||
|
?, ?,
|
||||||
|
?, ?,
|
||||||
|
|
||||||
|
?
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS UiSettings (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
|
||||||
|
transactionsGrouping TEXT NOT NULL,
|
||||||
|
|
||||||
|
showBalance INTEGER AS Boolean NOT NULL,
|
||||||
|
showBankIcons INTEGER AS Boolean NOT NULL,
|
||||||
|
showColoredAmounts INTEGER AS Boolean NOT NULL,
|
||||||
|
showTransactionsInAlternatingColors INTEGER AS Boolean NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
getUiSettings:
|
||||||
|
SELECT * FROM UiSettings WHERE id = 1;
|
||||||
|
|
||||||
|
upsertUiSettings:
|
||||||
|
INSERT OR REPLACE INTO UiSettings(id, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors)
|
||||||
|
VALUES (1, ?, ?, ?, ?, ?);
|
|
@ -13,8 +13,8 @@ import net.codinux.banking.ui.model.TanChallengeReceived
|
||||||
@Composable
|
@Composable
|
||||||
fun EnterTanDialogPreview_EnterTan() {
|
fun EnterTanDialogPreview_EnterTan() {
|
||||||
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
val tanMethods = listOf(TanMethod("Zeig mich an", TanMethodType.AppTan, "902"))
|
||||||
val user = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
val bank = BankViewInfo("12345678", "SupiDupiNutzer", "Abzockbank", BankingGroup.Postbank)
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, user = user)
|
val tanChallenge = TanChallenge(TanChallengeType.EnterTan, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethods.first().identifier, tanMethods, bank = bank)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ fun EnterTanDialogPreview_TanImage() {
|
||||||
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
val tanMethod = TanMethod("photoTAN-Verfahren", TanMethodType.photoTan, "902", 6, AllowedTanFormat.Numeric)
|
||||||
val tanImage = TanImage("image/png", tanImageBytes)
|
val tanImage = TanImage("image/png", tanImageBytes)
|
||||||
|
|
||||||
val user = BankViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank", BankingGroup.Comdirect)
|
val bank = BankViewInfo("10010010", "Ihr krasser Login Name", "Phantasie Bank", BankingGroup.Comdirect)
|
||||||
|
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, user)
|
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetAccountInfo, "Geben Sie die TAN ein", tanMethod.identifier, listOf(tanMethod), null, emptyList(), tanImage, null, bank)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
||||||
|
@ -50,10 +50,10 @@ fun EnterTanDialogPreview_WithMultipleTanMedia() { // shows that dialog is reall
|
||||||
TanMedium(TanMediumType.TanGenerator, "SparkassenCard (Debitkarte)", TanMediumStatus.Used, TanGeneratorTanMedium("5432109876"))
|
TanMedium(TanMediumType.TanGenerator, "SparkassenCard (Debitkarte)", TanMediumStatus.Used, TanGeneratorTanMedium("5432109876"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val user = BankViewInfo("10010010", "Ihr krasser Login Name", "Eine ganz gewöhnliche Sparkasse", BankingGroup.Sparkasse)
|
val bank = BankViewInfo("10010010", "Ihr krasser Login Name", "Eine ganz gewöhnliche Sparkasse", BankingGroup.Sparkasse)
|
||||||
val account = BankAccountViewInfo("12345678", null, BankAccountType.CheckingAccount, null, "Giro Konto")
|
val account = BankAccountViewInfo("12345678", null, BankAccountType.CheckingAccount, null, "Giro Konto")
|
||||||
|
|
||||||
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetTransactions, "Sie möchten eine \"Umsatzabfrage\" freigeben: Bitte bestätigen Sie den \"Startcode 80061030\" mit der Taste \"OK\".", "913", tanMethods, "SparkassenCard (Debitkarte)", tanMedia, tanImage, null, user, account)
|
val tanChallenge = TanChallenge(TanChallengeType.Image, ActionRequiringTan.GetTransactions, "Sie möchten eine \"Umsatzabfrage\" freigeben: Bitte bestätigen Sie den \"Startcode 80061030\" mit der Taste \"OK\".", "913", tanMethods, "SparkassenCard (Debitkarte)", tanMedia, tanImage, null, bank, account)
|
||||||
|
|
||||||
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
EnterTanDialog(TanChallengeReceived(tanChallenge) { }) { }
|
||||||
}
|
}
|
|
@ -17,8 +17,8 @@ class SqliteBankingRepositoryTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val underTest = object : SqliteBankingRepository(sqlDriver) {
|
private val underTest = object : SqliteBankingRepository(sqlDriver) {
|
||||||
override public suspend fun persistTransaction(userId: Long, bankAccountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
|
override public suspend fun persistTransaction(bankId: Long, accountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
|
||||||
super.persistTransaction(userId, bankAccountId, transaction)
|
super.persistTransaction(bankId, accountId, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,35 +27,35 @@ class SqliteBankingRepositoryTest {
|
||||||
val bankAccounts = listOf(
|
val bankAccounts = listOf(
|
||||||
BankAccount("12345", null, null, null, "Monika Tester", BankAccountType.CheckingAccount, balance = Amount("12.34"), retrievedTransactionsFrom = LocalDate(2024, 5, 7), features = setOf(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.InstantTransfer), serverTransactionsRetentionDays = 320)
|
BankAccount("12345", null, null, null, "Monika Tester", BankAccountType.CheckingAccount, balance = Amount("12.34"), retrievedTransactionsFrom = LocalDate(2024, 5, 7), features = setOf(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.InstantTransfer), serverTransactionsRetentionDays = 320)
|
||||||
)
|
)
|
||||||
val user = User("12345678", "SupiDupiUser", "geheim", "Abzock-Bank", "ABCDDEBBXXX", "Monika Tester", accounts = bankAccounts, bankingGroup = BankingGroup.DKB, serverAddress = "").apply {
|
val bank = BankAccess("12345678", "SupiDupiUser", "geheim", "Abzock-Bank", "ABCDDEBBXXX", "Monika Tester", accounts = bankAccounts, bankingGroup = BankingGroup.DKB, serverAddress = "").apply {
|
||||||
wrongCredentialsEntered = true
|
wrongCredentialsEntered = true
|
||||||
displayIndex = 99
|
displayIndex = 99
|
||||||
}
|
}
|
||||||
|
|
||||||
val persisted = underTest.persistUser(user)
|
val persisted = underTest.persistBank(bank)
|
||||||
|
|
||||||
assertNotNull(persisted.id)
|
assertNotNull(persisted.id)
|
||||||
|
|
||||||
assertEquals(user.bankCode, persisted.bankCode)
|
assertEquals(bank.domesticBankCode, persisted.domesticBankCode)
|
||||||
assertEquals(user.loginName, persisted.loginName)
|
assertEquals(bank.loginName, persisted.loginName)
|
||||||
assertEquals(user.password, persisted.password)
|
assertEquals(bank.password, persisted.password)
|
||||||
|
|
||||||
assertEquals(user.bankName, persisted.bankName)
|
assertEquals(bank.bankName, persisted.bankName)
|
||||||
assertEquals(user.bic, persisted.bic)
|
assertEquals(bank.bic, persisted.bic)
|
||||||
|
|
||||||
assertEquals(user.customerName, persisted.customerName)
|
assertEquals(bank.customerName, persisted.customerName)
|
||||||
assertEquals(user.userId, persisted.userId)
|
assertEquals(bank.userId, persisted.userId)
|
||||||
|
|
||||||
assertEquals(user.bankingGroup, persisted.bankingGroup)
|
assertEquals(bank.bankingGroup, persisted.bankingGroup)
|
||||||
|
|
||||||
assertEquals(user.wrongCredentialsEntered, persisted.wrongCredentialsEntered)
|
assertEquals(bank.wrongCredentialsEntered, persisted.wrongCredentialsEntered)
|
||||||
assertEquals(user.displayIndex, persisted.displayIndex)
|
assertEquals(bank.displayIndex, persisted.displayIndex)
|
||||||
|
|
||||||
assertEquals(1, persisted.accounts.size)
|
assertEquals(1, persisted.accounts.size)
|
||||||
|
|
||||||
val persistedBankAccount = persisted.accounts.first()
|
val persistedBankAccount = persisted.accounts.first()
|
||||||
assertNotNull(persistedBankAccount.id)
|
assertNotNull(persistedBankAccount.id)
|
||||||
assertEquals(persisted.id, persistedBankAccount.userId)
|
assertEquals(persisted.id, persistedBankAccount.bankId)
|
||||||
|
|
||||||
assertEquals(bankAccounts.first().identifier, persistedBankAccount.identifier)
|
assertEquals(bankAccounts.first().identifier, persistedBankAccount.identifier)
|
||||||
assertEquals(bankAccounts.first().accountHolderName, persistedBankAccount.accountHolderName)
|
assertEquals(bankAccounts.first().accountHolderName, persistedBankAccount.accountHolderName)
|
||||||
|
|
|
@ -14,7 +14,7 @@ sqlDelight = "2.0.2"
|
||||||
|
|
||||||
agp = "8.5.2"
|
agp = "8.5.2"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "34"
|
||||||
android-minSdk = "24"
|
android-minSdk = "23"
|
||||||
android-targetSdk = "34"
|
android-targetSdk = "34"
|
||||||
androidx-activityCompose = "1.9.1"
|
androidx-activityCompose = "1.9.1"
|
||||||
androidx-appcompat = "1.7.0"
|
androidx-appcompat = "1.7.0"
|
||||||
|
|
Loading…
Reference in New Issue