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 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
if (response.isSuccessful) {
if (response.isStrongAuthenticationRequired == false || hasRetrievedTransactionsWithTanJustBefore) {
// TODO: make use of supportsRetrievingTransactionsOfLast90DaysWithoutTan in UI e.g. in updateAccountsTransactionsIfNoTanIsRequiredAsync()
account.supportsRetrievingTransactionsOfLast90DaysWithoutTan = !!! response.isStrongAuthenticationRequired
}
}
@ -310,6 +311,8 @@ open class FinTsClient @JvmOverloads constructor(
val bookedTransactions = mutableListOf<AccountTransaction>()
var remainingMt940String = ""
dialogContext.abortIfTanIsRequired = parameter.abortIfTanIsRequired
dialogContext.chunkedResponseHandler = { response ->
response.getFirstSegmentById<ReceivedAccountTransactions>(InstituteSegmentId.AccountTransactionsMt940)?.let { transactionsSegment ->
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
if (response.isStrongAuthenticationRequired) {
if (dialogContext.abortIfTanIsRequired) {
response.tanRequiredButWeWereToldToAbortIfSo = true
return response
}
response.tanResponse?.let { tanResponse ->
val customer = dialogContext.customer
val enteredTanResult = callback.enterTan(customer, createTanChallenge(tanResponse, customer))

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ open class GetTransactionsParameter(
val alsoRetrieveBalance: Boolean = true,
val fromDate: Date? = null,
val toDate: Date? = null,
val abortIfTanIsRequired: Boolean = false,
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.fints.banks.IBankFinder
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.utils.IThreadPool
import net.dankito.utils.ThreadPool
@ -99,6 +100,8 @@ open class BankingPresenter(
threadPool.runAsync {
readAppSettings()
readPersistedAccounts()
updateAccountsTransactionsIfNoTanIsRequiredAsync()
}
// 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,
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) {
getClientForAccount(bankAccount.account)?.let { client ->
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)
@ -283,12 +286,20 @@ open class BankingPresenter(
}
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 ->
account.bankAccounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) {
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
}
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 ->
val mappedResponse = mapper.mapResponse(bankAccount, response)

View File

@ -143,7 +143,7 @@ open class hbci4jBankingClient(
open fun getTransactionsOfLast90Days(bankAccount: BankAccount): GetTransactionsResponse {
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) {