Added IBankingPersistence to be able to store and deserialize added accounts and retrieved account transactions. Implemented BankingPersistenceJson not meant to be a real IBankingPersistence implementation but to get one up and running fast.
This commit is contained in:
parent
ab61064d35
commit
0c8870446e
|
@ -19,9 +19,11 @@ def title = 'Banking'
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(':BankingJavaFxControls')
|
implementation project(':BankingJavaFxControls')
|
||||||
|
|
||||||
api project(':fints4javaBankingClient')
|
implementation project(':fints4javaBankingClient')
|
||||||
|
|
||||||
|
implementation project(':BankingPersistenceJson')
|
||||||
|
|
||||||
implementation "ch.qos.logback:logback-classic:$logbackVersion"
|
implementation "ch.qos.logback:logback-classic:$logbackVersion"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.dankito.banking.javafx.dialogs.mainwindow
|
||||||
|
|
||||||
import javafx.scene.control.SplitPane
|
import javafx.scene.control.SplitPane
|
||||||
import net.dankito.banking.fints4javaBankingClientCreator
|
import net.dankito.banking.fints4javaBankingClientCreator
|
||||||
|
import net.dankito.banking.persistence.BankingPersistenceJson
|
||||||
import net.dankito.banking.ui.javafx.RouterJavaFx
|
import net.dankito.banking.ui.javafx.RouterJavaFx
|
||||||
import net.dankito.banking.ui.javafx.controls.AccountTransactionsView
|
import net.dankito.banking.ui.javafx.controls.AccountTransactionsView
|
||||||
import net.dankito.banking.ui.javafx.controls.AccountsView
|
import net.dankito.banking.ui.javafx.controls.AccountsView
|
||||||
|
@ -10,11 +11,12 @@ import net.dankito.banking.ui.javafx.util.Base64ServiceJava8
|
||||||
import net.dankito.banking.ui.presenter.MainWindowPresenter
|
import net.dankito.banking.ui.presenter.MainWindowPresenter
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import tornadofx.FX.Companion.messages
|
import tornadofx.FX.Companion.messages
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
class MainWindow : View(messages["application.title"]) {
|
class MainWindow : View(messages["application.title"]) {
|
||||||
|
|
||||||
private val presenter = MainWindowPresenter(fints4javaBankingClientCreator(), Base64ServiceJava8(), RouterJavaFx())
|
private val presenter = MainWindowPresenter(fints4javaBankingClientCreator(), BankingPersistenceJson(File("data/accounts.json")), Base64ServiceJava8(), RouterJavaFx())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ open class AccountTransactionsView(private val presenter: MainWindowPresenter) :
|
||||||
|
|
||||||
protected val balance = SimpleStringProperty("")
|
protected val balance = SimpleStringProperty("")
|
||||||
|
|
||||||
protected val transactionsToDisplay = FXCollections.observableArrayList<AccountTransaction>(presenter.allTransactions)
|
protected val transactionsToDisplay = FXCollections.observableArrayList<AccountTransaction>(listOf())
|
||||||
|
|
||||||
|
|
||||||
protected var currentMenu: ContextMenu? = null
|
protected var currentMenu: ContextMenu? = null
|
||||||
|
@ -37,6 +37,8 @@ open class AccountTransactionsView(private val presenter: MainWindowPresenter) :
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionsFilter.addListener { _, _, newValue -> updateTransactionsToDisplay(newValue) }
|
transactionsFilter.addListener { _, _, newValue -> updateTransactionsToDisplay(newValue) }
|
||||||
|
|
||||||
|
handleSelectedBankAccountsChanged(presenter.selectedBankAccounts) // so that isAccountSelected and transactionsToDisplay get set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.dankito.banking.persistence
|
||||||
|
|
||||||
|
import net.dankito.banking.ui.model.Account
|
||||||
|
|
||||||
|
|
||||||
|
interface IBankingPersistence {
|
||||||
|
|
||||||
|
fun saveOrUpdateAccount(account: Account, allAccounts: List<Account>)
|
||||||
|
|
||||||
|
fun readPersistedAccounts(): List<Account>
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,14 @@
|
||||||
package net.dankito.banking.ui.model
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators
|
||||||
import net.dankito.banking.ui.model.tan.TanMedium
|
import net.dankito.banking.ui.model.tan.TanMedium
|
||||||
import net.dankito.banking.ui.model.tan.TanMediumStatus
|
import net.dankito.banking.ui.model.tan.TanMediumStatus
|
||||||
import net.dankito.banking.ui.model.tan.TanProcedure
|
import net.dankito.banking.ui.model.tan.TanProcedure
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator::class) // to avoid stack overflow due to circular references // TODO: remove again, add custom domain object
|
||||||
open class Account(
|
open class Account(
|
||||||
val bank: Bank,
|
val bank: Bank,
|
||||||
val customerId: String,
|
val customerId: String,
|
||||||
|
@ -15,6 +18,7 @@ open class Account(
|
||||||
var bankAccounts: List<BankAccount> = listOf()
|
var bankAccounts: List<BankAccount> = listOf()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
internal constructor() : this(Bank(), "", "", "") // for object deserializers
|
internal constructor() : this(Bank(), "", "", "") // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package net.dankito.banking.ui.model
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator::class) // to avoid stack overflow due to circular references // TODO: remove again, add custom domain object
|
||||||
open class AccountTransaction(
|
open class AccountTransaction(
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val bookingDate: Date,
|
val bookingDate: Date,
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package net.dankito.banking.ui.model
|
package net.dankito.banking.ui.model
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIdentityInfo
|
||||||
|
import com.fasterxml.jackson.annotation.ObjectIdGenerators
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
||||||
|
@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator::class) // to avoid stack overflow due to circular references // TODO: remove again, add custom domain object
|
||||||
open class BankAccount @JvmOverloads constructor(
|
open class BankAccount @JvmOverloads constructor(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
val identifier: String,
|
val identifier: String,
|
||||||
|
|
|
@ -6,6 +6,10 @@ open class TanMedium(
|
||||||
val status: TanMediumStatus
|
val status: TanMediumStatus
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this("", TanMediumStatus.Available) // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$displayName $status"
|
return "$displayName $status"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ open class TanProcedure(
|
||||||
val bankInternalProcedureCode: String
|
val bankInternalProcedureCode: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$displayName ($type, ${bankInternalProcedureCode})"
|
return "$displayName ($type, ${bankInternalProcedureCode})"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.dankito.banking.ui.presenter
|
package net.dankito.banking.ui.presenter
|
||||||
|
|
||||||
|
import net.dankito.banking.persistence.IBankingPersistence
|
||||||
import net.dankito.banking.ui.BankingClientCallback
|
import net.dankito.banking.ui.BankingClientCallback
|
||||||
import net.dankito.banking.ui.IBankingClient
|
import net.dankito.banking.ui.IBankingClient
|
||||||
import net.dankito.banking.ui.IBankingClientCreator
|
import net.dankito.banking.ui.IBankingClientCreator
|
||||||
|
@ -21,7 +22,9 @@ import net.dankito.fints.model.BankInfo
|
||||||
import net.dankito.utils.IThreadPool
|
import net.dankito.utils.IThreadPool
|
||||||
import net.dankito.utils.ThreadPool
|
import net.dankito.utils.ThreadPool
|
||||||
import net.dankito.utils.extensions.ofMaxLength
|
import net.dankito.utils.extensions.ofMaxLength
|
||||||
|
import net.dankito.utils.web.client.IWebClient
|
||||||
import net.dankito.utils.web.client.OkHttpWebClient
|
import net.dankito.utils.web.client.OkHttpWebClient
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
@ -29,19 +32,22 @@ import kotlin.collections.ArrayList
|
||||||
|
|
||||||
open class MainWindowPresenter(
|
open class MainWindowPresenter(
|
||||||
protected val bankingClientCreator: IBankingClientCreator,
|
protected val bankingClientCreator: IBankingClientCreator,
|
||||||
|
protected val persister: IBankingPersistence,
|
||||||
protected val base64Service: IBase64Service,
|
protected val base64Service: IBase64Service,
|
||||||
protected val router: IRouter
|
protected val router: IRouter,
|
||||||
|
protected val webClient: IWebClient = OkHttpWebClient(),
|
||||||
|
protected val threadPool: IThreadPool = ThreadPool()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
protected const val OneDayMillis = 24 * 60 * 60 * 1000
|
protected const val OneDayMillis = 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
|
private val log = LoggerFactory.getLogger(MainWindowPresenter::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected val bankFinder: BankFinder = BankFinder()
|
protected val bankFinder: BankFinder = BankFinder()
|
||||||
|
|
||||||
protected val threadPool: IThreadPool = ThreadPool()
|
|
||||||
|
|
||||||
|
|
||||||
protected val clientsForAccounts = mutableMapOf<Account, IBankingClient>()
|
protected val clientsForAccounts = mutableMapOf<Account, IBankingClient>()
|
||||||
|
|
||||||
|
@ -68,19 +74,54 @@ open class MainWindowPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
threadPool.runAsync {
|
||||||
|
readPersistedAccounts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected open fun readPersistedAccounts() {
|
||||||
|
try {
|
||||||
|
val deserializedAccounts = persister.readPersistedAccounts()
|
||||||
|
|
||||||
|
deserializedAccounts.forEach { account ->
|
||||||
|
val bank = account.bank
|
||||||
|
val bankInfo = BankInfo(bank.name, bank.bankCode, bank.bic, "", "", "", bank.finTsServerAddress, "FinTS V3.0", null)
|
||||||
|
|
||||||
|
val newClient = bankingClientCreator.createClient(bankInfo, account.customerId, account.password, webClient, base64Service, threadPool, callback)
|
||||||
|
|
||||||
|
addClientForAccount(account, newClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
callAccountsChangedListeners()
|
||||||
|
|
||||||
|
selectedAllBankAccounts() // TODO: save last selected bank account(s)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error("Could not deserialize persisted accounts with persister $persister", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun addClientForAccount(account: Account, client: IBankingClient) {
|
||||||
|
clientsForAccounts.put(account, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: move BankInfo out of fints4javaLib
|
// TODO: move BankInfo out of fints4javaLib
|
||||||
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, callback: (AddAccountResponse) -> Unit) {
|
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, callback: (AddAccountResponse) -> Unit) {
|
||||||
|
|
||||||
val newClient = bankingClientCreator.createClient(bankInfo, customerId, pin, OkHttpWebClient(), base64Service, threadPool, this.callback)
|
val newClient = bankingClientCreator.createClient(bankInfo, customerId, pin, webClient, base64Service, threadPool, this.callback)
|
||||||
|
|
||||||
newClient.addAccountAsync { response ->
|
newClient.addAccountAsync { response ->
|
||||||
val account = response.account
|
val account = response.account
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
clientsForAccounts.put(account, newClient)
|
addClientForAccount(account, newClient)
|
||||||
|
|
||||||
callAccountsChangedListeners()
|
callAccountsChangedListeners()
|
||||||
|
|
||||||
|
persistAccount(account)
|
||||||
|
|
||||||
if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) {
|
if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) {
|
||||||
account.bankAccounts.forEach { bankAccount ->
|
account.bankAccounts.forEach { bankAccount ->
|
||||||
retrievedAccountTransactions(bankAccount, response)
|
retrievedAccountTransactions(bankAccount, response)
|
||||||
|
@ -155,6 +196,12 @@ open class MainWindowPresenter(
|
||||||
response.balances.forEach { entry ->
|
response.balances.forEach { entry ->
|
||||||
entry.key.balance = entry.value
|
entry.key.balance = entry.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
persistAccount(bankAccount.account)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun persistAccount(account: Account) {
|
||||||
|
persister.saveOrUpdateAccount(account, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,21 +48,23 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(':BankingUiCommon')
|
implementation project(':BankingUiCommon')
|
||||||
|
|
||||||
implementation project(':fints4javaBankingClient')
|
implementation project(':fints4javaBankingClient')
|
||||||
|
|
||||||
|
implementation project(':BankingPersistenceJson')
|
||||||
|
|
||||||
implementation "com.github.clans:fab:$clansFloatingActionButtonVersion"
|
implementation "com.github.clans:fab:$clansFloatingActionButtonVersion"
|
||||||
|
|
||||||
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||||
|
|
||||||
api "net.dankito.utils:android-utils:$androidUtilsVersion", {
|
implementation "net.dankito.utils:android-utils:$androidUtilsVersion", {
|
||||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
|
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
|
||||||
exclude group: 'com.android.support', module: 'appcompat-v7'
|
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||||
exclude group: 'com.android.support', module: 'design'
|
exclude group: 'com.android.support', module: 'design'
|
||||||
}
|
}
|
||||||
|
|
||||||
api "org.slf4j:slf4j-android:$slf4JVersion"
|
implementation "org.slf4j:slf4j-android:$slf4JVersion"
|
||||||
|
|
||||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||||
implementation 'com.android.support:design:28.0.0'
|
implementation 'com.android.support:design:28.0.0'
|
||||||
|
|
|
@ -18,9 +18,11 @@ import kotlinx.android.synthetic.main.action_view_account_menu_item.view.*
|
||||||
import net.dankito.banking.fints4java.android.ui.views.MainActivityFloatingActionMenuButton
|
import net.dankito.banking.fints4java.android.ui.views.MainActivityFloatingActionMenuButton
|
||||||
import net.dankito.banking.fints4java.android.util.Base64ServiceAndroid
|
import net.dankito.banking.fints4java.android.util.Base64ServiceAndroid
|
||||||
import net.dankito.banking.fints4javaBankingClientCreator
|
import net.dankito.banking.fints4javaBankingClientCreator
|
||||||
|
import net.dankito.banking.persistence.BankingPersistenceJson
|
||||||
import net.dankito.banking.ui.model.Account
|
import net.dankito.banking.ui.model.Account
|
||||||
import net.dankito.banking.ui.presenter.MainWindowPresenter
|
import net.dankito.banking.ui.presenter.MainWindowPresenter
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
@ -37,12 +39,14 @@ class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton
|
private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton
|
||||||
|
|
||||||
|
|
||||||
val presenter = MainWindowPresenter(fints4javaBankingClientCreator(), Base64ServiceAndroid(), RouterAndroid(this))
|
lateinit var presenter: MainWindowPresenter
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
presenter = MainWindowPresenter(fints4javaBankingClientCreator(), BankingPersistenceJson(File(this.filesDir, "data/accounts.json")), Base64ServiceAndroid(), RouterAndroid(this))
|
||||||
|
|
||||||
initUi()
|
initUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# BankingPersistenceJson
|
||||||
|
|
||||||
|
A simple IBankingPersistence implementation based on Json.
|
||||||
|
|
||||||
|
Not meant to be a real or useful implementation. Just there to get you up and running fast.
|
||||||
|
|
||||||
|
Preferably use another IBankingPersistence implementation, e.g. a JPA based one.
|
|
@ -0,0 +1,20 @@
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
|
||||||
|
sourceCompatibility = "1.7"
|
||||||
|
targetCompatibility = "1.7"
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.6"
|
||||||
|
}
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.6"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':BankingUiCommon')
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package net.dankito.banking.persistence
|
||||||
|
|
||||||
|
import net.dankito.banking.ui.model.Account
|
||||||
|
import net.dankito.utils.serialization.ISerializer
|
||||||
|
import net.dankito.utils.serialization.JacksonJsonSerializer
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
open class BankingPersistenceJson(
|
||||||
|
protected val jsonFile: File,
|
||||||
|
protected val serializer: ISerializer = JacksonJsonSerializer()
|
||||||
|
) : IBankingPersistence {
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
jsonFile.absoluteFile.parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun saveOrUpdateAccount(account: Account, allAccounts: List<Account>) {
|
||||||
|
serializer.serializeObject(allAccounts, jsonFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readPersistedAccounts(): List<Account> {
|
||||||
|
return serializer.deserializeListOr(jsonFile, Account::class.java, listOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue