Updated database to new BankingClient model

This commit is contained in:
dankito 2024-09-09 01:06:03 +02:00
parent df5df6f1ca
commit a2dbe912d4
12 changed files with 142 additions and 114 deletions

View File

@ -54,7 +54,7 @@ class InMemoryBankingRepository(
account.iconUrl, account.wrongCredentialsEntered,
)
// TODO: someday may fix and get userAccountId and bankAccountId
// TODO: someday may fix and get userId and bankAccountId
private fun map(transaction: AccountTransaction, userId: Long = nextId++, bankAccountId: Long = nextId++) = AccountTransactionEntity(
nextId++,
userId, bankAccountId,

View File

@ -19,7 +19,7 @@ open class SqliteBankingRepository(
private val database = BankmeisterDb(sqlDriver)
private val userAccountQueries = database.userAccountQueries
private val userQueries = database.userQueries
private val accountTransactionQueries = database.accountTransactionQueries
@ -29,17 +29,17 @@ open class SqliteBankingRepository(
override fun getAllUsers(): List<UserEntity> {
val bankAccounts = getAllBankAccounts().groupBy { it.userId }
return userAccountQueries.selectAllUsers { id, bankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodId, selectedTanMediumName, bankingGroup, iconUrl, wrongCredentialsEntered, userSetDisplayName, displayIndex ->
UserEntity(id, bankCode, loginName, password, bankName, bic, customerName, userId, bankAccounts[id] ?: emptyList(), selectedTanMethodId, emptyList(), selectedTanMediumName, emptyList(),
bankingGroup?.let { BankingGroup.valueOf(it) }, "", userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered)
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(),
bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered)
}.executeAsList()
}
override suspend fun persistUser(user: User): UserEntity {
return userAccountQueries.transactionWithResult {
userAccountQueries.insertUser(user.bankCode, user.loginName, user.password, user.bankName, user.bic,
return userQueries.transactionWithResult {
userQueries.insertUser(user.bankCode, user.loginName, user.password, user.bankName, user.bic,
user.customerName, user.userId, user.selectedTanMethodIdentifier, user.selectedTanMediumIdentifier,
user.bankingGroup?.name, user.iconUrl, user.wrongCredentialsEntered, user.userSetDisplayName, user.displayIndex.toLong()
user.bankingGroup?.name, user.serverAddress, user.userSetDisplayName, user.displayIndex.toLong(), user.iconUrl, user.wrongCredentialsEntered
)
val userId = getLastInsertedId() // getLastInsertedId() / last_insert_rowid() has to be called in a transaction with the insert operation, otherwise it will not work
@ -52,11 +52,12 @@ open class SqliteBankingRepository(
fun getAllBankAccounts(): List<BankAccountEntity> = userAccountQueries.selectAllBankAccounts { id, userId, identifier, accountHolderName, type, iban, subAccountNumber, productName, currency, accountLimit, balance, isAccountTypeSupportedByApplication, features, serverTransactionsRetentionDays, lastTransactionsRetrievalTime, retrievedTransactionsFrom, userSetDisplayName, displayIndex, hideAccount, includeInAutomaticAccountsUpdate ->
fun getAllBankAccounts(): List<BankAccountEntity> = 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,
identifier, subAccountNumber, iban, productName,
accountHolderName, BankAccountType.valueOf(type),
currency, accountLimit,
@ -81,7 +82,7 @@ open class SqliteBankingRepository(
* 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 {
userAccountQueries.insertBankAccount(
userQueries.insertBankAccount(
userId,
account.identifier, account.accountHolderName, mapEnum(account.type),
account.iban, account.subAccountNumber, account.productName, account.currency, account.accountLimit,
@ -144,12 +145,16 @@ open class SqliteBankingRepository(
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
transaction.postingText,
mapAmount(transaction.openingBalance), mapAmount(transaction.closingBalance),
transaction.userSetDisplayName, transaction.category, transaction.notes,
transaction.statementNumber?.toLong(), transaction.sheetNumber?.toLong(),
mapAmount(transaction.openingBalance), mapAmount(transaction.closingBalance),
transaction.endToEndReference, transaction.customerReference, transaction.mandateReference,
transaction.customerReference, transaction.bankReference,
transaction.furtherInformation,
transaction.endToEndReference, transaction.mandateReference,
transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
transaction.compensationAmount, transaction.originalAmount,
@ -158,7 +163,9 @@ open class SqliteBankingRepository(
transaction.journalNumber, transaction.textKeyAddition,
transaction.orderReferenceNumber, transaction.referenceNumber
transaction.orderReferenceNumber, transaction.referenceNumber,
transaction.isReversal
)
return AccountTransactionEntity(getLastInsertedId(), userId, bankAccountId, transaction)
@ -166,7 +173,7 @@ open class SqliteBankingRepository(
private fun getLastInsertedId(): Long =
userAccountQueries.getLastInsertedId().executeAsOne()
userQueries.getLastInsertedId().executeAsOne()
private fun mapTransaction(
@ -178,21 +185,27 @@ open class SqliteBankingRepository(
otherPartyName: String?, otherPartyBankId: String?, otherPartyAccountId: String?,
postingText: String?,
openingBalance: String?, closingBalance: String?,
userSetDisplayName: String?, category: String?, notes: String?,
statementNumber: Long?, sheetNumber: Long?,
openingBalance: String?, closingBalance: String?,
customerReference: String?, bankReference: String?,
furtherInformation: String?,
endToEndReference: String?, customerReference: String?, mandateReference: String?, creditorIdentifier: String?, originatorsIdentificationCode: String?,
endToEndReference: String?, mandateReference: String?,
creditorIdentifier: String?, originatorsIdentificationCode: String?,
compensationAmount: String?, originalAmount: String?, deviantOriginator: String?, deviantRecipient: String?, referenceWithNoSpecialType: String?,
compensationAmount: String?, originalAmount: String?,
deviantOriginator: String?, deviantRecipient: String?,
referenceWithNoSpecialType: String?,
journalNumber: String?, textKeyAddition: String?,
referenceForTheAccountOwner: String?, referenceOfTheAccountServicingInstitution: String?, supplementaryDetails: String?,
orderReferenceNumber: String?, referenceNumber: String?,
transactionReferenceNumber: String?, relatedReferenceNumber: String?
isReversal: Boolean
): AccountTransactionEntity = AccountTransactionEntity(
id,
userId, bankAccountId,
@ -208,8 +221,8 @@ open class SqliteBankingRepository(
statementNumber?.toInt(), sheetNumber?.toInt(),
referenceForTheAccountOwner, referenceOfTheAccountServicingInstitution,
supplementaryDetails,
customerReference, bankReference,
furtherInformation,
endToEndReference, mandateReference,
creditorIdentifier, originatorsIdentificationCode,
@ -220,8 +233,9 @@ open class SqliteBankingRepository(
journalNumber, textKeyAddition,
transactionReferenceNumber, relatedReferenceNumber
// TODO: add isReversal
orderReferenceNumber, referenceNumber,
isReversal
)

View File

@ -76,8 +76,8 @@ fun SideMenuContent() {
Text("Konten", color = textColor)
}
BanksList(iconSize = iconSize, textColor = textColor, itemModifier = itemModifier, itemHorizontalPadding = ItemHorizontalPadding) { userAccount, bankAccount ->
uiState.transactionsFilter.value.selectedAccountChanged(userAccount, bankAccount)
BanksList(iconSize = iconSize, textColor = textColor, itemModifier = itemModifier, itemHorizontalPadding = ItemHorizontalPadding) { user, bankAccount ->
uiState.transactionsFilter.value.selectedAccountChanged(user, bankAccount)
coroutineScope.launch {
drawerState.close()

View File

@ -28,7 +28,7 @@ private val formatUtil = DI.formatUtil
fun GroupedTransactionsListItems(
modifier: Modifier,
transactionsToDisplay: List<AccountTransactionViewModel>,
userAccountsId: Map<Long, UserEntity>,
usersById: Map<Long, UserEntity>,
transactionsGrouping: TransactionsGrouping
) {
val groupingService = remember { TransactionsGroupingService() }
@ -58,7 +58,7 @@ fun GroupedTransactionsListItems(
Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed
monthTransactions.forEachIndexed { index, transaction ->
key(transaction.id) {
TransactionListItem(userAccountsId[transaction.userId], transaction, index, monthTransactions.size)
TransactionListItem(usersById[transaction.userId], transaction, index, monthTransactions.size)
}
}
}

View File

@ -16,7 +16,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.*
import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.BankAccountFeatures
import net.codinux.banking.ui.IOorDefault
import net.codinux.banking.ui.composables.BankIcon
import net.codinux.banking.ui.config.Colors
@ -39,11 +38,11 @@ fun TransferMoneyDialog(
data: ShowTransferMoneyDialogData,
onDismiss: () -> Unit,
) {
val userAccounts = uiState.users.value
val accountsToUserAccount = userAccounts.sortedBy { it.displayIndex }
val users = uiState.users.value
val accountsToUser = users.sortedBy { it.displayIndex }
.flatMap { user -> user.accounts.sortedBy { it.displayIndex }.map { it to user } }.toMap()
val accountsSupportingTransferringMoney = userAccounts.flatMap { it.accounts }
val accountsSupportingTransferringMoney = users.flatMap { it.accounts }
.filter { it.supportsMoneyTransfer }
if (accountsSupportingTransferringMoney.isEmpty()) {
@ -87,7 +86,7 @@ fun TransferMoneyDialog(
coroutineScope.launch(Dispatchers.IOorDefault) {
val successful = bankingService.transferMoney(
accountsToUserAccount[senderAccount]!!, senderAccount,
accountsToUser[senderAccount]!!, senderAccount,
recipientName, recipientAccountIdentifier,
Amount(amount), // TODO: verify entered amount is valid
"EUR", // TODO: add input field for currency
@ -119,13 +118,13 @@ fun TransferMoneyDialog(
Select(
"Konto",
accountsSupportingTransferringMoney, senderAccount, { senderAccount = it },
{ account -> "${accountsToUserAccount[account]?.displayName} ${account.displayName}" },
leadingIcon = { BankIcon(accountsToUserAccount[senderAccount]) }
{ account -> "${accountsToUser[account]?.displayName} ${account.displayName}" },
leadingIcon = { BankIcon(accountsToUser[senderAccount]) }
) { account ->
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
BankIcon(accountsToUserAccount[account], Modifier.padding(end = 6.dp))
BankIcon(accountsToUser[account], Modifier.padding(end = 6.dp))
Text("${accountsToUserAccount[account]?.displayName} ${account.displayName}")
Text("${accountsToUser[account]?.displayName} ${account.displayName}")
}
}
}

View File

@ -48,7 +48,7 @@ class BankingService(
suspend fun init() {
try {
uiState.userAccounts.value = getAllUserAccounts()
uiState.users.value = getAllUsers()
uiState.transactions.value = getAllAccountTransactionsAsViewModel()
} catch (e: Throwable) {

View File

@ -1,2 +0,0 @@
ALTER TABLE AccountTransaction
RENAME COLUMN reference TO unparsedReference;

View File

@ -1,4 +0,0 @@
import kotlin.Boolean;
ALTER TABLE BankAccount
ADD COLUMN haveAllTransactionsBeenRetrieved INTEGER AS Boolean NOT NULL DEFAULT 0;

View File

@ -1,5 +0,0 @@
ALTER TABLE BankAccount
ADD COLUMN lastTransactionsRetrievalTime TEXT;
ALTER TABLE BankAccount
DROP COLUMN haveAllTransactionsBeenRetrieved;

View File

@ -1,34 +1,39 @@
import kotlin.Boolean;
CREATE TABLE IF NOT EXISTS AccountTransaction (
id INTEGER PRIMARY KEY AUTOINCREMENT,
userAccountId INTEGER NOT NULL,
userId INTEGER NOT NULL,
bankAccountId INTEGER NOT NULL,
amount TEXT NOT NULL,
currency TEXT NOT NULL,
unparsedReference TEXT,
reference TEXT,
bookingDate TEXT NOT NULL,
valueDate TEXT NOT NULL,
otherPartyName TEXT,
otherPartyBankCode TEXT,
otherPartyBankId TEXT,
otherPartyAccountId TEXT,
bookingText TEXT,
postingText TEXT,
openingBalance TEXT,
closingBalance TEXT,
userSetDisplayName TEXT,
category TEXT,
notes TEXT,
statementNumber INTEGER,
sequenceNumber INTEGER,
openingBalance TEXT,
closingBalance TEXT,
sheetNumber INTEGER,
customerReference TEXT,
bankReference TEXT,
furtherInformation TEXT,
endToEndReference TEXT,
customerReference TEXT,
mandateReference TEXT,
creditorIdentifier TEXT,
originatorsIdentificationCode TEXT,
@ -39,40 +44,47 @@ CREATE TABLE IF NOT EXISTS AccountTransaction (
deviantRecipient TEXT,
referenceWithNoSpecialType TEXT,
primaNotaNumber TEXT,
textKeySupplement TEXT,
journalNumber TEXT,
textKeyAddition TEXT,
referenceForTheAccountOwner TEXT,
referenceOfTheAccountServicingInstitution TEXT,
supplementaryDetails TEXT,
orderReferenceNumber TEXT,
referenceNumber TEXT,
transactionReferenceNumber TEXT,
relatedReferenceNumber TEXT
isReversal INTEGER AS Boolean NOT NULL
);
insertTransaction:
INSERT INTO AccountTransaction(
userAccountId, bankAccountId,
userId, bankAccountId,
amount, currency, unparsedReference,
amount, currency, reference,
bookingDate, valueDate,
otherPartyName, otherPartyBankCode, otherPartyAccountId,
bookingText,
otherPartyName, otherPartyBankId, otherPartyAccountId,
postingText,
openingBalance, closingBalance,
userSetDisplayName, category, notes,
statementNumber, sequenceNumber,
openingBalance, closingBalance,
statementNumber, sheetNumber,
endToEndReference, customerReference, mandateReference,
customerReference, bankReference,
furtherInformation,
endToEndReference, mandateReference,
creditorIdentifier, originatorsIdentificationCode,
compensationAmount, originalAmount,
deviantOriginator, deviantRecipient,
referenceWithNoSpecialType,
primaNotaNumber, textKeySupplement,
transactionReferenceNumber, relatedReferenceNumber
journalNumber, textKeyAddition,
orderReferenceNumber, referenceNumber,
isReversal
)
VALUES(
?, ?,
@ -82,19 +94,27 @@ VALUES(
?, ?, ?,
?,
?, ?,
?, ?, ?,
?, ?,
?, ?,
?,
?, ?,
?, ?,
?, ?, ?,
?, ?,
?, ?,
?, ?,
?,
?, ?,
?, ?
?, ?,
?
);
@ -103,13 +123,13 @@ SELECT AccountTransaction.*
FROM AccountTransaction;
selectAllTransactionsAsViewModel:
SELECT id, userAccountId, bankAccountId, amount, currency, unparsedReference, valueDate, otherPartyName, bookingText, userSetDisplayName, category
SELECT id, userId, bankAccountId, amount, currency, reference, valueDate, otherPartyName, postingText, userSetDisplayName, category
FROM AccountTransaction;
selectAllTransactionsOfUser:
SELECT AccountTransaction.*
FROM AccountTransaction WHERE userAccountId = ?;
FROM AccountTransaction WHERE userId = ?;
getTransactionWithId:
SELECT AccountTransaction.*

View File

@ -1,6 +1,6 @@
import kotlin.Boolean;
CREATE TABLE IF NOT EXISTS UserAccount (
CREATE TABLE IF NOT EXISTS User (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bankCode TEXT NOT NULL,
@ -18,17 +18,19 @@ CREATE TABLE IF NOT EXISTS UserAccount (
selectedTanMediumName TEXT,
bankingGroup TEXT,
iconUrl TEXT,
wrongCredentialsEntered INTEGER AS Boolean NOT NULL,
serverAddress TEXT,
userSetDisplayName TEXT,
displayIndex INTEGER NOT NULL
displayIndex INTEGER NOT NULL,
iconUrl TEXT,
wrongCredentialsEntered INTEGER AS Boolean NOT NULL
);
insertUser:
INSERT INTO UserAccount(
INSERT INTO User(
bankCode, loginName, password,
bankName, bic,
@ -39,10 +41,13 @@ INSERT INTO UserAccount(
selectedTanMediumName,
bankingGroup, iconUrl,
bankingGroup,
serverAddress,
wrongCredentialsEntered,
userSetDisplayName, displayIndex
userSetDisplayName, displayIndex,
iconUrl,
wrongCredentialsEntered
)
VALUES(
?, ?, ?,
@ -53,37 +58,38 @@ VALUES(
?,
?, ?,
?,
?, ?,
?, ?
);
selectAllUsers:
SELECT UserAccount.*
FROM UserAccount;
SELECT User.*
FROM User;
CREATE TABLE IF NOT EXISTS BankAccount (
id INTEGER PRIMARY KEY AUTOINCREMENT,
userAccountId INTEGER NOT NULL,
userId INTEGER NOT NULL,
identifier TEXT NOT NULL,
subAccountNumber TEXT,
iban TEXT,
productName TEXT,
accountHolderName TEXT NOT NULL,
type TEXT NOT NULL,
iban TEXT,
subAccountNumber TEXT,
productName TEXT,
currency TEXT NOT NULL,
accountLimit TEXT,
balance TEXT NOT NULL,
isAccountTypeSupportedByApplication INTEGER AS Boolean NOT NULL,
features TEXT NOT NULL,
countDaysForWhichTransactionsAreKept INTEGER,
balance TEXT NOT NULL,
serverTransactionsRetentionDays INTEGER,
lastTransactionsRetrievalTime TEXT,
retrievedTransactionsFrom TEXT,
@ -97,7 +103,7 @@ CREATE TABLE IF NOT EXISTS BankAccount (
insertBankAccount:
INSERT INTO BankAccount(
userAccountId,
userId,
identifier, accountHolderName, type,
iban, subAccountNumber, productName, currency, accountLimit,
@ -106,7 +112,7 @@ INSERT INTO BankAccount(
isAccountTypeSupportedByApplication, features,
countDaysForWhichTransactionsAreKept, lastTransactionsRetrievalTime, retrievedTransactionsFrom,
serverTransactionsRetentionDays, lastTransactionsRetrievalTime, retrievedTransactionsFrom,
userSetDisplayName, displayIndex,

View File

@ -17,39 +17,39 @@ class SqliteBankingRepositoryTest {
}
private val underTest = object : SqliteBankingRepository(sqlDriver) {
override public suspend fun persistTransaction(userAccountId: Long, bankAccountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
super.persistTransaction(userAccountId, bankAccountId, transaction)
override public suspend fun persistTransaction(userId: Long, bankAccountId: Long, transaction: AccountTransaction): AccountTransactionEntity =
super.persistTransaction(userId, bankAccountId, transaction)
}
@Test
fun saveUserAccount() = runTest {
fun saveUser() = runTest {
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)
)
val userAccount = User("12345678", "SupiDupiUser", "geheim", "Abzock-Bank", "ABCDDEBBXXX", "Monika Tester", accounts = bankAccounts, bankingGroup = BankingGroup.DKB, serverAddress = "").apply {
val user = User("12345678", "SupiDupiUser", "geheim", "Abzock-Bank", "ABCDDEBBXXX", "Monika Tester", accounts = bankAccounts, bankingGroup = BankingGroup.DKB, serverAddress = "").apply {
wrongCredentialsEntered = true
displayIndex = 99
}
val persisted = underTest.persistUser(userAccount)
val persisted = underTest.persistUser(user)
assertNotNull(persisted.id)
assertEquals(userAccount.bankCode, persisted.bankCode)
assertEquals(userAccount.loginName, persisted.loginName)
assertEquals(userAccount.password, persisted.password)
assertEquals(user.bankCode, persisted.bankCode)
assertEquals(user.loginName, persisted.loginName)
assertEquals(user.password, persisted.password)
assertEquals(userAccount.bankName, persisted.bankName)
assertEquals(userAccount.bic, persisted.bic)
assertEquals(user.bankName, persisted.bankName)
assertEquals(user.bic, persisted.bic)
assertEquals(userAccount.customerName, persisted.customerName)
assertEquals(userAccount.userId, persisted.userId)
assertEquals(user.customerName, persisted.customerName)
assertEquals(user.userId, persisted.userId)
assertEquals(userAccount.bankingGroup, persisted.bankingGroup)
assertEquals(user.bankingGroup, persisted.bankingGroup)
assertEquals(userAccount.wrongCredentialsEntered, persisted.wrongCredentialsEntered)
assertEquals(userAccount.displayIndex, persisted.displayIndex)
assertEquals(user.wrongCredentialsEntered, persisted.wrongCredentialsEntered)
assertEquals(user.displayIndex, persisted.displayIndex)
assertEquals(1, persisted.accounts.size)