Removed bankAccount property from GetTransactionsResponse

This commit is contained in:
dankito 2020-09-19 02:35:57 +02:00
parent da5e285d61
commit 99205b53c9
10 changed files with 78 additions and 64 deletions

View File

@ -347,19 +347,18 @@ open class FinTsClient(
open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData, open fun getTransactionsAsync(parameter: GetTransactionsParameter, bank: BankData,
account: AccountData, callback: (GetTransactionsResponse) -> Unit) { account: AccountData, callback: (GetTransactionsResponse) -> Unit) {
// TODO: or add default RetrievedAccountData if an error occurs?
val dialogContext = DialogContext(bank, product) val dialogContext = DialogContext(bank, product)
initDialog(dialogContext) { initDialogResponse -> initDialog(dialogContext) { initDialogResponse ->
if (initDialogResponse.successful == false) { if (initDialogResponse.successful == false) {
callback(GetTransactionsResponse(initDialogResponse)) callback(GetTransactionsResponse(initDialogResponse, RetrievedAccountData.unsuccessfulList(account)))
} }
else { else {
mayGetBalance(parameter, account, dialogContext) { balanceResponse -> mayGetBalance(parameter, account, dialogContext) { balanceResponse ->
if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented
closeDialog(dialogContext) closeDialog(dialogContext)
callback(GetTransactionsResponse(balanceResponse)) callback(GetTransactionsResponse(balanceResponse, RetrievedAccountData.unsuccessfulList(account)))
} }
else { else {
getTransactionsAfterInitAndGetBalance(parameter, account, dialogContext, balanceResponse, callback) getTransactionsAfterInitAndGetBalance(parameter, account, dialogContext, balanceResponse, callback)
@ -374,7 +373,8 @@ open class FinTsClient(
val balance: Money? = balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let { val balance: Money? = balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
Money(it.balance, it.currency) Money(it.balance, it.currency)
} }
val retrievedData = RetrievedAccountData(account, balance, listOf(), listOf()) val bookedTransactions = mutableSetOf<AccountTransaction>()
val unbookedTransactions = mutableSetOf<Any>()
val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext) val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext)
@ -386,17 +386,19 @@ open class FinTsClient(
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)
retrievedData.addBookedTransactions(chunkTransaction) bookedTransactions.addAll(chunkTransaction)
remainingMt940String = remainder remainingMt940String = remainder
parameter.retrievedChunkListener?.invoke(retrievedData.bookedTransactions) parameter.retrievedChunkListener?.invoke(bookedTransactions)
} }
} }
getAndHandleResponseForMessage(message, dialogContext) { response -> getAndHandleResponseForMessage(message, dialogContext) { response ->
closeDialog(dialogContext) closeDialog(dialogContext)
callback(GetTransactionsResponse(response, listOf(retrievedData), val successful = response.successful && (parameter.alsoRetrieveBalance == false || balance != null)
callback(GetTransactionsResponse(response, listOf(RetrievedAccountData(account, successful, balance, bookedTransactions, unbookedTransactions)),
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null
)) ))
} }

View File

@ -3,17 +3,21 @@ package net.dankito.banking.fints.model
open class RetrievedAccountData( open class RetrievedAccountData(
open val accountData: AccountData, open val accountData: AccountData,
open val successfullyRetrievedData: Boolean,
open val balance: Money?, open val balance: Money?,
open var bookedTransactions: Collection<AccountTransaction>, open var bookedTransactions: Collection<AccountTransaction>,
open var unbookedTransactions: List<Any> open var unbookedTransactions: Collection<Any>
) { ) {
open fun addBookedTransactions(transactions: List<AccountTransaction>) { companion object {
val bookedTransactions = this.bookedTransactions.toMutableSet() // some banks like Postbank return some transactions multiple times -> remove these
bookedTransactions.addAll(transactions) fun unsuccessful(account: AccountData): RetrievedAccountData {
return RetrievedAccountData(account, false, null, listOf(), listOf())
}
fun unsuccessfulList(account: AccountData): List<RetrievedAccountData> {
return listOf(unsuccessful(account))
}
this.bookedTransactions = bookedTransactions
} }
} }

View File

@ -10,7 +10,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
@ -161,7 +160,7 @@ class HomeFragment : Fragment() {
presenter.addSelectedBankAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() } presenter.addSelectedBankAccountsChangedListener { updateMenuItemsStateAndTransactionsToDisplay() }
presenter.addRetrievedAccountTransactionsResponseListener { response -> presenter.addRetrievedAccountTransactionsResponseListener { response ->
handleGetTransactionsResponse(response) handleGetTransactionsResponseOffUiThread(response)
} }
updateMenuItemsStateAndTransactionsToDisplay() updateMenuItemsStateAndTransactionsToDisplay()
@ -181,19 +180,25 @@ class HomeFragment : Fragment() {
presenter.updateSelectedBankAccountTransactionsAsync { } presenter.updateSelectedBankAccountTransactionsAsync { }
} }
private fun handleGetTransactionsResponse(response: GetTransactionsResponse) { private fun handleGetTransactionsResponseOffUiThread(response: GetTransactionsResponse) {
context?.asActivity()?.let { activity -> context?.asActivity()?.let { activity ->
activity.runOnUiThread { activity.runOnUiThread {
if (response.isSuccessful) { handleGetTransactionsResponseOnUiThread(activity, response)
updateTransactionsToDisplayOnUiThread() }
} }
else if (response.userCancelledAction == false) { // if user cancelled entering TAN then don't show a error message }
AlertDialog.Builder(activity)
.setMessage(activity.getString(R.string.fragment_home_could_not_retrieve_account_transactions, private fun handleGetTransactionsResponseOnUiThread(context: Context, response: GetTransactionsResponse) {
response.bankAccount.displayName, response.errorToShowToUser)) response.retrievedData.forEach { retrievedData ->
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } if (retrievedData.successfullyRetrievedData) {
.show() updateTransactionsToDisplayOnUiThread()
} }
else if (response.userCancelledAction == false) { // if user cancelled entering TAN then don't show a error message
AlertDialog.Builder(context)
.setMessage(context.getString(R.string.fragment_home_could_not_retrieve_account_transactions,
retrievedData.account.displayName, response.errorToShowToUser))
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
.show()
} }
} }
} }

View File

@ -143,13 +143,15 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
} }
protected open fun handleGetTransactionsResponseOnUiThread(response: GetTransactionsResponse) { protected open fun handleGetTransactionsResponseOnUiThread(response: GetTransactionsResponse) {
if (response.isSuccessful) { response.retrievedData.forEach { retrievedData ->
updateTransactionsToDisplay() if (retrievedData.successfullyRetrievedData) {
} updateTransactionsToDisplay()
else if (response.userCancelledAction == false) { // if user cancelled entering TAN then don't show a error message }
JavaFxDialogService().showErrorMessageOnUiThread( else if (response.userCancelledAction == false) { // if user cancelled entering TAN then don't show a error message
String.format(messages["account.transactions.control.view.could.not.retrieve.account.transactions"], response.bankAccount.displayName, response.errorToShowToUser) JavaFxDialogService().showErrorMessageOnUiThread(
) String.format(messages["account.transactions.control.view.could.not.retrieve.account.transactions"], retrievedData.account.displayName, response.errorToShowToUser)
)
}
} }
} }

View File

@ -5,6 +5,7 @@ import net.dankito.utils.multiplatform.BigDecimal
open class RetrievedAccountData( open class RetrievedAccountData(
open val account: TypedBankAccount, open val account: TypedBankAccount,
open val successfullyRetrievedData: Boolean,
open val balance: BigDecimal?, open val balance: BigDecimal?,
open val bookedTransactions: Collection<IAccountTransaction>, open val bookedTransactions: Collection<IAccountTransaction>,
open val unbookedTransactions: List<Any> open val unbookedTransactions: List<Any>

View File

@ -1,11 +1,9 @@
package net.dankito.banking.ui.model.responses package net.dankito.banking.ui.model.responses
import net.dankito.banking.ui.model.RetrievedAccountData import net.dankito.banking.ui.model.RetrievedAccountData
import net.dankito.banking.ui.model.TypedBankAccount
open class GetTransactionsResponse( open class GetTransactionsResponse(
open val bankAccount: TypedBankAccount,
isSuccessful: Boolean, isSuccessful: Boolean,
errorToShowToUser: String?, errorToShowToUser: String?,
open val retrievedData: List<RetrievedAccountData> = listOf(), open val retrievedData: List<RetrievedAccountData> = listOf(),

View File

@ -161,9 +161,7 @@ open class BankingPresenter(
persistAccountOffUiThread(account) persistAccountOffUiThread(account)
response.retrievedData.forEach { retrievedData -> response.retrievedData.forEach { retrievedData ->
retrievedAccountTransactions(GetTransactionsResponse(retrievedData.account, true, null, retrievedAccountTransactions(GetTransactionsResponse(true, null, listOf(retrievedData)), startDate, false)
listOf(retrievedData)), startDate, false
)
} }
findIconForBankAsync(account) findIconForBankAsync(account)
@ -355,7 +353,7 @@ open class BankingPresenter(
asyncRunner.runAsync { // don't block retrieving next chunk by blocked saving to db / json asyncRunner.runAsync { // don't block retrieving next chunk by blocked saving to db / json
updateAccountTransactions(bankAccount, accountTransactionsChunk) updateAccountTransactions(bankAccount, accountTransactionsChunk)
callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(bankAccount, true, null, listOf(RetrievedAccountData(bankAccount, null, accountTransactionsChunk, listOf())))) callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(true, null, listOf(RetrievedAccountData(bankAccount, true, null, accountTransactionsChunk, listOf()))))
} }
} }
} }

