Implemented abortIfTanIsRequired so that at app start we can try to get all accounts' transaction without that user is being asked to enter a TAN if retrieving transaction without TAN isn't supported

This commit is contained in:
dankito 2020-05-25 00:51:30 +02:00
parent 126eaafced
commit 614074b9b9
8 changed files with 33 additions and 7 deletions

View File

@ -255,13 +255,14 @@ open class FinTsClient @JvmOverloads constructor(
val now = Date() val now = Date()
val ninetyDaysAgo = Date(now.time - NinetyDaysAgoMilliseconds - now.timezoneOffset * 60 * 1000) // map to UTC val ninetyDaysAgo = Date(now.time - NinetyDaysAgoMilliseconds - now.timezoneOffset * 60 * 1000) // map to UTC
val response = getTransactions(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo), bank, customer, account) val response = getTransactions(GetTransactionsParameter(account.supportsFeature(AccountFeature.RetrieveBalance), ninetyDaysAgo, abortIfTanIsRequired = true), bank, customer, account)
account.triedToRetrieveTransactionsOfLast90DaysWithoutTan = true account.triedToRetrieveTransactionsOfLast90DaysWithoutTan = true
if (response.isSuccessful) { if (response.isSuccessful) {
if (response.isStrongAuthenticationRequired == false || hasRetrievedTransactionsWithTanJustBefore) { if (response.isStrongAuthenticationRequired == false || hasRetrievedTransactionsWithTanJustBefore) {
// TODO: make use of supportsRetrievingTransactionsOfLast90DaysWithoutTan in UI e.g. in updateAccountsTransactionsIfNoTanIsRequiredAsync()
account.supportsRetrievingTransactionsOfLast90DaysWithoutTan = !!! response.isStrongAuthenticationRequired account.supportsRetrievingTransactionsOfLast90DaysWithoutTan = !!! response.isStrongAuthenticationRequired
} }
} }
@ -310,6 +311,8 @@ open class FinTsClient @JvmOverloads constructor(
val bookedTransactions = mutableListOf<AccountTransaction>() val bookedTransactions = mutableListOf<AccountTransaction>()
var remainingMt940String = "" var remainingMt940String = ""
dialogContext.abortIfTanIsRequired = parameter.abortIfTanIsRequired
dialogContext.chunkedResponseHandler = { response -> dialogContext.chunkedResponseHandler = { response ->
response.getFirstSegmentById<ReceivedAccountTransactions>(InstituteSegmentId.AccountTransactionsMt940)?.let { transactionsSegment -> response.getFirstSegmentById<ReceivedAccountTransactions>(InstituteSegmentId.AccountTransactionsMt940)?.let { transactionsSegment ->
val (chunkTransaction, remainder) = mt940Parser.parseTransactionsChunk(remainingMt940String + transactionsSegment.bookedTransactionsString, account) val (chunkTransaction, remainder) = mt940Parser.parseTransactionsChunk(remainingMt940String + transactionsSegment.bookedTransactionsString, account)
@ -699,6 +702,12 @@ open class FinTsClient @JvmOverloads constructor(
protected open fun handleMayRequiresTan(response: Response, dialogContext: DialogContext): Response { // TODO: use response from DialogContext protected open fun handleMayRequiresTan(response: Response, dialogContext: DialogContext): Response { // TODO: use response from DialogContext
if (response.isStrongAuthenticationRequired) { if (response.isStrongAuthenticationRequired) {
if (dialogContext.abortIfTanIsRequired) {
response.tanRequiredButWeWereToldToAbortIfSo = true
return response
}
response.tanResponse?.let { tanResponse -> response.tanResponse?.let { tanResponse ->
val customer = dialogContext.customer val customer = dialogContext.customer
val enteredTanResult = callback.enterTan(customer, createTanChallenge(tanResponse, customer)) val enteredTanResult = callback.enterTan(customer, createTanChallenge(tanResponse, customer))

View File

@ -8,6 +8,7 @@ open class DialogContext(
bank: BankData, bank: BankData,
customer: CustomerData, customer: CustomerData,
product: ProductData, product: ProductData,
var abortIfTanIsRequired: Boolean = false,
var currentMessage: MessageBuilderResult? = null, var currentMessage: MessageBuilderResult? = null,
var dialogId: String = InitialDialogId, var dialogId: String = InitialDialogId,
var response: Response? = null, var response: Response? = null,

View File

@ -8,5 +8,6 @@ open class GetTransactionsParameter @JvmOverloads constructor(
val fromDate: Date? = null, val fromDate: Date? = null,
val toDate: Date? = null, val toDate: Date? = null,
val maxCountEntries: Int? = null, val maxCountEntries: Int? = null,
val abortIfTanIsRequired: Boolean = false,
val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null
) )

View File

@ -29,9 +29,12 @@ open class Response(
open var tanRequiredButUserDidNotEnterOne = false open var tanRequiredButUserDidNotEnterOne = false
open var tanRequiredButWeWereToldToAbortIfSo = false
open val successful: Boolean open val successful: Boolean
get() = noTanProcedureSelected == false && couldCreateMessage && didReceiveResponse get() = noTanProcedureSelected == false && couldCreateMessage && didReceiveResponse
&& responseContainsErrors == false && tanRequiredButUserDidNotEnterOne == false && responseContainsErrors == false && tanRequiredButUserDidNotEnterOne == false
&& tanRequiredButWeWereToldToAbortIfSo == false
open val isStrongAuthenticationRequired: Boolean open val isStrongAuthenticationRequired: Boolean
get() = tanResponse?.isStrongAuthenticationRequired == true get() = tanResponse?.isStrongAuthenticationRequired == true

View File

@ -8,6 +8,7 @@ open class GetTransactionsParameter(
val alsoRetrieveBalance: Boolean = true, val alsoRetrieveBalance: Boolean = true,
val fromDate: Date? = null, val fromDate: Date? = null,
val toDate: Date? = null, val toDate: Date? = null,
val abortIfTanIsRequired: Boolean = false,
val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null
) { ) {

View File

@ -17,6 +17,7 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
import net.dankito.banking.util.IBankIconFinder import net.dankito.banking.util.IBankIconFinder
import net.dankito.banking.fints.banks.IBankFinder import net.dankito.banking.fints.banks.IBankFinder
import net.dankito.banking.fints.model.BankInfo import net.dankito.banking.fints.model.BankInfo
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.settings.AppSettings import net.dankito.banking.ui.model.settings.AppSettings
import net.dankito.utils.IThreadPool import net.dankito.utils.IThreadPool
import net.dankito.utils.ThreadPool import net.dankito.utils.ThreadPool
@ -99,6 +100,8 @@ open class BankingPresenter(
threadPool.runAsync { threadPool.runAsync {
readAppSettings() readAppSettings()
readPersistedAccounts() readPersistedAccounts()
updateAccountsTransactionsIfNoTanIsRequiredAsync()
} }
// preloadBankList asynchronously; on Android it takes approximately 18 seconds till banks are indexed for first time -> do it as early as possible // preloadBankList asynchronously; on Android it takes approximately 18 seconds till banks are indexed for first time -> do it as early as possible
@ -264,16 +267,16 @@ open class BankingPresenter(
open fun fetchAccountTransactionsAsync(bankAccount: BankAccount, open fun fetchAccountTransactionsAsync(bankAccount: BankAccount,
callback: (GetTransactionsResponse) -> Unit) { callback: (GetTransactionsResponse) -> Unit) {
fetchAccountTransactionsAsync(bankAccount, null, callback) fetchAccountTransactionsAsync(bankAccount, null, false, callback)
} }
open fun fetchAccountTransactionsAsync(bankAccount: BankAccount, fromDate: Date?, open fun fetchAccountTransactionsAsync(bankAccount: BankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false,
callback: (GetTransactionsResponse) -> Unit) { callback: (GetTransactionsResponse) -> Unit) {
getClientForAccount(bankAccount.account)?.let { client -> getClientForAccount(bankAccount.account)?.let { client ->
val startDate = Date() val startDate = Date()
client.getTransactionsAsync(bankAccount, net.dankito.banking.ui.model.parameters.GetTransactionsParameter(true, fromDate, null, { receivedAccountsTransactionChunk(bankAccount, it) } )) { response -> client.getTransactionsAsync(bankAccount, GetTransactionsParameter(true, fromDate, null, abortIfTanIsRequired, { receivedAccountsTransactionChunk(bankAccount, it) } )) { response ->
retrievedAccountTransactions(bankAccount, startDate, response) retrievedAccountTransactions(bankAccount, startDate, response)
@ -283,12 +286,20 @@ open class BankingPresenter(
} }
open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) { open fun updateAccountsTransactionsAsync(callback: (GetTransactionsResponse) -> Unit) {
updateAccountsTransactionsAsync(false, callback)
}
open fun updateAccountsTransactionsIfNoTanIsRequiredAsync() {
updateAccountsTransactionsAsync(true) { }
}
protected open fun updateAccountsTransactionsAsync(abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
clientsForAccounts.keys.forEach { account -> clientsForAccounts.keys.forEach { account ->
account.bankAccounts.forEach { bankAccount -> account.bankAccounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) { if (bankAccount.supportsRetrievingAccountTransactions) {
val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.time - OneDayMillis) } // one day before last received transactions val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.time - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, callback) fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
} }
} }
} }

View File

@ -106,7 +106,7 @@ open class fints4kBankingClient(
callback(GetTransactionsResponse(false, "Cannot find account for ${bankAccount.identifier}")) // TODO: translate callback(GetTransactionsResponse(false, "Cannot find account for ${bankAccount.identifier}")) // TODO: translate
} }
else { else {
client.getTransactionsAsync(net.dankito.banking.fints.model.GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, parameter.toDate, null, client.getTransactionsAsync(GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, parameter.toDate, null, parameter.abortIfTanIsRequired,
{ parameter.retrievedChunkListener?.invoke(mapper.mapTransactions(bankAccount, it)) } ), account) { response -> { parameter.retrievedChunkListener?.invoke(mapper.mapTransactions(bankAccount, it)) } ), account) { response ->
val mappedResponse = mapper.mapResponse(bankAccount, response) val mappedResponse = mapper.mapResponse(bankAccount, response)

View File

@ -143,7 +143,7 @@ open class hbci4jBankingClient(
open fun getTransactionsOfLast90Days(bankAccount: BankAccount): GetTransactionsResponse { open fun getTransactionsOfLast90Days(bankAccount: BankAccount): GetTransactionsResponse {
val ninetyDaysAgo = Date(Date().time - NinetyDaysInMilliseconds) val ninetyDaysAgo = Date(Date().time - NinetyDaysInMilliseconds)
return getTransactions(bankAccount, GetTransactionsParameter(bankAccount.supportsRetrievingBalance, ninetyDaysAgo)) return getTransactions(bankAccount, GetTransactionsParameter(bankAccount.supportsRetrievingBalance, ninetyDaysAgo)) // TODO: implement abortIfTanIsRequired
} }
override fun getTransactionsAsync(bankAccount: BankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { override fun getTransactionsAsync(bankAccount: BankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) {