Persisting AccountTransactions in db
This commit is contained in:
parent
7495cb89c2
commit
6d35b9c64f
|
@ -22,4 +22,4 @@ xcuserdata
|
||||||
!*.xcworkspace/contents.xcworkspacedata
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
**/xcshareddata/WorkspaceSettings.xcsettings
|
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||||
|
|
||||||
composeApp/data/logs
|
composeApp/data/
|
||||||
|
|
|
@ -11,6 +11,8 @@ plugins {
|
||||||
alias(libs.plugins.compose.compiler)
|
alias(libs.plugins.compose.compiler)
|
||||||
|
|
||||||
alias(libs.plugins.kotlinxSerialization)
|
alias(libs.plugins.kotlinxSerialization)
|
||||||
|
|
||||||
|
alias(libs.plugins.sqldelight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +65,10 @@ kotlin {
|
||||||
implementation(libs.klf)
|
implementation(libs.klf)
|
||||||
implementation(libs.kotlinx.serializable)
|
implementation(libs.kotlinx.serializable)
|
||||||
|
|
||||||
|
implementation(libs.sqldelight.runtime)
|
||||||
|
implementation(libs.sqldelight.coroutines.extensions)
|
||||||
|
implementation(libs.sqldelight.paging.extensions)
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
|
@ -75,20 +81,50 @@ kotlin {
|
||||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commonTest.dependencies {
|
||||||
|
implementation(libs.kotlin.test)
|
||||||
|
}
|
||||||
|
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
implementation(compose.preview)
|
implementation(compose.preview)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
|
|
||||||
|
implementation(libs.sqldelight.android.driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeMain.dependencies {
|
||||||
|
implementation(libs.sqldelight.native.driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmMain.dependencies {
|
||||||
|
implementation(libs.sqldelight.sqlite.driver)
|
||||||
|
}
|
||||||
|
jvmTest.dependencies {
|
||||||
|
implementation(libs.kotlin.test.junit)
|
||||||
}
|
}
|
||||||
|
|
||||||
desktopMain.dependencies {
|
desktopMain.dependencies {
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
implementation(libs.kotlinx.coroutines.swing)
|
implementation(libs.kotlinx.coroutines.swing)
|
||||||
|
|
||||||
|
implementation(libs.sqldelight.sqlite.driver)
|
||||||
|
|
||||||
implementation(libs.logback)
|
implementation(libs.logback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sqldelight {
|
||||||
|
databases {
|
||||||
|
create("BankmeisterDb") {
|
||||||
|
packageName.set("net.codinux.banking.dataaccess")
|
||||||
|
generateAsync.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "net.codinux.banking.ui"
|
namespace = "net.codinux.banking.ui"
|
||||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
|
|
@ -5,11 +5,17 @@ import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import app.cash.sqldelight.async.coroutines.synchronous
|
||||||
|
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
||||||
|
import net.codinux.banking.dataaccess.BankmeisterDb
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
DI.setRepository(AndroidSqliteDriver(BankmeisterDb.Schema.synchronous(), this, "Bankmeister.db"))
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
App()
|
App()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
|
|
||||||
|
interface BankingRepository {
|
||||||
|
|
||||||
|
fun getAllAccountTransactions(): List<AccountTransactionEntity>
|
||||||
|
|
||||||
|
suspend fun persistAccountTransactions(transactions: Collection<AccountTransaction>)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
|
|
||||||
|
class InMemoryBankingRepository(
|
||||||
|
transactions: Collection<AccountTransaction>
|
||||||
|
) : BankingRepository {
|
||||||
|
|
||||||
|
private var nextId = 0L // TODO: make thread-safe
|
||||||
|
|
||||||
|
private val transactions = transactions.map { map(it) }.toMutableList()
|
||||||
|
|
||||||
|
|
||||||
|
override fun getAllAccountTransactions(): List<AccountTransactionEntity> = transactions.toList()
|
||||||
|
|
||||||
|
override suspend fun persistAccountTransactions(transactions: Collection<AccountTransaction>) {
|
||||||
|
this.transactions.addAll(transactions.map { map(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun map(transaction: AccountTransaction) = AccountTransactionEntity(
|
||||||
|
nextId++,
|
||||||
|
transaction.amount, transaction.currency, transaction.reference,
|
||||||
|
transaction.bookingDate, transaction.valueDate,
|
||||||
|
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId,
|
||||||
|
transaction.bookingText
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import net.codinux.banking.dataaccess.entities.AccountTransactionEntity
|
||||||
|
|
||||||
|
class SqliteBankingRepository(
|
||||||
|
sqlDriver: SqlDriver
|
||||||
|
) : BankingRepository {
|
||||||
|
|
||||||
|
private val database = BankmeisterDb(sqlDriver)
|
||||||
|
|
||||||
|
private val accountTransactionQueries = database.accountTransactionQueries
|
||||||
|
|
||||||
|
|
||||||
|
override fun getAllAccountTransactions(): List<AccountTransactionEntity> {
|
||||||
|
return accountTransactionQueries.selectAllTransactions { id, amount, currency, reference, bookingDate, valueDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, userSetDisplayName, notes, information, statementNumber, sequenceNumber, openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, originatorsIdentificationCode, compensationAmount, originalAmount, sepaReference, deviantOriginator, deviantRecipient, referenceWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber ->
|
||||||
|
AccountTransactionEntity(
|
||||||
|
id,
|
||||||
|
Amount(amount), currency, reference,
|
||||||
|
mapToDate(bookingDate), mapToDate(valueDate),
|
||||||
|
otherPartyName, otherPartyBankCode, otherPartyAccountId,
|
||||||
|
bookingText,
|
||||||
|
|
||||||
|
userSetDisplayName, notes,
|
||||||
|
|
||||||
|
information,
|
||||||
|
statementNumber?.toInt(), sequenceNumber?.toInt(),
|
||||||
|
|
||||||
|
mapToAmount(openingBalance), mapToAmount(closingBalance),
|
||||||
|
|
||||||
|
endToEndReference, customerReference, mandateReference,
|
||||||
|
creditorIdentifier, originatorsIdentificationCode,
|
||||||
|
compensationAmount, originalAmount,
|
||||||
|
sepaReference,
|
||||||
|
deviantOriginator, deviantRecipient,
|
||||||
|
referenceWithNoSpecialType, primaNotaNumber,
|
||||||
|
textKeySupplement,
|
||||||
|
|
||||||
|
currencyType, bookingKey,
|
||||||
|
referenceForTheAccountOwner, referenceOfTheAccountServicingInstitution,
|
||||||
|
supplementaryDetails,
|
||||||
|
|
||||||
|
transactionReferenceNumber, relatedReferenceNumber
|
||||||
|
)
|
||||||
|
}.executeAsList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun persistAccountTransactions(transactions: Collection<AccountTransaction>) {
|
||||||
|
transactions.forEach {
|
||||||
|
saveAccountTransaction(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun saveAccountTransaction(transaction: AccountTransaction) {
|
||||||
|
accountTransactionQueries.insertTransaction(
|
||||||
|
transaction.amount.amount, transaction.currency, transaction.reference,
|
||||||
|
mapDate(transaction.bookingDate), mapDate(transaction.valueDate),
|
||||||
|
transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId,
|
||||||
|
transaction.bookingText,
|
||||||
|
|
||||||
|
transaction.userSetDisplayName, transaction.notes,
|
||||||
|
|
||||||
|
transaction.information,
|
||||||
|
transaction.statementNumber?.toLong(), transaction.sequenceNumber?.toLong(),
|
||||||
|
transaction.openingBalance?.amount, transaction.closingBalance?.amount,
|
||||||
|
|
||||||
|
transaction.endToEndReference, transaction.customerReference, transaction.mandateReference,
|
||||||
|
transaction.creditorIdentifier, transaction.originatorsIdentificationCode,
|
||||||
|
transaction.compensationAmount, transaction.originalAmount,
|
||||||
|
transaction.sepaReference,
|
||||||
|
transaction.deviantOriginator, transaction.deviantRecipient,
|
||||||
|
transaction.referenceWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement,
|
||||||
|
|
||||||
|
transaction.currencyType, transaction.bookingKey,
|
||||||
|
transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution,
|
||||||
|
transaction.supplementaryDetails,
|
||||||
|
|
||||||
|
transaction.transactionReferenceNumber, transaction.relatedReferenceNumber
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun mapToAmount(serializedAmount: String?): Amount? = serializedAmount?.let { Amount(it) }
|
||||||
|
|
||||||
|
private fun mapDate(date: LocalDate): String = date.toString()
|
||||||
|
|
||||||
|
private fun mapToDate(serializedDate: String): LocalDate = LocalDate.parse(serializedDate)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package net.codinux.banking.dataaccess.entities
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
|
||||||
|
class AccountTransactionEntity(
|
||||||
|
val id: Long,
|
||||||
|
|
||||||
|
amount: Amount,
|
||||||
|
currency: String,
|
||||||
|
reference: String,
|
||||||
|
|
||||||
|
bookingDate: LocalDate,
|
||||||
|
valueDate: LocalDate,
|
||||||
|
|
||||||
|
otherPartyName: String? = null,
|
||||||
|
otherPartyBankCode: String? = null,
|
||||||
|
otherPartyAccountId: String? = null,
|
||||||
|
|
||||||
|
bookingText: String? = null,
|
||||||
|
|
||||||
|
userSetDisplayName: String? = null,
|
||||||
|
notes: String? = null,
|
||||||
|
information: String? = null,
|
||||||
|
|
||||||
|
statementNumber: Int? = null,
|
||||||
|
sequenceNumber: Int? = null,
|
||||||
|
|
||||||
|
openingBalance: Amount? = null,
|
||||||
|
closingBalance: Amount? = null,
|
||||||
|
|
||||||
|
endToEndReference: String? = null,
|
||||||
|
customerReference: String? = null,
|
||||||
|
mandateReference: String? = null,
|
||||||
|
creditorIdentifier: String? = null,
|
||||||
|
originatorsIdentificationCode: String? = null,
|
||||||
|
compensationAmount: String? = null,
|
||||||
|
originalAmount: String? = null,
|
||||||
|
sepaReference: String? = null,
|
||||||
|
deviantOriginator: String? = null,
|
||||||
|
deviantRecipient: String? = null,
|
||||||
|
referenceWithNoSpecialType: String? = null,
|
||||||
|
primaNotaNumber: String? = null,
|
||||||
|
textKeySupplement: String? = null,
|
||||||
|
|
||||||
|
currencyType: String? = null,
|
||||||
|
bookingKey: String? = null,
|
||||||
|
referenceForTheAccountOwner: String? = null,
|
||||||
|
referenceOfTheAccountServicingInstitution: String? = null,
|
||||||
|
supplementaryDetails: String? = null,
|
||||||
|
|
||||||
|
transactionReferenceNumber: String? = null,
|
||||||
|
relatedReferenceNumber: String? = null
|
||||||
|
) : AccountTransaction(
|
||||||
|
amount, currency, reference,
|
||||||
|
bookingDate, valueDate,
|
||||||
|
otherPartyName, otherPartyBankCode, otherPartyAccountId,
|
||||||
|
bookingText,
|
||||||
|
|
||||||
|
information,
|
||||||
|
statementNumber, sequenceNumber,
|
||||||
|
openingBalance, closingBalance,
|
||||||
|
|
||||||
|
endToEndReference, customerReference, mandateReference,
|
||||||
|
creditorIdentifier, originatorsIdentificationCode,
|
||||||
|
compensationAmount, originalAmount,
|
||||||
|
sepaReference,
|
||||||
|
deviantOriginator, deviantRecipient,
|
||||||
|
referenceWithNoSpecialType, primaNotaNumber,
|
||||||
|
textKeySupplement,
|
||||||
|
|
||||||
|
currencyType, bookingKey,
|
||||||
|
referenceForTheAccountOwner, referenceOfTheAccountServicingInstitution,
|
||||||
|
supplementaryDetails,
|
||||||
|
|
||||||
|
transactionReferenceNumber, relatedReferenceNumber,
|
||||||
|
|
||||||
|
userSetDisplayName, notes
|
||||||
|
)
|
|
@ -14,7 +14,6 @@ import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
|
||||||
import net.codinux.banking.ui.composables.StateHandler
|
import net.codinux.banking.ui.composables.StateHandler
|
||||||
import net.codinux.banking.ui.composables.TransactionsList
|
import net.codinux.banking.ui.composables.TransactionsList
|
||||||
import net.codinux.banking.ui.dialogs.AddAccountDialog
|
import net.codinux.banking.ui.dialogs.AddAccountDialog
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package net.codinux.banking.ui.config
|
package net.codinux.banking.ui.config
|
||||||
|
|
||||||
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
|
import net.codinux.banking.dataaccess.BankingRepository
|
||||||
|
import net.codinux.banking.dataaccess.InMemoryBankingRepository
|
||||||
|
import net.codinux.banking.dataaccess.SqliteBankingRepository
|
||||||
import net.codinux.banking.ui.Platform
|
import net.codinux.banking.ui.Platform
|
||||||
import net.codinux.banking.ui.getPlatform
|
import net.codinux.banking.ui.getPlatform
|
||||||
import net.codinux.banking.ui.service.BankFinder
|
import net.codinux.banking.ui.service.*
|
||||||
import net.codinux.banking.ui.service.BankingService
|
|
||||||
import net.codinux.banking.ui.service.FormatUtil
|
|
||||||
import net.codinux.banking.ui.state.UiState
|
import net.codinux.banking.ui.state.UiState
|
||||||
|
|
||||||
object DI {
|
object DI {
|
||||||
|
@ -13,12 +15,24 @@ object DI {
|
||||||
|
|
||||||
val platform: Platform = getPlatform()
|
val platform: Platform = getPlatform()
|
||||||
|
|
||||||
val bankFinder = BankFinder()
|
|
||||||
|
|
||||||
val bankingService = BankingService(uiState, bankFinder)
|
|
||||||
|
|
||||||
val formatUtil = FormatUtil()
|
val formatUtil = FormatUtil()
|
||||||
|
|
||||||
|
val bankFinder = BankFinder()
|
||||||
|
|
||||||
|
|
||||||
|
var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList())
|
||||||
|
|
||||||
|
val bankingService by lazy {
|
||||||
|
BankingService(uiState, bankingRepository, bankFinder) }
|
||||||
|
|
||||||
|
|
||||||
|
fun setRepository(sqlDriver: SqlDriver) = setRepository(SqliteBankingRepository(sqlDriver))
|
||||||
|
|
||||||
|
fun setRepository(repository: BankingRepository) {
|
||||||
|
this.bankingRepository = repository
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
bankingService.init()
|
bankingService.init()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
import net.codinux.banking.client.model.options.RetrieveTransactions
|
import net.codinux.banking.client.model.options.RetrieveTransactions
|
||||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||||
import net.codinux.banking.client.model.response.*
|
import net.codinux.banking.client.model.response.*
|
||||||
|
import net.codinux.banking.dataaccess.BankingRepository
|
||||||
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||||
import net.codinux.banking.fints.config.FinTsClientOptions
|
import net.codinux.banking.fints.config.FinTsClientOptions
|
||||||
import net.codinux.banking.ui.model.BankInfo
|
import net.codinux.banking.ui.model.BankInfo
|
||||||
|
@ -25,6 +26,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 bankingRepository: BankingRepository,
|
||||||
private val bankFinder: BankFinder
|
private val bankFinder: BankFinder
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -36,9 +38,11 @@ class BankingService(
|
||||||
|
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
val transactions = readTransactionsFromCsv()
|
try {
|
||||||
|
uiState.transactions.value = bankingRepository.getAllAccountTransactions()
|
||||||
uiState.transactions.value = transactions
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Could not read all account transactions from repository" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun findBanks(query: String): List<BankInfo> =
|
suspend fun findBanks(query: String): List<BankInfo> =
|
||||||
|
@ -64,13 +68,21 @@ class BankingService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSuccessfulGetAccountDataResponse(response: GetAccountDataResponse) {
|
private suspend fun handleSuccessfulGetAccountDataResponse(response: GetAccountDataResponse) {
|
||||||
// TODO: save customer
|
// TODO: save customer
|
||||||
|
|
||||||
val transactions = uiState.transactions.value.toMutableList()
|
val transactions = uiState.transactions.value.toMutableList()
|
||||||
transactions.addAll(response.bookedTransactions)
|
transactions.addAll(response.bookedTransactions)
|
||||||
|
|
||||||
uiState.transactions.value = transactions.sortedByDescending { it.valueDate }
|
uiState.transactions.value = transactions.sortedByDescending { it.valueDate }
|
||||||
|
|
||||||
|
try {
|
||||||
|
bankingRepository.persistAccountTransactions(response.bookedTransactions)
|
||||||
|
|
||||||
|
log.info { "Saved ${response.bookedTransactions.size} transactions" }
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
log.error(e) { "Could not save account transactions ${response.bookedTransactions}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUnsuccessfulBankingClientResponse(action: BankingClientAction, response: Response<*>) {
|
private fun handleUnsuccessfulBankingClientResponse(action: BankingClientAction, response: Response<*>) {
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS AccountTransaction (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
|
amount TEXT NOT NULL,
|
||||||
|
currency TEXT NOT NULL,
|
||||||
|
reference TEXT NOT NULL,
|
||||||
|
|
||||||
|
bookingDate TEXT NOT NULL,
|
||||||
|
valueDate TEXT NOT NULL,
|
||||||
|
|
||||||
|
otherPartyName TEXT,
|
||||||
|
otherPartyBankCode TEXT,
|
||||||
|
otherPartyAccountId TEXT,
|
||||||
|
|
||||||
|
bookingText TEXT,
|
||||||
|
|
||||||
|
userSetDisplayName TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
information TEXT,
|
||||||
|
|
||||||
|
statementNumber INTEGER,
|
||||||
|
sequenceNumber INTEGER,
|
||||||
|
|
||||||
|
openingBalance TEXT,
|
||||||
|
closingBalance TEXT,
|
||||||
|
|
||||||
|
endToEndReference TEXT,
|
||||||
|
customerReference TEXT,
|
||||||
|
mandateReference TEXT,
|
||||||
|
creditorIdentifier TEXT,
|
||||||
|
originatorsIdentificationCode TEXT,
|
||||||
|
compensationAmount TEXT,
|
||||||
|
originalAmount TEXT,
|
||||||
|
sepaReference TEXT,
|
||||||
|
deviantOriginator TEXT,
|
||||||
|
deviantRecipient TEXT,
|
||||||
|
referenceWithNoSpecialType TEXT,
|
||||||
|
primaNotaNumber TEXT,
|
||||||
|
textKeySupplement TEXT,
|
||||||
|
|
||||||
|
currencyType TEXT,
|
||||||
|
bookingKey TEXT,
|
||||||
|
referenceForTheAccountOwner TEXT,
|
||||||
|
referenceOfTheAccountServicingInstitution TEXT,
|
||||||
|
supplementaryDetails TEXT,
|
||||||
|
|
||||||
|
transactionReferenceNumber TEXT,
|
||||||
|
relatedReferenceNumber TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
insertTransaction:
|
||||||
|
INSERT INTO AccountTransaction(
|
||||||
|
amount, currency, reference,
|
||||||
|
bookingDate, valueDate,
|
||||||
|
otherPartyName, otherPartyBankCode, otherPartyAccountId,
|
||||||
|
bookingText,
|
||||||
|
|
||||||
|
userSetDisplayName, notes,
|
||||||
|
|
||||||
|
information,
|
||||||
|
statementNumber, sequenceNumber,
|
||||||
|
openingBalance, closingBalance,
|
||||||
|
|
||||||
|
endToEndReference, customerReference, mandateReference,
|
||||||
|
creditorIdentifier, originatorsIdentificationCode,
|
||||||
|
compensationAmount, originalAmount,
|
||||||
|
sepaReference,
|
||||||
|
deviantOriginator, deviantRecipient,
|
||||||
|
referenceWithNoSpecialType,
|
||||||
|
primaNotaNumber, textKeySupplement,
|
||||||
|
|
||||||
|
currencyType, bookingKey,
|
||||||
|
referenceForTheAccountOwner, referenceOfTheAccountServicingInstitution,
|
||||||
|
supplementaryDetails,
|
||||||
|
|
||||||
|
transactionReferenceNumber, relatedReferenceNumber
|
||||||
|
)
|
||||||
|
VALUES(
|
||||||
|
?, ?, ?,
|
||||||
|
?, ?,
|
||||||
|
?, ?, ?,
|
||||||
|
?,
|
||||||
|
|
||||||
|
?, ?,
|
||||||
|
|
||||||
|
?,
|
||||||
|
?, ?,
|
||||||
|
?, ?,
|
||||||
|
|
||||||
|
?, ?, ?,
|
||||||
|
?, ?,
|
||||||
|
?, ?,
|
||||||
|
?,
|
||||||
|
?, ?,
|
||||||
|
?,
|
||||||
|
?, ?,
|
||||||
|
|
||||||
|
?, ?,
|
||||||
|
?, ?,
|
||||||
|
?,
|
||||||
|
|
||||||
|
?, ?
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
selectAllTransactions:
|
||||||
|
SELECT AccountTransaction.*
|
||||||
|
FROM AccountTransaction;
|
|
@ -4,9 +4,15 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.unit.DpSize
|
import androidx.compose.ui.unit.DpSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.*
|
import androidx.compose.ui.window.*
|
||||||
|
import app.cash.sqldelight.async.coroutines.synchronous
|
||||||
|
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
||||||
import bankmeister.composeapp.generated.resources.AppIcon_svg
|
import bankmeister.composeapp.generated.resources.AppIcon_svg
|
||||||
import bankmeister.composeapp.generated.resources.Res
|
import bankmeister.composeapp.generated.resources.Res
|
||||||
|
import net.codinux.banking.dataaccess.BankmeisterDb
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
import net.codinux.log.Log
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
Window(
|
Window(
|
||||||
|
@ -15,6 +21,11 @@ fun main() = application {
|
||||||
icon = painterResource(Res.drawable.AppIcon_svg),
|
icon = painterResource(Res.drawable.AppIcon_svg),
|
||||||
state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(900.dp, 800.dp)),
|
state = WindowState(position = WindowPosition(Alignment.Center), size = DpSize(900.dp, 800.dp)),
|
||||||
) {
|
) {
|
||||||
|
File("data/db").mkdirs()
|
||||||
|
DI.setRepository(JdbcSqliteDriver("jdbc:sqlite:data/db/Bankmeister.db").apply {
|
||||||
|
val schema = BankmeisterDb.Schema.synchronous().create(this)
|
||||||
|
})
|
||||||
|
|
||||||
App()
|
App()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package net.codinux.banking.dataaccess
|
||||||
|
|
||||||
|
import app.cash.sqldelight.async.coroutines.synchronous
|
||||||
|
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
class SqliteBankingRepositoryTest {
|
||||||
|
|
||||||
|
private val sqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
|
||||||
|
BankmeisterDb.Schema.synchronous().create(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val underTest = SqliteBankingRepository(sqlDriver)
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun saveTransaction() = runBlocking {
|
||||||
|
val transaction = AccountTransaction(Amount("12.45"), "EUR", "Lohn", LocalDate(2024, 5, 7), LocalDate(2024, 6, 15), "Dein Boss")
|
||||||
|
|
||||||
|
underTest.persistAccountTransactions(listOf(transaction))
|
||||||
|
|
||||||
|
val result = underTest.getAllAccountTransactions()
|
||||||
|
|
||||||
|
assertEquals(1, result.size)
|
||||||
|
|
||||||
|
val persisted = result.first()
|
||||||
|
assertNotNull(persisted.id)
|
||||||
|
|
||||||
|
assertEquals(transaction.amount, persisted.amount)
|
||||||
|
assertEquals(transaction.currency, persisted.currency)
|
||||||
|
assertEquals(transaction.reference, persisted.reference)
|
||||||
|
assertEquals(transaction.bookingDate, persisted.bookingDate)
|
||||||
|
assertEquals(transaction.valueDate, persisted.valueDate)
|
||||||
|
assertEquals(transaction.otherPartyName, persisted.otherPartyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,13 @@
|
||||||
package net.codinux.banking.ui
|
package net.codinux.banking.ui
|
||||||
|
|
||||||
import androidx.compose.ui.window.ComposeUIViewController
|
import androidx.compose.ui.window.ComposeUIViewController
|
||||||
|
import app.cash.sqldelight.async.coroutines.synchronous
|
||||||
|
import app.cash.sqldelight.driver.native.NativeSqliteDriver
|
||||||
|
import net.codinux.banking.dataaccess.BankmeisterDb
|
||||||
|
import net.codinux.banking.ui.config.DI
|
||||||
|
|
||||||
fun MainViewController() = ComposeUIViewController { App() }
|
fun MainViewController() = ComposeUIViewController {
|
||||||
|
DI.setRepository(NativeSqliteDriver(Database.Schema.synchronous(), "Bankmeister.db"))
|
||||||
|
|
||||||
|
App()
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ kotlinx-serializable = "1.7.1"
|
||||||
klf = "1.6.0"
|
klf = "1.6.0"
|
||||||
logback = "1.5.7"
|
logback = "1.5.7"
|
||||||
|
|
||||||
|
sqlDelight = "2.0.2"
|
||||||
|
|
||||||
agp = "8.2.2"
|
agp = "8.2.2"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "34"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
|
@ -36,6 +38,13 @@ kotlinx-serializable = { group = "org.jetbrains.kotlinx", name = "kotlinx-serial
|
||||||
klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" }
|
klf = { group = "net.codinux.log", name = "klf", version.ref = "klf" }
|
||||||
logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
|
logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
|
||||||
|
|
||||||
|
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqlDelight" }
|
||||||
|
sqldelight-coroutines-extensions = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqlDelight" }
|
||||||
|
sqldelight-paging-extensions = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqlDelight" }
|
||||||
|
sqldelight-sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqlDelight" }
|
||||||
|
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" }
|
||||||
|
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" }
|
||||||
|
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
|
||||||
androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
|
androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
|
||||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" }
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" }
|
||||||
|
@ -58,3 +67,5 @@ jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin
|
||||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
||||||
|
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqlDelight" }
|
Loading…
Reference in New Issue