From f4194f832e8bab6f83f6d4fb67950e3e6a41d747 Mon Sep 17 00:00:00 2001 From: dankl Date: Sun, 3 Nov 2019 23:06:40 +0100 Subject: [PATCH] Started UI specific model to get independent of underlying FinTS- / banking client implementation --- .../net/dankito/banking/ui/model/Account.kt | 34 ++++ .../banking/ui/model/AccountTransaction.kt | 28 ++++ .../java/net/dankito/banking/ui/model/Bank.kt | 22 +++ .../dankito/banking/ui/model/BankAccount.kt | 30 ++++ .../banking/ui/model/BankAccountType.kt | 8 + .../dankito/banking/ui/model/TanProcedure.kt | 14 ++ .../banking/ui/model/TanProcedureType.kt | 19 +++ .../ui/model/responses/AddAccountResponse.kt | 24 +++ .../model/responses/BankingClientResponse.kt | 19 +++ .../responses/GetTransactionsResponse.kt | 15 ++ .../android/mapper/fints4javaModelMapper.kt | 141 +++++++++++++++++ .../android/ui/MainWindowPresenter.kt | 145 ++++++++++++------ .../ui/adapter/AccountTransactionAdapter.kt | 2 +- .../ui/adapter/TanProceduresAdapter.kt | 2 +- .../android/ui/dialogs/AddAccountDialog.kt | 10 +- .../android/ui/dialogs/BankTransferDialog.kt | 12 +- .../android/ui/home/HomeFragment.kt | 15 +- 17 files changed, 472 insertions(+), 68 deletions(-) create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Account.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/AccountTransaction.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Bank.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccount.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccountType.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedure.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedureType.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/AddAccountResponse.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/BankingClientResponse.kt create mode 100644 BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt create mode 100644 fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Account.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Account.kt new file mode 100644 index 00000000..cb362daf --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Account.kt @@ -0,0 +1,34 @@ +package net.dankito.banking.ui.model + +import java.math.BigDecimal + + +open class Account( + val bank: Bank, + val customerId: String, + var pin: String, + var name: String, + var userId: String = customerId, + var bankAccounts: List = listOf() +) { + + internal constructor() : this(Bank(), "", "", "") // for object deserializers + + + var supportedTanProcedures: List = listOf() + + var selectedTanProcedure: TanProcedure? = null + + + val balance: BigDecimal + get() = bankAccounts.map { it.balance }.fold(BigDecimal.ZERO) { acc, e -> acc + e } + + val transactions: List + get() = bankAccounts.flatMap { it.transactions } + + + override fun toString(): String { + return "$name ($customerId)" + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/AccountTransaction.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/AccountTransaction.kt new file mode 100644 index 00000000..96cba179 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/AccountTransaction.kt @@ -0,0 +1,28 @@ +package net.dankito.banking.ui.model + +import java.math.BigDecimal +import java.text.DateFormat +import java.util.* + + +open class AccountTransaction( + val amount: BigDecimal, + val currency: String, + val usage: String, + val bookingDate: Date, + val otherPartyName: String?, + val otherPartyBankCode: String?, + val otherPartyAccountId: String?, + val bookingText: String?, + val bankAccount: BankAccount +) { + + // for object deserializers + internal constructor() : this(0.toBigDecimal(),"", "", Date(), null, null, null, null, BankAccount()) + + + override fun toString(): String { + return "${DateFormat.getDateInstance(DateFormat.MEDIUM).format(bookingDate)} $amount $otherPartyName: $usage" + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Bank.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Bank.kt new file mode 100644 index 00000000..16fd9d07 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/Bank.kt @@ -0,0 +1,22 @@ +package net.dankito.banking.ui.model + + +open class Bank( + val bankCode: String, + var finTsServerAddress: String, + var bic: String, + var name: String +) { + + + internal constructor() : this("", "", "", "") // for object deserializers + + + var iconUrl: String? = null + + + override fun toString(): String { + return "$name ($bankCode)" + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccount.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccount.kt new file mode 100644 index 00000000..b3788357 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccount.kt @@ -0,0 +1,30 @@ +package net.dankito.banking.ui.model + +import java.math.BigDecimal + + +open class BankAccount @JvmOverloads constructor( + val account: Account, + val identifier: String, + var name: String, + var iban: String?, + var subAccountNumber: String?, + var balance: BigDecimal = BigDecimal.ZERO, + var currency: String = "EUR", + var type: BankAccountType = BankAccountType.Giro, + accountTransactions: List = listOf() +) { + + + internal constructor() : this(Account(), "", "", null, null) // for object deserializers + + + var transactions: List = accountTransactions + protected set + + + override fun toString(): String { + return "$name ($identifier)" + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccountType.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccountType.kt new file mode 100644 index 00000000..d383a600 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/BankAccountType.kt @@ -0,0 +1,8 @@ +package net.dankito.banking.ui.model + + +enum class BankAccountType { + + Giro + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedure.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedure.kt new file mode 100644 index 00000000..b0060bad --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedure.kt @@ -0,0 +1,14 @@ +package net.dankito.banking.ui.model + + +open class TanProcedure( + val displayName: String, + val type: TanProcedureType, + val bankInternalProcedureCode: String +) { + + override fun toString(): String { + return "$displayName ($type, ${bankInternalProcedureCode})" + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedureType.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedureType.kt new file mode 100644 index 00000000..527ef040 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/TanProcedureType.kt @@ -0,0 +1,19 @@ +package net.dankito.banking.ui.model + + +enum class TanProcedureType { + + EnterTan, + + ChipTanManuell, + + ChipTanOptisch, + + ChipTanQrCode, + + PhotoTan, + + SmsTan, + + PushTan +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/AddAccountResponse.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/AddAccountResponse.kt new file mode 100644 index 00000000..e85cee80 --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/AddAccountResponse.kt @@ -0,0 +1,24 @@ +package net.dankito.banking.ui.model.responses + +import net.dankito.banking.ui.model.Account +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.BankAccount +import java.math.BigDecimal + + +open class AddAccountResponse( + isSuccessful: Boolean, + errorToShowToUser: String?, + val account: Account, + val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false, + bookedTransactionsOfLast90Days: Map> = mapOf(), + unbookedTransactionsOfLast90Days: Map> = mapOf(), + balances: Map = mapOf() +) + : GetTransactionsResponse(isSuccessful, errorToShowToUser, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balances) { + + override fun toString(): String { + return account.toString() + " " + super.toString() + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/BankingClientResponse.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/BankingClientResponse.kt new file mode 100644 index 00000000..956c884b --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/BankingClientResponse.kt @@ -0,0 +1,19 @@ +package net.dankito.banking.ui.model.responses + + +open class BankingClientResponse( + val isSuccessful: Boolean, + val errorToShowToUser: String? +) { + + + override fun toString(): String { + return if (isSuccessful) { + "Successful" + } + else { + "Error: $errorToShowToUser" + } + } + +} \ No newline at end of file diff --git a/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt new file mode 100644 index 00000000..0fef818b --- /dev/null +++ b/BankingUiCommon/src/main/java/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt @@ -0,0 +1,15 @@ +package net.dankito.banking.ui.model.responses + +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.BankAccount +import java.math.BigDecimal + + +open class GetTransactionsResponse( + isSuccessful: Boolean, + errorToShowToUser: String?, + val bookedTransactions: Map> = mapOf(), + val unbookedTransactions: Map> = mapOf(), + val balances: Map = mapOf() +) + : BankingClientResponse(isSuccessful, errorToShowToUser) diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt new file mode 100644 index 00000000..f9140cbb --- /dev/null +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/mapper/fints4javaModelMapper.kt @@ -0,0 +1,141 @@ +package net.dankito.banking.fints4java.android.mapper + +import net.dankito.banking.ui.model.* +import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.GetTransactionsResponse +import net.dankito.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion +import net.dankito.fints.model.AccountData +import net.dankito.fints.model.BankData +import net.dankito.fints.model.CustomerData +import net.dankito.fints.response.client.FinTsClientResponse +import net.dankito.fints.response.segments.AccountType +import java.math.BigDecimal + + +open class fints4javaModelMapper { + + open fun mapAccount(customer: CustomerData, bank: BankData): Account { + val mappedBank = mapBank(bank) + + val account = Account(mappedBank, customer.customerId, customer.pin, customer.name, customer.userId) + + account.bankAccounts = mapBankAccounts(account, customer.accounts) + account.supportedTanProcedures = mapTanProcedures(customer.supportedTanProcedures) + + return account + } + + open fun mapBank(bank: BankData): Bank { + return Bank(bank.bankCode, bank.finTs3ServerAddress, bank.bic, bank.name) + } + + open fun mapBankAccounts(account: Account, accountData: List): List { + return accountData.map { mapBankAccount(account, it) } + } + + open fun mapBankAccount(account: Account, accountData: AccountData): BankAccount { + return BankAccount(account, accountData.accountIdentifier, accountData.accountHolderName, accountData.iban, + accountData.subAccountAttribute, BigDecimal.ZERO, accountData.currency ?: "EUR", + mapBankAccountType(accountData.accountType)) + } + + open fun mapBankAccountType(type: AccountType?): BankAccountType { + return when (type) { + + else -> BankAccountType.Giro + } + } + + open fun mapTransactions(bankAccount: BankAccount, transactions: List): List { + return transactions.map { mapTransaction(bankAccount, it) } + } + + open fun mapTransaction(bankAccount: BankAccount, transaction: net.dankito.fints.model.AccountTransaction): AccountTransaction { + return AccountTransaction( + transaction.amount, + transaction.currency, + transaction.usage, + transaction.bookingDate, + transaction.otherPartyName, + transaction.otherPartyBankCode, + transaction.otherPartyAccountId, + transaction.bookingText, + bankAccount + ) + } + + open fun mapTanProcedures(tanProcedures: List): List { + return tanProcedures.map { mapTanProcedure(it) } + } + + open fun mapTanProcedure(tanProcedure: net.dankito.fints.model.TanProcedure): TanProcedure { + return TanProcedure( + tanProcedure.displayName, + mapTanProcedureType(tanProcedure.type), + tanProcedure.securityFunction.code + ) + } + + open fun mapTanProcedureType(type: net.dankito.fints.model.TanProcedureType): TanProcedureType { + return when (type) { + net.dankito.fints.model.TanProcedureType.EnterTan -> TanProcedureType.EnterTan // TODO: add ChipTanManuell + net.dankito.fints.model.TanProcedureType.ChipTan -> TanProcedureType.ChipTanOptisch + net.dankito.fints.model.TanProcedureType.ChipTanQrCode -> TanProcedureType.ChipTanQrCode + net.dankito.fints.model.TanProcedureType.PhotoTan -> TanProcedureType.PhotoTan + net.dankito.fints.model.TanProcedureType.SmsTan -> TanProcedureType.SmsTan + net.dankito.fints.model.TanProcedureType.PushTan -> TanProcedureType.PushTan + } + } + + + fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse { + var bookedTransactions = mapOf>() + var balances = mapOf() + + account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse + bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)) + response.balance?.let { balances = mapOf(bankAccount to it) } + } + + return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response), + account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan, + bookedTransactions, + mapOf(), // TODO: map unbooked transactions + balances) + } + + fun mapResponse(account: Account, response: net.dankito.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { + val bankAccount = account.bankAccounts.first() // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse + + return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response), + mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)), + mapOf(), // TODO: map unbooked transactions + response.balance?.let { mapOf(bankAccount to it) } ?: mapOf()) + } + + open fun mapErrorToShowToUser(response: FinTsClientResponse): String? { + return response.exception?.localizedMessage ?: response.errorsToShowToUser.joinToString("\n") + } + + + open fun mapTanProcedureBack(tanProcedure: TanProcedure): net.dankito.fints.model.TanProcedure { + return net.dankito.fints.model.TanProcedure( + tanProcedure.displayName, + Sicherheitsfunktion.values().first { it.code == tanProcedure.bankInternalProcedureCode }, + mapTanProcedureTypeBack(tanProcedure.type) + ) + } + + open fun mapTanProcedureTypeBack(type: TanProcedureType): net.dankito.fints.model.TanProcedureType { + return when (type) { + TanProcedureType.EnterTan -> net.dankito.fints.model.TanProcedureType.EnterTan + TanProcedureType.ChipTanManuell -> net.dankito.fints.model.TanProcedureType.EnterTan // TODO: add ChipTanManuell + TanProcedureType.ChipTanOptisch -> net.dankito.fints.model.TanProcedureType.ChipTan + TanProcedureType.ChipTanQrCode -> net.dankito.fints.model.TanProcedureType.ChipTanQrCode + TanProcedureType.PhotoTan -> net.dankito.fints.model.TanProcedureType.PhotoTan + TanProcedureType.SmsTan -> net.dankito.fints.model.TanProcedureType.SmsTan + TanProcedureType.PushTan -> net.dankito.fints.model.TanProcedureType.PushTan + } + } + +} \ No newline at end of file diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt index c45bf294..ffe7fdcd 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/MainWindowPresenter.kt @@ -1,14 +1,17 @@ package net.dankito.banking.fints4java.android.ui import net.dankito.banking.fints4java.android.Base64ServiceAndroid +import net.dankito.banking.ui.model.Account +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.responses.AddAccountResponse +import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.fints.FinTsClient import net.dankito.fints.FinTsClientCallback import net.dankito.fints.banks.BankFinder import net.dankito.fints.model.* import net.dankito.fints.model.mapper.BankDataMapper -import net.dankito.fints.response.client.AddAccountResponse import net.dankito.fints.response.client.FinTsClientResponse -import net.dankito.fints.response.client.GetTransactionsResponse import net.dankito.utils.IThreadPool import net.dankito.utils.ThreadPool import java.math.BigDecimal @@ -26,18 +29,20 @@ open class MainWindowPresenter(callback: FinTsClientCallback) { protected val bankDataMapper = BankDataMapper() + protected val fints4javaModelMapper = net.dankito.banking.fints4java.android.mapper.fints4javaModelMapper() - protected val accounts = mutableMapOf() - protected val bookedTransactions = mutableMapOf>() + protected val accounts = mutableMapOf() - protected val unbookedTransactions = mutableMapOf>() + protected val bookedTransactions = mutableMapOf>() - protected val balances = mutableMapOf() + protected val unbookedTransactions = mutableMapOf>() - protected val accountAddedListeners = mutableListOf<(BankData, CustomerData) -> Unit>() + protected val balances = mutableMapOf() - protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(CustomerData, GetTransactionsResponse) -> Unit>() + protected val accountAddedListeners = mutableListOf<(Account) -> Unit>() + + protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(BankAccount, GetTransactionsResponse) -> Unit>() open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, @@ -47,81 +52,106 @@ open class MainWindowPresenter(callback: FinTsClientCallback) { val customer = CustomerData(customerId, pin) finTsClient.addAccountAsync(bank, customer) { response -> - if (response.isSuccessful) { - accounts.put(customer, bank) + val account = fints4javaModelMapper.mapAccount(customer, bank) + val mappedResponse = fints4javaModelMapper.mapResponse(account, response) - callAccountAddedListeners(bank, customer) + if (response.isSuccessful) { + accounts.put(account, Pair(customer, bank)) + + callAccountAddedListeners(account) if (response.supportsRetrievingTransactionsOfLast90DaysWithoutTan) { - retrievedAccountTransactions(customer, response) + account.bankAccounts.forEach { bankAccount -> + retrievedAccountTransactions(bankAccount, mappedResponse) + } } } - callback(response) + callback(mappedResponse) } } - open fun getAccountTransactionsAsync(bank: BankData, customer: CustomerData, + open fun getAccountTransactionsAsync(account: Account, callback: (GetTransactionsResponse) -> Unit) { - getAccountTransactionsAsync(bank, customer, null, callback) + account.bankAccounts.forEach { bankAccount -> + getAccountTransactionsAsync(bankAccount, callback) // TODO: use a synchronous version of getAccountTransactions() so that all bank accounts get handled serially + } } - open fun getAccountTransactionsAsync(bank: BankData, customer: CustomerData, fromDate: Date?, - callback: (GetTransactionsResponse) -> Unit) { + open fun getAccountTransactionsAsync(bankAccount: BankAccount, + callback: (GetTransactionsResponse) -> Unit) { - finTsClient.getTransactionsAsync(GetTransactionsParameter(true, fromDate), bank, customer) { response -> - retrievedAccountTransactions(customer, response) + getAccountTransactionsAsync(bankAccount, null, callback) + } - callback(response) + open fun getAccountTransactionsAsync(bankAccount: BankAccount, fromDate: Date?, + callback: (GetTransactionsResponse) -> Unit) { + + getCustomerAndBankForAccount(bankAccount.account)?.let { customerBankPair -> + finTsClient.getTransactionsAsync(GetTransactionsParameter(true, fromDate), + customerBankPair.second, customerBankPair.first) { response -> + + val mappedResponse = fints4javaModelMapper.mapResponse(bankAccount.account, response) + + retrievedAccountTransactions(bankAccount, mappedResponse) + + callback(mappedResponse) + } } } open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) { - accounts.forEach { entry -> - val customer = entry.key - val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year - val lastRetrievedTransactionDate = bookedTransactions[customer]?.firstOrNull { it.bookingDate <= today }?.bookingDate - val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - 24 * 60 * 60 * 1000) } // on day before last received transaction + accounts.keys.forEach { account -> + account.bankAccounts.forEach { bankAccount -> + val today = Date() // TODO: still don't know where this bug is coming from that bank returns a transaction dated at end of year + val lastRetrievedTransactionDate = bookedTransactions[bankAccount]?.firstOrNull { it.bookingDate <= today }?.bookingDate + val fromDate = lastRetrievedTransactionDate?.let { Date(it.time - 24 * 60 * 60 * 1000) } // on day before last received transaction - getAccountTransactionsAsync(entry.value, customer, fromDate, callback) + getAccountTransactionsAsync(bankAccount, fromDate, callback) + } } } - protected open fun retrievedAccountTransactions(customer: CustomerData, response: GetTransactionsResponse) { + protected open fun retrievedAccountTransactions(bankAccount: BankAccount, response: GetTransactionsResponse) { if (response.isSuccessful) { - updateAccountTransactionsAndBalances(customer, response) + updateAccountTransactionsAndBalances(bankAccount, response) } - callRetrievedAccountTransactionsResponseListener(customer, response) + callRetrievedAccountTransactionsResponseListener(bankAccount, response) } - protected open fun updateAccountTransactionsAndBalances(customer: CustomerData, response: GetTransactionsResponse) { + protected open fun updateAccountTransactionsAndBalances(bankAccount: BankAccount, response: GetTransactionsResponse) { - if (bookedTransactions.containsKey(customer) == false) { - bookedTransactions.put(customer, response.bookedTransactions.toMutableSet()) - } - else { - bookedTransactions[customer]?.addAll(response.bookedTransactions) // TODO: does currently not work, overwrite equals() + response.bookedTransactions.forEach { entry -> + if (bookedTransactions.containsKey(entry.key) == false) { + bookedTransactions.put(bankAccount, entry.value.toMutableSet()) + } + else { + bookedTransactions[bankAccount]?.addAll(entry.value) // TODO: does currently not work, overwrite equals() + } } - if (unbookedTransactions.containsKey(customer) == false) { - unbookedTransactions.put(customer, response.unbookedTransactions.toMutableSet()) - } - else { - unbookedTransactions[customer]?.addAll(response.unbookedTransactions) + response.unbookedTransactions.forEach { entry -> + if (unbookedTransactions.containsKey(entry.key) == false) { + unbookedTransactions.put(bankAccount, entry.value.toMutableSet()) + } + else { + unbookedTransactions[bankAccount]?.addAll(entry.value) + } } - response.balance?.let { - balances[customer] = it + response.balances.forEach { entry -> + balances[entry.key] = entry.value } } - open fun transferMoneyAsync(bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) { - accounts.entries.firstOrNull()?.let { // TODO: of course not correct, but i have to think of a multi account architecture and data model anyway - finTsClient.doBankTransferAsync(bankTransferData, it.value, it.key, callback) + open fun transferMoneyAsync(bankAccount: BankAccount, bankTransferData: BankTransferData, callback: (FinTsClientResponse) -> Unit) { + getCustomerAndBankForAccount(bankAccount.account)?.let { customerBankPair -> + finTsClient.doBankTransferAsync( + bankTransferData, customerBankPair.second, customerBankPair.first, callback) } } @@ -152,6 +182,19 @@ open class MainWindowPresenter(callback: FinTsClientCallback) { } + protected open fun getCustomerAndBankForAccount(account: Account): Pair? { + (accounts.get(account) as? Pair)?.let { customerBankPair -> + account.selectedTanProcedure?.let { selectedTanProcedure -> + customerBankPair.first.selectedTanProcedure = fints4javaModelMapper.mapTanProcedureBack(selectedTanProcedure) + } + + return customerBankPair // TODO: return IBankingClient + } + + return null + } + + open val allTransactions: List get() = bookedTransactions.values.flatten().toList() // TODO: someday add unbooked transactions @@ -159,23 +202,23 @@ open class MainWindowPresenter(callback: FinTsClientCallback) { get() = balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e } - open fun addAccountAddedListener(listener: (BankData, CustomerData) -> Unit) { + open fun addAccountAddedListener(listener: (Account) -> Unit) { accountAddedListeners.add(listener) } - protected open fun callAccountAddedListeners(bank: BankData, customer: CustomerData) { + protected open fun callAccountAddedListeners(account: Account) { ArrayList(accountAddedListeners).forEach { - it(bank, customer) // TODO: use RxJava for this + it(account) // TODO: use RxJava for this } } - open fun addRetrievedAccountTransactionsResponseListener(listener: (CustomerData, GetTransactionsResponse) -> Unit) { + open fun addRetrievedAccountTransactionsResponseListener(listener: (BankAccount, GetTransactionsResponse) -> Unit) { retrievedAccountTransactionsResponseListeners.add(listener) } - protected open fun callRetrievedAccountTransactionsResponseListener(customer: CustomerData, response: GetTransactionsResponse) { + protected open fun callRetrievedAccountTransactionsResponseListener(bankAccount: BankAccount, response: GetTransactionsResponse) { ArrayList(retrievedAccountTransactionsResponseListeners).forEach { - it(customer, response) // TODO: use RxJava for this + it(bankAccount, response) // TODO: use RxJava for this } } diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/AccountTransactionAdapter.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/AccountTransactionAdapter.kt index d4371c34..48c855a8 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/AccountTransactionAdapter.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/AccountTransactionAdapter.kt @@ -4,7 +4,7 @@ import android.view.ContextMenu import android.view.View import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.ui.adapter.viewholder.AccountTransactionViewHolder -import net.dankito.fints.model.AccountTransaction +import net.dankito.banking.ui.model.AccountTransaction import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.extensions.setTextColorToColorResource import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/TanProceduresAdapter.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/TanProceduresAdapter.kt index 6ca9bfb0..374bbef4 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/TanProceduresAdapter.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/adapter/TanProceduresAdapter.kt @@ -4,7 +4,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import net.dankito.banking.fints4java.android.R -import net.dankito.fints.model.TanProcedure +import net.dankito.banking.ui.model.TanProcedure import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.ui.adapter.ListAdapter diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/AddAccountDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/AddAccountDialog.kt index 0b4310ec..82ca3834 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/AddAccountDialog.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/AddAccountDialog.kt @@ -20,8 +20,8 @@ import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.adapter.BankListAdapter import net.dankito.banking.fints4java.android.ui.adapter.TanProceduresAdapter +import net.dankito.banking.ui.model.responses.AddAccountResponse import net.dankito.fints.model.BankInfo -import net.dankito.fints.response.client.AddAccountResponse import net.dankito.utils.android.extensions.asActivity @@ -100,7 +100,7 @@ open class AddAccountDialog : DialogFragment() { } else { AlertDialog.Builder(context) - .setMessage(context.getString(R.string.dialog_add_account_message_could_not_add_account, (response.exception ?: response.errorsToShowToUser.joinToString("\n")))) + .setMessage(context.getString(R.string.dialog_add_account_message_could_not_add_account, response.errorToShowToUser)) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } .show() } @@ -127,7 +127,7 @@ open class AddAccountDialog : DialogFragment() { val view = context.asActivity()?.layoutInflater?.inflate(R.layout.view_successfully_added_account, null) val adapter = TanProceduresAdapter() - adapter.setItems(response.customer.supportedTanProcedures) + adapter.setItems(response.account.supportedTanProcedures) view?.findViewById(R.id.txtSuccessfullyAddedAccountMessage)?.setText(messageId) @@ -137,7 +137,7 @@ open class AddAccountDialog : DialogFragment() { override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - response.customer.selectedTanProcedure = adapter.getItem(position) + response.account.selectedTanProcedure = adapter.getItem(position) } } @@ -149,7 +149,7 @@ open class AddAccountDialog : DialogFragment() { } protected open fun retrieveAccountTransactionsAndDismiss(response: AddAccountResponse, messageDialog: DialogInterface) { - presenter.getAccountTransactionsAsync(response.bank, response.customer) { } // TODO: show error message if not successful. Here or in HomeFragment + presenter.getAccountTransactionsAsync(response.account) { } messageDialog.dismiss() } diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt index 2f04686b..72a1811f 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/dialogs/BankTransferDialog.kt @@ -13,6 +13,7 @@ import kotlinx.android.synthetic.main.dialog_bank_transfer.* import kotlinx.android.synthetic.main.dialog_bank_transfer.view.* import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.ui.MainWindowPresenter +import net.dankito.banking.ui.model.BankAccount import net.dankito.fints.messages.segmente.implementierte.sepa.ISepaMessageCreator import net.dankito.fints.messages.segmente.implementierte.sepa.SepaMessageCreator import net.dankito.fints.model.BankTransferData @@ -30,17 +31,20 @@ open class BankTransferDialog : DialogFragment() { protected lateinit var presenter: MainWindowPresenter + protected lateinit var bankAccount: BankAccount + protected var preselectedValues: BankTransferData? = null protected val sepaMessageCreator: ISepaMessageCreator = SepaMessageCreator() - open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, fullscreen: Boolean = false) { - show(activity, presenter, null, fullscreen) + open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, fullscreen: Boolean = false) { + show(activity, presenter, bankAccount, null, fullscreen) } - open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, preselectedValues: BankTransferData?, fullscreen: Boolean = false) { + open fun show(activity: AppCompatActivity, presenter: MainWindowPresenter, bankAccount: BankAccount, preselectedValues: BankTransferData?, fullscreen: Boolean = false) { this.presenter = presenter + this.bankAccount = bankAccount this.preselectedValues = preselectedValues val style = if(fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.Dialog @@ -113,7 +117,7 @@ open class BankTransferDialog : DialogFragment() { edtxtUsage.text.toString() ) - presenter.transferMoneyAsync(transferData) { + presenter.transferMoneyAsync(bankAccount, transferData) { context?.asActivity()?.runOnUiThread { handleTransferMoneyResultOnUiThread(it, transferData) } diff --git a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/home/HomeFragment.kt b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/home/HomeFragment.kt index 08a97f1d..74cc89fa 100644 --- a/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/home/HomeFragment.kt +++ b/fints4javaAndroidApp/src/main/java/net/dankito/banking/fints4java/android/ui/home/HomeFragment.kt @@ -18,8 +18,9 @@ import net.dankito.banking.fints4java.android.R import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.adapter.AccountTransactionAdapter import net.dankito.banking.fints4java.android.ui.dialogs.BankTransferDialog +import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.fints.model.BankTransferData -import net.dankito.fints.response.client.GetTransactionsResponse import net.dankito.utils.android.extensions.asActivity import java.math.BigDecimal @@ -159,7 +160,7 @@ class HomeFragment : Fragment() { mnitmBalance.isVisible = true } else { AlertDialog.Builder(activity) // TODO: may show account name in message - .setMessage(activity.getString(R.string.fragment_home_could_not_retrieve_account_transactions, response.exception ?: response.errorsToShowToUser.joinToString("\n"))) + .setMessage(activity.getString(R.string.fragment_home_could_not_retrieve_account_transactions, response.errorToShowToUser)) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } .show() } @@ -169,13 +170,15 @@ class HomeFragment : Fragment() { private fun showBankTransferDialog() { - (context as? AppCompatActivity)?.let { activity -> - BankTransferDialog().show(activity, presenter, mapPreselectedValues()) + transactionAdapter.selectedTransaction?.let { selectedTransaction -> + (context as? AppCompatActivity)?.let { activity -> + BankTransferDialog().show(activity, presenter, selectedTransaction.bankAccount, mapPreselectedValues(selectedTransaction)) + } } } - private fun mapPreselectedValues(): BankTransferData? { - transactionAdapter.selectedTransaction?.let { selectedTransaction -> + private fun mapPreselectedValues(selectedTransaction: AccountTransaction?): BankTransferData? { + selectedTransaction?.let { return BankTransferData( selectedTransaction.otherPartyName ?: "", selectedTransaction.otherPartyAccountId ?: "",