From d7bdd1ae51a17b6f21f7b6ce891a8f1474bb0136 Mon Sep 17 00:00:00 2001 From: dankl Date: Wed, 8 Jan 2020 20:36:56 +0100 Subject: [PATCH] Added ddAccountDialog and started AccountsView --- .../javafx/dialogs/mainwindow/MainWindow.kt | 16 +- .../src/main/resources/Messages.properties | 24 +- .../src/main/resources/Messages_de.properties | 24 +- .../dankito/banking/ui/javafx/RouterJavaFx.kt | 5 +- .../ui/javafx/controls/AccountsView.kt | 43 ++++ .../ui/javafx/dialogs/AddAccountDialog.kt | 231 ++++++++++++++++++ 6 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsView.kt create mode 100755 BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt diff --git a/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt b/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt index bf3cd7f6..90a09c4c 100755 --- a/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt +++ b/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt @@ -2,13 +2,12 @@ package net.dankito.banking.javafx.dialogs.mainwindow import net.dankito.banking.fints4javaBankingClientCreator import net.dankito.banking.ui.javafx.RouterJavaFx +import net.dankito.banking.ui.javafx.controls.AccountsView import net.dankito.banking.ui.javafx.dialogs.mainwindow.controls.MainMenuBar import net.dankito.banking.ui.javafx.util.Base64ServiceJava8 import net.dankito.banking.ui.presenter.MainWindowPresenter +import tornadofx.* import tornadofx.FX.Companion.messages -import tornadofx.View -import tornadofx.borderpane -import tornadofx.get class MainWindow : View(messages["main.window.title"]) { @@ -16,12 +15,23 @@ class MainWindow : View(messages["main.window.title"]) { private val presenter = MainWindowPresenter(fints4javaBankingClientCreator(), Base64ServiceJava8(), RouterJavaFx()) + private var accountsView = AccountsView(presenter) + + override val root = borderpane { prefHeight = 620.0 prefWidth = 1150.0 top = MainMenuBar().root + + center { + splitpane { + setDividerPosition(0, 0.2) + + add(accountsView) + } + } } } \ No newline at end of file diff --git a/BankingJavaFxApp/src/main/resources/Messages.properties b/BankingJavaFxApp/src/main/resources/Messages.properties index 0b9f02dc..80237ac2 100755 --- a/BankingJavaFxApp/src/main/resources/Messages.properties +++ b/BankingJavaFxApp/src/main/resources/Messages.properties @@ -1,4 +1,26 @@ main.window.title=Banking main.window.menu.file=File -main.window.menu.file.quit=Quit \ No newline at end of file +main.window.menu.file.quit=Quit + + +ok=OK +cancel=Cancel +close=Close + +accounts=Accounts +check=Check + + +accounts.view.context.menu.info=Info + + +add.account.dialog.title=Add account +add.account.dialog.bank.code.label=Bank code +add.account.dialog.customer.id.and.password.hint=Enter the same values here as in your online banking portal +add.account.dialog.customer.id=Customer Id (Account number) +add.account.dialog.customer.id.hint=The user name you use for logging in into online banking +add.account.dialog.password=Online banking password +add.account.dialog.password.hint=The password you use for logging in into online banking +add.account.dialog.could.not.add.account=Could not add account for bank code '%s' and customer id '%s': %s +add.account.dialog.add.account.success=Successfully added accounterror.message.could.not.retrieve.accounting.entries=Could not get entries for account '%s': %s \ No newline at end of file diff --git a/BankingJavaFxApp/src/main/resources/Messages_de.properties b/BankingJavaFxApp/src/main/resources/Messages_de.properties index ed4c040c..9f48d322 100755 --- a/BankingJavaFxApp/src/main/resources/Messages_de.properties +++ b/BankingJavaFxApp/src/main/resources/Messages_de.properties @@ -1,4 +1,26 @@ main.window.title=Banking main.window.menu.file=Datei -main.window.menu.file.quit=Beenden \ No newline at end of file +main.window.menu.file.quit=Beenden + + +ok=OK +cancel=Abbrechen +close=Schließen + +accounts=Konten +check=Überprüfen + + +accounts.view.context.menu.info=Info + + +add.account.dialog.title=Konto hinzufügen +add.account.dialog.bank.code.label=Bankleitzahl +add.account.dialog.customer.id.and.password.hint=Geben Sie hier die selben Werte ein wie auf Ihrer Online Banking Webseite +add.account.dialog.customer.id=Kundennummer (Kontonummer) +add.account.dialog.customer.id.hint=Der Nutzernamen den Sie fürs Online Banking verwenden +add.account.dialog.password=Online Banking Passwort +add.account.dialog.password.hint=Das Passwort das Sie fürs Online Banking verwenden +add.account.dialog.could.not.add.account=Konnte Kontoinformation zu Bankleitzahl '%s' und Nutzername '%s' nicht finden: %s +add.account.dialog.add.account.success=Hinzufügen des Kontos war erfolgreich. \ No newline at end of file diff --git a/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt index 97afce95..e5c15021 100644 --- a/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt +++ b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt @@ -1,18 +1,21 @@ package net.dankito.banking.ui.javafx import net.dankito.banking.ui.IRouter +import net.dankito.banking.ui.javafx.dialogs.AddAccountDialog import net.dankito.banking.ui.model.Account import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.tan.TanChallenge import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium import net.dankito.banking.ui.presenter.MainWindowPresenter +import tornadofx.FX.Companion.messages +import tornadofx.get open class RouterJavaFx : IRouter { override fun showAddAccountDialog(presenter: MainWindowPresenter) { - + AddAccountDialog(presenter).show(messages["add.account.dialog.title"]) } override fun getTanFromUserFromNonUiThread(account: Account, tanChallenge: TanChallenge, presenter: MainWindowPresenter): EnterTanResult { diff --git a/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsView.kt b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsView.kt new file mode 100644 index 00000000..611ce6b9 --- /dev/null +++ b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsView.kt @@ -0,0 +1,43 @@ +package net.dankito.banking.ui.javafx.controls + +import javafx.geometry.Pos +import net.dankito.banking.ui.presenter.MainWindowPresenter +import net.dankito.utils.javafx.ui.extensions.fixedHeight +import net.dankito.utils.javafx.ui.extensions.fixedWidth +import tornadofx.* + + +open class AccountsView(protected val presenter: MainWindowPresenter) : View() { + + + override val root = vbox { + borderpane { + fixedHeight = 36.0 + + left = label(messages["accounts"]) { + borderpaneConstraints { + alignment = Pos.CENTER_LEFT + marginLeft = 4.0 + } + } + + right = button("+") { + fixedHeight = 32.0 + fixedWidth = 32.0 + + action { showAddAccountDialog() } + + borderpaneConstraints { + alignment = Pos.CENTER_RIGHT + marginTopBottom(2.0) + } + } + } + + } + + private fun showAddAccountDialog() { + presenter.showAddAccountDialog() + } + +} \ No newline at end of file diff --git a/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt new file mode 100755 index 00000000..ad568fda --- /dev/null +++ b/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/AddAccountDialog.kt @@ -0,0 +1,231 @@ +package net.dankito.banking.ui.javafx.dialogs + +import javafx.beans.property.SimpleBooleanProperty +import javafx.beans.property.SimpleStringProperty +import javafx.geometry.Insets +import javafx.geometry.Pos +import javafx.scene.control.Tooltip +import javafx.scene.paint.Color +import javafx.scene.text.Font +import javafx.scene.text.FontWeight +import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.presenter.MainWindowPresenter +import net.dankito.fints.model.BankInfo +import net.dankito.utils.javafx.ui.controls.UpdateButton +import net.dankito.utils.javafx.ui.dialogs.Window +import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible +import net.dankito.utils.javafx.ui.extensions.setBackgroundToColor +import tornadofx.* + + +open class AddAccountDialog(protected val presenter: MainWindowPresenter) : Window() { + + companion object { + private val LabelMargins = Insets(6.0, 4.0, 6.0, 4.0) + + private val TextFieldHeight = 36.0 + private val TextFieldMargins = Insets(0.0, 4.0, 12.0, 4.0) + + private val ButtonHeight = 40.0 + private val ButtonWidth = 150.0 + } + + + private val bankCode = SimpleStringProperty("") + + protected var selectedBank: BankInfo? = null + + private val customerId = SimpleStringProperty("") + + private val password = SimpleStringProperty("") + + private val requiredDataHasBeenEntered = SimpleBooleanProperty(false) + + + private val checkEnteredCredentialsResult = SimpleStringProperty("") + + private val isEnteredCredentialsResultVisible = SimpleBooleanProperty(false) + + private val didEnteredCredentialsMatch = SimpleBooleanProperty(false) + + + private val checkCredentialsButton = UpdateButton(messages["check"]) + + + init { + bankCode.addListener { _, _, newValue -> checkIsEnteredBankCodeValid(newValue) } + + customerId.addListener { _, _, _ -> checkIfRequiredDataHasBeenEntered() } + + password.addListener { _, _, _ -> checkIfRequiredDataHasBeenEntered() } + } + + + override val root = vbox { + prefWidth = 350.0 + + label(messages["add.account.dialog.bank.code.label"]) { + vboxConstraints { + margin = LabelMargins + } + } + + textfield(bankCode) { + prefHeight = TextFieldHeight + + vboxConstraints { + margin = TextFieldMargins + } + } + + label(messages["add.account.dialog.customer.id.and.password.hint"]) { + font = Font.font(this.font.name, FontWeight.BOLD, this.font.size + 1) + + isWrapText = true + + vboxConstraints { + marginTop = 12.0 + marginBottom = 6.0 + } + } + + label(messages["add.account.dialog.customer.id"]) { + vboxConstraints { + margin = LabelMargins + } + } + + textfield(customerId) { + promptText = messages["add.account.dialog.customer.id.hint"] + prefHeight = TextFieldHeight + + vboxConstraints { + margin = TextFieldMargins + } + } + + label(messages["add.account.dialog.password"]) { + vboxConstraints { + margin = LabelMargins + } + } + + passwordfield(password) { + promptText = messages["add.account.dialog.password.hint"] + prefHeight = TextFieldHeight + + vboxConstraints { + margin = TextFieldMargins + } + } + + label(checkEnteredCredentialsResult) { + visibleWhen(isEnteredCredentialsResultVisible) + ensureOnlyUsesSpaceIfVisible() + + isWrapText = true + font = Font(font.size + 1) + + paddingAll = 8.0 + + checkEnteredCredentialsResult.addListener { _, _, _ -> + if (didEnteredCredentialsMatch.value) { + setBackgroundToColor(Color.TRANSPARENT) + } + else { + setBackgroundToColor(Color.RED) + } + + tooltip = Tooltip(checkEnteredCredentialsResult.value) + + currentWindow?.sizeToScene() + } + + vboxConstraints { + marginTop = 12.0 + marginBottom = 6.0 + } + } + + hbox { + alignment = Pos.CENTER_RIGHT + + button(messages["cancel"]) { + prefHeight = ButtonHeight + prefWidth = ButtonWidth + + isCancelButton = true + + action { close() } + + hboxConstraints { + margin = Insets(6.0, 0.0, 4.0, 0.0) + } + } + + add(checkCredentialsButton.apply { + prefHeight = ButtonHeight + prefWidth = ButtonWidth + + isDefaultButton = true + + enableWhen(requiredDataHasBeenEntered) + + action { checkEnteredCredentials() } + + hboxConstraints { + margin = Insets(6.0, 4.0, 4.0, 12.0) + } + }) + } + } + + + protected open fun checkIsEnteredBankCodeValid(enteredBankCode: String?) { + enteredBankCode?.let { + val banksSearchResult = presenter.searchBanksByNameBankCodeOrCity(enteredBankCode) + + // TODO: show banksSearchResult in AutoCompleteListView + + val uniqueBankCodes = banksSearchResult.map { it.bankCode }.toSet() + selectedBank = if (uniqueBankCodes.size == 1) banksSearchResult.first() else null + + checkIfRequiredDataHasBeenEntered() + } + } + + protected open fun checkIfRequiredDataHasBeenEntered() { + requiredDataHasBeenEntered.value = selectedBank != null + && selectedBank?.supportsFinTs3_0 == true + && customerId.value.isNotEmpty() // TODO: check if it is of length 10? + && password.value.isNotEmpty() // TODO: check if it is of length 5? + } + + + protected open fun checkEnteredCredentials() { + selectedBank?.let { + presenter.addAccountAsync(it, customerId.value, password.value) { response -> + runLater { handleAddAccountResultOnUiThread(response) } + } + } + } + + protected open fun handleAddAccountResultOnUiThread(response: AddAccountResponse) { + isEnteredCredentialsResultVisible.value = true + didEnteredCredentialsMatch.value = response.isSuccessful + val account = response.account + + // TODO: in case of success show alert to ask if account transactions should get retrieved? + + val message = if (response.isSuccessful) messages["add.account.dialog.add.account.success"] + else String.format(messages["add.account.dialog.could.not.add.account"], + account.bank.bankCode, account.customerId, response.errorToShowToUser) + + checkEnteredCredentialsResult.value = message + + if (response.isSuccessful) { + close() + } + } + +} \ No newline at end of file