diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/SqliteBankingRepository.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/SqliteBankingRepository.kt index ba9afbd..c82e514 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/SqliteBankingRepository.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/SqliteBankingRepository.kt @@ -4,10 +4,12 @@ import app.cash.sqldelight.db.SqlDriver import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate import net.codinux.banking.client.model.* -import net.codinux.banking.dataaccess.entities.AccountTransactionEntity -import net.codinux.banking.dataaccess.entities.BankAccountEntity -import net.codinux.banking.dataaccess.entities.UserEntity +import net.codinux.banking.client.model.tan.AllowedTanFormat +import net.codinux.banking.client.model.tan.TanMethod +import net.codinux.banking.client.model.tan.TanMethodType +import net.codinux.banking.dataaccess.entities.* import net.codinux.banking.ui.model.AccountTransactionViewModel +import net.codinux.log.Log import net.codinux.log.logger import kotlin.enums.EnumEntries import kotlin.js.JsName @@ -28,9 +30,10 @@ open class SqliteBankingRepository( override fun getAllUsers(): List { val bankAccounts = getAllBankAccounts().groupBy { it.userId } + val tanMethods = getAllTanMethods().groupBy { it.userId } return userQueries.selectAllUsers { id, bankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodIdentifier, selectedTanMediumIdentifier, bankingGroup, serverAddress, userSetDisplayName, displayIndex, iconUrl, wrongCredentialsEntered -> - UserEntity(id, bankCode, loginName, password, bankName, bic, customerName, userId, bankAccounts[id] ?: emptyList(), selectedTanMethodIdentifier, emptyList(), selectedTanMediumIdentifier, emptyList(), + UserEntity(id, bankCode, loginName, password, bankName, bic, customerName, userId, bankAccounts[id] ?: emptyList(), selectedTanMethodIdentifier, tanMethods[id] ?: emptyList(), selectedTanMediumIdentifier, emptyList(), bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered) }.executeAsList() } @@ -46,12 +49,13 @@ open class SqliteBankingRepository( val bankAccounts = persistBankAccounts(userId, user.accounts) - UserEntity(userId, user, bankAccounts) + val tanMethods = persistTanMethods(userId, user.tanMethods) + + UserEntity(userId, user, bankAccounts, tanMethods) } } - fun getAllBankAccounts(): List = userQueries.selectAllBankAccounts { id, userId, identifier, subAccountNumber, iban, productName, accountHolderName, type, currency, accountLimit, isAccountTypeSupportedByApplication, features, balance, serverTransactionsRetentionDays, lastTransactionsRetrievalTime, retrievedTransactionsFrom, userSetDisplayName, displayIndex, hideAccount, includeInAutomaticAccountsUpdate -> BankAccountEntity( id, userId, @@ -107,6 +111,39 @@ open class SqliteBankingRepository( } + private fun getAllTanMethods(): List = userQueries.selectAllTanMethods { id, userId, displayName, type, identifier, maxTanInputLength, allowedTanFormat -> + TanMethodEntity( + id, + userId, + + displayName, + mapToEnum(type, TanMethodType.entries), + identifier, + mapToInt(maxTanInputLength), + mapToEnum(allowedTanFormat, AllowedTanFormat.entries) + ) + }.executeAsList() + + private suspend fun persistTanMethods(userId: Long, tanMethods: List): List = + tanMethods.map { persistTanMethod(userId, it) } + + private suspend fun persistTanMethod(userId: Long, tanMethod: TanMethod): TanMethodEntity { + userQueries.insertTanMethod( + userId, + + tanMethod.displayName, + mapEnum(tanMethod.type), + tanMethod.identifier, + mapInt(tanMethod.maxTanInputLength), + mapEnum(tanMethod.allowedTanFormat) + ) + + val tanMethodId = getLastInsertedId() + + return TanMethodEntity(tanMethodId, userId, tanMethod) + } + + override fun getAllAccountTransactionsAsViewModel(): List = accountTransactionQueries.selectAllTransactionsAsViewModel { id, userId, bankAccountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetDisplayName, category -> AccountTransactionViewModel(id, userId, bankAccountId, mapToAmount(amount), currency, reference, mapToDate(valueDate), otherPartyName, postingText, userSetDisplayName, category) @@ -284,7 +321,10 @@ open class SqliteBankingRepository( private fun > mapEnum(enum: Enum): String = enum.name - private fun > mapToEnum(enumName: String, values: EnumEntries): E? { + private fun > mapToEnum(enumName: String, values: EnumEntries): E = + values.first { it.name == enumName } + + private fun > mapToEnumNullable(enumName: String, values: EnumEntries): E? { val mapped = values.firstOrNull { it.name == enumName } if (mapped == null) { @@ -298,7 +338,7 @@ open class SqliteBankingRepository( enums.joinToString(",") { it.name } private fun > mapEnumSet(enumsString: String, values: EnumEntries): Set = - enumsString.split(',').filterNot { it.isBlank() }.mapNotNull { mapToEnum(it, values) }.toSet() + enumsString.split(',').filterNot { it.isBlank() }.mapNotNull { mapToEnumNullable(it, values) }.toSet() @JvmName("mapIntNullable") @JsName("mapIntNullable") diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/TanMethodEntity.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/TanMethodEntity.kt new file mode 100644 index 0000000..92f4f0c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/TanMethodEntity.kt @@ -0,0 +1,21 @@ +package net.codinux.banking.dataaccess.entities + +import net.codinux.banking.client.model.tan.AllowedTanFormat +import net.codinux.banking.client.model.tan.TanMethod +import net.codinux.banking.client.model.tan.TanMethodType + +class TanMethodEntity( + val id: Long, + val userId: Long, + + displayName: String, + type: TanMethodType, + identifier: String, + maxTanInputLength: Int? = null, + allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric +) : TanMethod(displayName, type, identifier, maxTanInputLength, allowedTanFormat) { + + constructor(id: Long, userId: Long, tanMethod: TanMethod) + : this(id, userId, tanMethod.displayName, tanMethod.type, tanMethod.identifier, tanMethod.maxTanInputLength, tanMethod.allowedTanFormat) + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/UserEntity.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/UserEntity.kt index a7dd175..a254d6b 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/UserEntity.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/dataaccess/entities/UserEntity.kt @@ -21,7 +21,7 @@ class UserEntity( override val accounts: List = emptyList(), selectedTanMethodIdentifier: String? = null, - tanMethods: List = listOf(), + override val tanMethods: List = listOf(), selectedTanMediumIdentifier: String? = null, tanMedia: List = listOf(), @@ -45,11 +45,11 @@ class UserEntity( } - constructor(id: Long, user: User, bankAccounts: List) : this( + constructor(id: Long, user: User, bankAccounts: List, tanMethods: List) : this( id, user.bankCode, user.loginName, user.password, user.bankName, user.bic, user.customerName, user.userId, bankAccounts, - user.selectedTanMethodIdentifier, user.tanMethods, user.selectedTanMediumIdentifier, user.tanMedia, + user.selectedTanMethodIdentifier, tanMethods, user.selectedTanMediumIdentifier, user.tanMedia, user.bankingGroup, user.serverAddress, user.userSetDisplayName, user.displayIndex, user.iconUrl, user.wrongCredentialsEntered, diff --git a/composeApp/src/commonMain/sqldelight/net/codinux/banking/ui/User.sq b/composeApp/src/commonMain/sqldelight/net/codinux/banking/ui/User.sq index c1a6511..ddd519b 100644 --- a/composeApp/src/commonMain/sqldelight/net/codinux/banking/ui/User.sq +++ b/composeApp/src/commonMain/sqldelight/net/codinux/banking/ui/User.sq @@ -141,6 +141,46 @@ SELECT BankAccount.* FROM BankAccount; + +CREATE TABLE IF NOT EXISTS TanMethod ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + + userId INTEGER NOT NULL, + + displayName TEXT NOT NULL, + type TEXT NOT NULL, + identifier TEXT NOT NULL, + maxTanInputLength INTEGER , + allowedTanFormat TEXT NOT NULL +); + + +insertTanMethod: +INSERT INTO TanMethod( + userId, + + displayName, + type, + identifier, + maxTanInputLength, + allowedTanFormat +) +VALUES ( + ?, + + ?, + ?, + ?, + ?, + ? +); + + +selectAllTanMethods: +SELECT TanMethod.* +FROM TanMethod; + + -- TODO: find a better place for this cross-cutting concern: getLastInsertedId: SELECT last_insert_rowid();