View File

@ -78,7 +78,7 @@ open class fints4kBankingClient(
override fun getTransactionsAsync(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { override fun getTransactionsAsync(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) {
findAccountForBankAccount(bankAccount) { account, errorMessage -> findAccountForBankAccount(bankAccount) { account, errorMessage ->
if (account == null) { if (account == null) {
callback(GetTransactionsResponse(bankAccount, false, errorMessage)) callback(GetTransactionsResponse(false, errorMessage))
} }
else { else {
val mappedParameter = GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, val mappedParameter = GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate,

View File

@ -39,7 +39,7 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
open fun mapResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { open fun mapResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse {
return GetTransactionsResponse(bankAccount, response.isSuccessful, mapErrorToShowToUser(response), return GetTransactionsResponse(response.isSuccessful, mapErrorToShowToUser(response),
map(bankAccount.customer as TypedCustomer, response.retrievedData), map(bankAccount.customer as TypedCustomer, response.retrievedData),
response.userCancelledAction, response.tanRequiredButWeWereToldToAbortIfSo) response.userCancelledAction, response.tanRequiredButWeWereToldToAbortIfSo)
} }
@ -58,9 +58,10 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
return RetrievedAccountData( return RetrievedAccountData(
account, account,
retrievedData.successfullyRetrievedData,
retrievedData.balance?.toBigDecimal(), retrievedData.balance?.toBigDecimal(),
mapTransactions(account, retrievedData.bookedTransactions), mapTransactions(account, retrievedData.bookedTransactions),
listOf() listOf() // TODO: map unbooked transactions
) )
} }

View File

@ -89,26 +89,28 @@ open class hbci4jBankingClient(
} }
protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(customer: TypedCustomer): AddAccountResponse { protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(customer: TypedCustomer): AddAccountResponse {
val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>() var supportsRetrievingTransactionsOfLast90DaysWithoutTan = false
val balances = mutableMapOf<TypedBankAccount, BigDecimal>() var userCancelledAction = false
val bookedTransactions = mutableMapOf<TypedBankAccount, List<IAccountTransaction>>()
val unbookedTransactions = mutableMapOf<TypedBankAccount, List<Any>>()
customer.accounts.forEach { bankAccount -> val retrievedData = customer.accounts.map { account ->
if (bankAccount.supportsRetrievingAccountTransactions) { if (account.supportsRetrievingAccountTransactions) {
val response = getTransactionsOfLast90Days(bankAccount) val response = getTransactionsOfLast90Days(account)
transactionsOfLast90DaysResponses.add(response) if (response.isSuccessful) {
supportsRetrievingTransactionsOfLast90DaysWithoutTan = true
}
if (response.userCancelledAction) {
userCancelledAction = true
}
bookedTransactions.put(bankAccount, response.bookedTransactions) response.retrievedData.first()
unbookedTransactions.put(bankAccount, response.unbookedTransactions) }
balances.put(bankAccount, response.balance?.toBigDecimal() ?: BigDecimal.Zero) // TODO: really add BigDecimal.Zero if balance couldn't be retrieved? else {
RetrievedAccountData(account, false, null, listOf(), listOf())
} }
} }
val supportsRetrievingTransactionsOfLast90DaysWithoutTan = transactionsOfLast90DaysResponses.firstOrNull { it.isSuccessful } != null
return AddAccountResponse(true, null, customer, supportsRetrievingTransactionsOfLast90DaysWithoutTan, return AddAccountResponse(true, null, customer, supportsRetrievingTransactionsOfLast90DaysWithoutTan,
bookedTransactions, unbookedTransactions, balances) retrievedData, userCancelledAction)
} }
@ -146,6 +148,7 @@ open class hbci4jBankingClient(
protected open fun getTransactions(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): GetTransactionsResponse { protected open fun getTransactions(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): GetTransactionsResponse {
val connection = connect() val connection = connect()
val retrievingDataFailed = listOf(RetrievedAccountData(bankAccount, false, null, listOf(), listOf()))
connection.handle?.let { handle -> connection.handle?.let { handle ->
try { try {
@ -153,8 +156,8 @@ open class hbci4jBankingClient(
// Pruefen, ob die Kommunikation mit der Bank grundsaetzlich geklappt hat // Pruefen, ob die Kommunikation mit der Bank grundsaetzlich geklappt hat
if (!status.isOK) { if (!status.isOK) {
log.error("Could not connect to bank ${credentials.bankCode} ${status.toString()}: ${status.errorString}") log.error("Could not connect to bank ${credentials.bankCode} $status: ${status.errorString}")
return GetTransactionsResponse(bankAccount, false, "Could not connect to bank ${credentials.bankCode}: ${status.toString()}") return GetTransactionsResponse(false, "Could not connect to bank ${credentials.bankCode}: $status", retrievingDataFailed)
} }
// Auswertung des Saldo-Abrufs. // Auswertung des Saldo-Abrufs.
@ -163,7 +166,7 @@ open class hbci4jBankingClient(
val balanceResult = nullableBalanceJob.jobResult as GVRSaldoReq val balanceResult = nullableBalanceJob.jobResult as GVRSaldoReq
if(balanceResult.isOK == false) { if(balanceResult.isOK == false) {
log.error("Could not fetch balance of bank account $bankAccount: $balanceResult", balanceResult.getJobStatus().exceptions) log.error("Could not fetch balance of bank account $bankAccount: $balanceResult", balanceResult.getJobStatus().exceptions)
return GetTransactionsResponse(bankAccount, false, "Could not fetch balance of bank account $bankAccount: $balanceResult") return GetTransactionsResponse(false, "Could not fetch balance of bank account $bankAccount: $balanceResult", retrievingDataFailed)
} }
balance = balanceResult.entries[0].ready.value.bigDecimalValue.toBigDecimal() balance = balanceResult.entries[0].ready.value.bigDecimalValue.toBigDecimal()
@ -177,15 +180,15 @@ open class hbci4jBankingClient(
// Pruefen, ob der Abruf der Umsaetze geklappt hat // Pruefen, ob der Abruf der Umsaetze geklappt hat
if (result.isOK == false) { if (result.isOK == false) {
log.error("Could not get fetch account transactions of bank account $bankAccount: $result", result.getJobStatus().exceptions) log.error("Could not get fetch account transactions of bank account $bankAccount: $result", result.getJobStatus().exceptions)
return GetTransactionsResponse(bankAccount, false, "Could not fetch account transactions of bank account $bankAccount: $result") return GetTransactionsResponse(false, "Could not fetch account transactions of bank account $bankAccount: $result", retrievingDataFailed)
} }
return GetTransactionsResponse(bankAccount, true, null, accountTransactionMapper.mapAccountTransactions(bankAccount, result), return GetTransactionsResponse(true, null, listOf(RetrievedAccountData(bankAccount, true, balance.toBigDecimal(),
listOf(), balance?.toBigDecimal()) accountTransactionMapper.mapAccountTransactions(bankAccount, result), listOf())))
} }
catch(e: Exception) { catch(e: Exception) {
log.error("Could not get accounting details for bank ${credentials.bankCode}", e) log.error("Could not get accounting details for bank ${credentials.bankCode}", e)
return GetTransactionsResponse(bankAccount, false, e.getInnerExceptionMessage()) return GetTransactionsResponse(false, e.getInnerExceptionMessage(), retrievingDataFailed)
} }
finally { finally {
closeConnection(connection) closeConnection(connection)
@ -194,7 +197,7 @@ open class hbci4jBankingClient(
closeConnection(connection) closeConnection(connection)
return GetTransactionsResponse(bankAccount, false, connection.error.getInnerExceptionMessage()) return GetTransactionsResponse(false, connection.error.getInnerExceptionMessage(), retrievingDataFailed)
} }
protected open fun executeJobsForGetAccountingEntries(handle: HBCIHandler, bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): Triple<HBCIJob?, HBCIJob, HBCIExecStatus> { protected open fun executeJobsForGetAccountingEntries(handle: HBCIHandler, bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): Triple<HBCIJob?, HBCIJob, HBCIExecStatus> {