Implemented displaying state if no transactions are displayed (e.g. if no transactions have been fetch, account doesn't support fetching transactions of retrieved period didn't contain any transactions)

This commit is contained in:
dankito 2020-09-20 23:50:40 +02:00
parent 002878cf09
commit a382904015
10 changed files with 254 additions and 64 deletions

View File

@ -19,6 +19,7 @@ import net.dankito.banking.ui.android.adapter.AccountTransactionAdapter
import net.dankito.banking.ui.android.di.BankingComponent import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider import net.dankito.banking.ui.android.extensions.addHorizontalItemDivider
import net.dankito.banking.ui.android.extensions.showAmount import net.dankito.banking.ui.android.extensions.showAmount
import net.dankito.banking.ui.model.TransactionsRetrievalState
import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.TypedBankAccount
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.banking.ui.model.responses.GetTransactionsResponse
@ -93,6 +94,8 @@ class HomeFragment : Fragment() {
setFetchAllTransactionsView() setFetchAllTransactionsView()
} }
rootView.btnRetrieveTransactions.setOnClickListener { fetchTransactions() }
return rootView return rootView
} }
@ -235,7 +238,6 @@ class HomeFragment : Fragment() {
private fun updateTransactionsToDisplayOnUiThread() { private fun updateTransactionsToDisplayOnUiThread() {
transactionAdapter.items = presenter.searchSelectedAccountTransactions(appliedTransactionsFilter) transactionAdapter.items = presenter.searchSelectedAccountTransactions(appliedTransactionsFilter)
// TODO: if transactions are filtered calculate and show balance of displayed transactions?
mnitmBalance.title = presenter.formatAmount(presenter.balanceOfSelectedBankAccounts) mnitmBalance.title = presenter.formatAmount(presenter.balanceOfSelectedBankAccounts)
mnitmBalance.isVisible = presenter.doSelectedBankAccountsSupportRetrievingBalance mnitmBalance.isVisible = presenter.doSelectedBankAccountsSupportRetrievingBalance
@ -247,15 +249,35 @@ class HomeFragment : Fragment() {
else transactionAdapter.items.map { it.amount }.sum() else transactionAdapter.items.map { it.amount }.sum()
txtTransactionsBalance.showAmount(presenter, sumOfDisplayedTransactions) txtTransactionsBalance.showAmount(presenter, sumOfDisplayedTransactions)
setRecyclerViewAndNoTransactionsFetchedView()
setFetchAllTransactionsView() setFetchAllTransactionsView()
} }
private fun setRecyclerViewAndNoTransactionsFetchedView() {
val transactionsRetrievalState = presenter.selectedBankAccountsTransactionRetrievalState
val haveTransactionsBeenRetrieved = transactionsRetrievalState == TransactionsRetrievalState.RetrievedTransactions
rcyvwAccountTransactions.visibility = if (haveTransactionsBeenRetrieved) View.VISIBLE else View.GONE
lytNoTransactionsFetched.visibility = if (haveTransactionsBeenRetrieved) View.GONE else View.VISIBLE
btnRetrieveTransactions.visibility = if (transactionsRetrievalState == TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions) View.GONE else View.VISIBLE
val transactionsRetrievalStateMessageId = when (transactionsRetrievalState) {
TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions -> R.string.fragment_home_transactions_retrieval_state_account_does_not_support_retrieving_transactions
TransactionsRetrievalState.NoTransactionsInRetrievedPeriod -> R.string.fragment_home_transactions_retrieval_state_no_transactions_in_retrieved_period
TransactionsRetrievalState.NeverRetrievedTransactions -> R.string.fragment_home_transactions_retrieval_state_never_retrieved_transactions
else -> null
}
txtNoTransactionsFetchedMessage.text = transactionsRetrievalStateMessageId?.let { requireContext().getString(transactionsRetrievalStateMessageId) } ?: ""
}
private fun setFetchAllTransactionsView() { private fun setFetchAllTransactionsView() {
accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched
var floatingActionMenuBottomMarginResourceId = R.dimen.fab_margin_bottom_without_toolbar var floatingActionMenuBottomMarginResourceId = R.dimen.fab_margin_bottom_without_toolbar
if (doNotShowFetchAllTransactionsOverlay || accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty()) { if (doNotShowFetchAllTransactionsOverlay || accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty()
|| presenter.selectedBankAccountsTransactionRetrievalState != TransactionsRetrievalState.RetrievedTransactions) {
lytFetchAllTransactionsOverlay.visibility = View.GONE lytFetchAllTransactionsOverlay.visibility = View.GONE
} }
else { else {
@ -268,14 +290,22 @@ class HomeFragment : Fragment() {
} }
} }
private fun fetchAllTransactions() {
accountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account -> private fun fetchTransactions() {
presenter.fetchAllAccountTransactionsAsync(account) { presenter.selectedBankAccounts.forEach { account ->
requireActivity().runOnUiThread { if (account.haveAllTransactionsBeenFetched) {
updateAccountsTransactions() presenter.updateBankAccountTransactionsAsync(account)
} }
else {
presenter.fetchAllAccountTransactionsAsync(account)
} }
} }
} }
private fun fetchAllTransactions() {
accountsForWhichNotAllTransactionsHaveBeenFetched.forEach { account ->
presenter.fetchAllAccountTransactionsAsync(account)
}
}
} }

View File

@ -12,7 +12,7 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
> >
<LinearLayout <RelativeLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -26,6 +26,7 @@
android:id="@+id/lytTransactionsSummary" android:id="@+id/lytTransactionsSummary"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_transactions_summary_height" android:layout_height="@dimen/fragment_account_transaction_transactions_summary_height"
android:layout_alignParentTop="true"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
> >
@ -59,10 +60,47 @@
android:id="@+id/rcyvwAccountTransactions" android:id="@+id/rcyvwAccountTransactions"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/lytTransactionsSummary"
android:layout_alignParentBottom="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
/> />
</LinearLayout> <LinearLayout
android:orientation="vertical"
android:id="@+id/lytNoTransactionsFetched"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:gravity="center"
android:visibility="gone"
>
<TextView
android:id="@+id/txtNoTransactionsFetchedMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textAlignment="gravity"
style="@style/TextAppearance.AppCompat.Medium"
/>
<Button
android:id="@+id/btnRetrieveTransactions"
android:layout_width="match_parent"
android:layout_height="@dimen/fragment_account_transaction_fetch_transactions_button_height"
android:layout_marginTop="@dimen/fragment_account_transaction_fetch_transactions_button_margin_top"
android:gravity="center"
android:textAlignment="gravity"
style="?android:attr/buttonBarButtonStyle"
android:textSize="@dimen/textAppearanceMediumTextSize"
android:textColor="@color/linkColor"
android:text="@string/fragment_home_fetch_transactions"
/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/lytFetchAllTransactionsOverlay" android:id="@+id/lytFetchAllTransactionsOverlay"

View File

@ -37,9 +37,13 @@
<string name="fragment_home_count_transactions">%d Umsätze</string> <string name="fragment_home_count_transactions">%d Umsätze</string>
<string name="fragment_home_could_not_retrieve_account_transactions">Kontoumsätze für \'%1$s\' konnten nicht empfangen werden.\n\nFehlermeldung Ihrer Bank:\n\n%2$s</string> <string name="fragment_home_could_not_retrieve_account_transactions">Kontoumsätze für \'%1$s\' konnten nicht empfangen werden.\n\nFehlermeldung Ihrer Bank:\n\n%2$s</string>
<string name="fragment_home_fetch_transactions">Umsätze abrufen</string>
<string name="fragment_home_transactions_retrieval_state_account_does_not_support_retrieving_transactions">Konto unterstützt Abrufen von Umsätzen nicht</string>
<string name="fragment_home_transactions_retrieval_state_never_retrieved_transactions">Noch keine Umsätze abgerufen</string>
<string name="fragment_home_transactions_retrieval_state_no_transactions_in_retrieved_period">Empfangener Zeitraum enthielt keine Umsätze</string>
<string name="fragment_home_transfer_money_to">Neue Überweisung an %s</string> <string name="fragment_home_transfer_money_to">Neue Überweisung an %s</string>
<string name="fragment_home_transfer_money_with_same_data">Neue Überweisung mit gleichen Daten</string> <string name="fragment_home_transfer_money_with_same_data">Neue Überweisung mit gleichen Daten</string>
<string name="fragment_home_fetch_all_account_transactions">Ältere Umsätze laden (erfordert TAN)</string> <string name="fragment_home_fetch_all_account_transactions">Ältere Umsätze abrufen (erfordert TAN)</string>
<string name="dialog_add_account_enter_bank">Bank (Suche auch mittels Bankleitzahl oder Ort):</string> <string name="dialog_add_account_enter_bank">Bank (Suche auch mittels Bankleitzahl oder Ort):</string>
<string name="dialog_add_account_add">Hinzufügen</string> <string name="dialog_add_account_add">Hinzufügen</string>

View File

@ -3,6 +3,8 @@
<dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="textAppearanceMediumTextSize">18sp</dimen>
<dimen name="nav_header_vertical_spacing">8dp</dimen> <dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">176dp</dimen> <dimen name="nav_header_height">176dp</dimen>
<dimen name="nav_header_app_icon_width">48dp</dimen> <dimen name="nav_header_app_icon_width">48dp</dimen>
@ -25,6 +27,8 @@
<dimen name="fragment_account_transaction_margin_start_and_end">4dp</dimen> <dimen name="fragment_account_transaction_margin_start_and_end">4dp</dimen>
<dimen name="fragment_account_transaction_transactions_summary_height">24dp</dimen> <dimen name="fragment_account_transaction_transactions_summary_height">24dp</dimen>
<dimen name="fragment_account_transaction_fetch_transactions_button_height">40dp</dimen>
<dimen name="fragment_account_transaction_fetch_transactions_button_margin_top">18dp</dimen>
<dimen name="fragment_account_transaction_fetch_all_transactions_overlay_height">48dp</dimen> <dimen name="fragment_account_transaction_fetch_all_transactions_overlay_height">48dp</dimen>
<dimen name="fragment_account_transaction_button_close_fetch_all_transactions_overlay_size">20dp</dimen> <dimen name="fragment_account_transaction_button_close_fetch_all_transactions_overlay_size">20dp</dimen>
<dimen name="fragment_account_transaction_button_close_fetch_all_transactions_overlay_margin_end">6dp</dimen> <dimen name="fragment_account_transaction_button_close_fetch_all_transactions_overlay_margin_end">6dp</dimen>

View File

@ -37,6 +37,10 @@
<string name="fragment_home_count_transactions">%d transactions</string> <string name="fragment_home_count_transactions">%d transactions</string>
<string name="fragment_home_could_not_retrieve_account_transactions">Could not retrieve account transactions for \'%1$s\'.\n\nError message from your bank:\n\n%2$s</string> <string name="fragment_home_could_not_retrieve_account_transactions">Could not retrieve account transactions for \'%1$s\'.\n\nError message from your bank:\n\n%2$s</string>
<string name="fragment_home_fetch_transactions">Fetch transactions</string>
<string name="fragment_home_transactions_retrieval_state_account_does_not_support_retrieving_transactions">Account does not support retrieving transactions</string>
<string name="fragment_home_transactions_retrieval_state_never_retrieved_transactions">No transactions fetched yet</string>
<string name="fragment_home_transactions_retrieval_state_no_transactions_in_retrieved_period">There haven\'t been any transactions in retrieved period</string>
<string name="fragment_home_transfer_money_to">Transfer money to %s</string> <string name="fragment_home_transfer_money_to">Transfer money to %s</string>
<string name="fragment_home_transfer_money_with_same_data">New transfer with same data</string> <string name="fragment_home_transfer_money_with_same_data">New transfer with same data</string>
<string name="fragment_home_fetch_all_account_transactions">Fetch earlier transactions (requires TAN)</string> <string name="fragment_home_fetch_all_account_transactions">Fetch earlier transactions (requires TAN)</string>

View File

@ -0,0 +1,14 @@
package net.dankito.banking.ui.model
enum class TransactionsRetrievalState {
AccountDoesNotSupportFetchingTransactions,
NeverRetrievedTransactions,
NoTransactionsInRetrievedPeriod,
RetrievedTransactions
}

View File

@ -273,10 +273,10 @@ open class BankingPresenter(
} }
} }
open fun fetchAllAccountTransactionsAsync(bankAccount: TypedBankAccount, open fun fetchAllAccountTransactionsAsync(account: TypedBankAccount,
callback: ((GetTransactionsResponse) -> Unit)? = null) { callback: ((GetTransactionsResponse) -> Unit)? = null) {
fetchAccountTransactionsAsync(bankAccount, null, false, callback) fetchAccountTransactionsAsync(account, null, false, callback)
} }
open fun fetchAccountTransactionsAsync(account: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false, open fun fetchAccountTransactionsAsync(account: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false,
@ -326,7 +326,7 @@ open class BankingPresenter(
} }
} }
protected open fun updateBankAccountTransactionsAsync(bankAccount: TypedBankAccount, abortIfTanIsRequired: Boolean, callback: (GetTransactionsResponse) -> Unit) { open fun updateBankAccountTransactionsAsync(bankAccount: TypedBankAccount, abortIfTanIsRequired: Boolean = false, callback: ((GetTransactionsResponse) -> Unit)? = null) {
val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback) fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
@ -459,7 +459,7 @@ open class BankingPresenter(
getBankingClientForAccount(account.customer)?.let { client -> getBankingClientForAccount(account.customer)?.let { client ->
client.transferMoneyAsync(data) { response -> client.transferMoneyAsync(data) { response ->
if (response.successful) { if (response.successful) {
updateBankAccountTransactionsAsync(account, true) { } updateBankAccountTransactionsAsync(account, true)
} }
callback(response) callback(response)
@ -634,6 +634,9 @@ open class BankingPresenter(
open val selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched: List<TypedBankAccount> open val selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched: List<TypedBankAccount>
get() = selectedBankAccounts.filter { it.haveAllTransactionsBeenFetched == false } get() = selectedBankAccounts.filter { it.haveAllTransactionsBeenFetched == false }
open val selectedBankAccountsTransactionRetrievalState: TransactionsRetrievalState
get() = getAccountsTransactionRetrievalState(selectedBankAccounts)
open val areAllAccountSelected: Boolean open val areAllAccountSelected: Boolean
get() = selectedAccountType == SelectedAccountType.AllAccounts get() = selectedAccountType == SelectedAccountType.AllAccounts
@ -732,6 +735,40 @@ open class BankingPresenter(
return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions
} }
protected open fun getAccountsTransactionRetrievalState(accounts: List<TypedBankAccount>): TransactionsRetrievalState {
val states = accounts.map { getAccountTransactionRetrievalState(it) }
if (states.contains(TransactionsRetrievalState.RetrievedTransactions)) {
return TransactionsRetrievalState.RetrievedTransactions
}
if (states.contains(TransactionsRetrievalState.NoTransactionsInRetrievedPeriod)) {
return TransactionsRetrievalState.NoTransactionsInRetrievedPeriod
}
if (states.contains(TransactionsRetrievalState.NeverRetrievedTransactions)) {
return TransactionsRetrievalState.NeverRetrievedTransactions
}
return TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions
}
protected open fun getAccountTransactionRetrievalState(account: TypedBankAccount): TransactionsRetrievalState {
if (account.supportsRetrievingAccountTransactions == false) {
return TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions
}
if (account.bookedTransactions.isNotEmpty()) {
return TransactionsRetrievalState.RetrievedTransactions
}
if (account.lastRetrievedTransactionsTimestamp != null) {
return TransactionsRetrievalState.NoTransactionsInRetrievedPeriod
}
return TransactionsRetrievalState.NeverRetrievedTransactions
}
protected open fun getBalanceForAccounts(customers: Collection<TypedCustomer>): BigDecimal { protected open fun getBalanceForAccounts(customers: Collection<TypedCustomer>): BigDecimal {
return customers.map { it.balance }.sum() return customers.map { it.balance }.sum()
} }

View File

@ -75,14 +75,19 @@
"Fetch all account transactions" = "Fetch earlier transactions (requires TAN)"; "Fetch all account transactions" = "Fetch earlier transactions (requires TAN)";
"Fetch transactions" = "Fetch transactions";
"Account does not support retrieving transactions" = "Account does not support retrieving transactions";
"No transactions fetched yet" = "No transactions fetched yet";
"There haven't been any transactions in retrieved period" = "There haven't been any transactions in retrieved period";
"Transfer money to %@" = "Transfer money to %@"; "Transfer money to %@" = "Transfer money to %@";
"New transfer with same data" = "New transfer with same data"; "New transfer with same data" = "New transfer with same data";
"Could not fetch latest transactions" = "Could not fetch latest transactions"; "Could not fetch latest transactions" = "Could not fetch latest transactions";
"Could not fetch latest transactions for %@. Error message from your bank: %@." = "Could not fetch latest transactions for %@.\nError message from your bank:\n%@."; "Could not fetch latest transactions for %@. Error message from your bank: %@." = "Could not fetch latest transactions for %@.\nError message from your bank:\n%@.";
"Could not fetch all transactions" = "Could not fetch all transactions"; "Could not fetch transactions" = "Could not fetch transactions";
"Could not fetch all transactions for %@. Error message from your bank: %@." = "Could not fetch all transactions for %@.\nError message from your bank:\n%@."; "Could not fetch transactions for %@. Error message from your bank: %@." = "Could not fetch transactions for %@.\nError message from your bank:\n%@.";
/* New action sheet */ /* New action sheet */

View File

@ -75,14 +75,19 @@
"Fetch all account transactions" = "Ältere Umsätze laden (erfordert TAN)"; "Fetch all account transactions" = "Ältere Umsätze laden (erfordert TAN)";
"Fetch transactions" = "Umsätze abrufen";
"Account does not support retrieving transactions" = "Konto unterstützt Abrufen von Umsätzen nicht";
"No transactions fetched yet" = "Noch keine Umsätze abgerufen";
"There haven't been any transactions in retrieved period" = "Empfangener Zeitraum enthielt keine Umsätze";
"Transfer money to %@" = "Neue Überweisung an %@"; "Transfer money to %@" = "Neue Überweisung an %@";
"New transfer with same data" = "Neue Überweisung mit gleichen Daten"; "New transfer with same data" = "Neue Überweisung mit gleichen Daten";
"Could not fetch latest transactions" = "Umsätze konnte nicht aktualisiert werden"; "Could not fetch latest transactions" = "Umsätze konnte nicht aktualisiert werden";
"Could not fetch latest transactions for %@. Error message from your bank: %@." = "Die Umsätze für %@ konnten nicht aktualisiert werden.\nFehlermeldung Ihrer Bank:\n%@."; "Could not fetch latest transactions for %@. Error message from your bank: %@." = "Die Umsätze für %@ konnten nicht aktualisiert werden.\nFehlermeldung Ihrer Bank:\n%@.";
"Could not fetch all transactions" = "Es konnte nicht all Umsätze geholt werden"; "Could not fetch transactions" = "Umsätze konnten nicht abgerufen werden";
"Could not fetch all transactions for %@. Error message from your bank: %@." = "Für %@ konnten nicht alle Umsätze geholt werden.\nFehlermeldung Ihrer Bank:\n%@."; "Could not fetch transactions for %@. Error message from your bank: %@." = "Umsätze für %@ konnten nicht abgerufen werden.\nFehlermeldung Ihrer Bank:\n%@.";
/* New action sheet */ /* New action sheet */

View File

@ -9,23 +9,29 @@ struct AccountTransactionsDialog: View {
private let title: String private let title: String
private let allTransactions: [IAccountTransaction] private let showBankIcons: Bool
@State private var balanceOfAllTransactions: CommonBigDecimal
private let areMoreThanOneBanksTransactionsDisplayed: Bool
@State private var haveAllTransactionsBeenFetched: Bool @State private var haveTransactionsBeenRetrievedForSelectedAccounts = true
@State private var showFetchAllTransactionsOverlay: Bool @State private var haveAllTransactionsBeenFetched: Bool = false
@State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] @State private var showTransactionsList = true
@State private var noTransactionsFetchedMessage: LocalizedStringKey = ""
@State private var showFetchTransactionsButton = true
@State private var showFetchAllTransactionsOverlay: Bool = false
@State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = []
@State private var filteredTransactions: [IAccountTransaction] @State private var filteredTransactions: [IAccountTransaction] = []
@State private var balanceOfFilteredTransactions: CommonBigDecimal @State private var balanceOfAllTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0")
@State private var balanceOfFilteredTransactions: CommonBigDecimal = CommonBigDecimal(decimal: "0")
@State private var searchText = "" @State private var searchText = ""
@ -46,39 +52,27 @@ struct AccountTransactionsDialog: View {
init(allBanks: [ICustomer]) { init(allBanks: [ICustomer]) {
let allAccounts = allBanks.flatMap { $0.accounts } self.init("All accounts", true)
self.init("All accounts", allAccounts.flatMap { $0.bookedTransactions }, allBanks.sumBalances(), allAccounts.filter { $0.haveAllTransactionsBeenFetched == false })
presenter.selectedAllBankAccounts() presenter.selectedAllBankAccounts()
} }
init(bank: ICustomer) { init(bank: ICustomer) {
self.init(bank.displayName, bank.accounts.flatMap { $0.bookedTransactions }, bank.balance, bank.accounts.filter { $0.haveAllTransactionsBeenFetched == false }) self.init(bank.displayName, false)
presenter.selectedAccount(customer: bank) presenter.selectedAccount(customer: bank)
} }
init(account: IBankAccount) { init(account: IBankAccount) {
self.init(account.displayName, account.bookedTransactions, account.balance, account.haveAllTransactionsBeenFetched ? [] : [account]) self.init(account.displayName, false)
presenter.selectedBankAccount(bankAccount: account) presenter.selectedBankAccount(bankAccount: account)
} }
fileprivate init(_ title: String, _ transactions: [IAccountTransaction], _ balance: CommonBigDecimal, _ accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = []) { fileprivate init(_ title: String, _ showBankIcons: Bool) {
self.title = title self.title = title
self.allTransactions = transactions
self._filteredTransactions = State(initialValue: transactions.sorted { $0.valueDate.date > $1.valueDate.date })
self._balanceOfAllTransactions = State(initialValue: balance) self.showBankIcons = showBankIcons
self._balanceOfFilteredTransactions = State(initialValue: balance)
self.areMoreThanOneBanksTransactionsDisplayed = Set(allTransactions.compactMap { $0.bankAccount }.compactMap { $0.customer as! Customer }).count > 1
_accountsForWhichNotAllTransactionsHaveBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched)
_haveAllTransactionsBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty)
_showFetchAllTransactionsOverlay = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty)
} }
@ -98,13 +92,26 @@ struct AccountTransactionsDialog: View {
} }
} }
Section { if showTransactionsList {
ForEach(filteredTransactions, id: \.technicalId) { transaction in Section {
AccountTransactionListItem(transaction, self.areMoreThanOneBanksTransactionsDisplayed) ForEach(filteredTransactions, id: \.technicalId) { transaction in
AccountTransactionListItem(transaction, self.showBankIcons)
}
}
}
else {
VStack(alignment: .center) {
Text(noTransactionsFetchedMessage)
.multilineTextAlignment(.center)
if showFetchTransactionsButton {
Button("Fetch transactions") { self.fetchTransactions() }
.padding(.top, 6)
}
} }
} }
if haveAllTransactionsBeenFetched == false && showFetchAllTransactionsOverlay == false { if haveAllTransactionsBeenFetched == false && showFetchAllTransactionsOverlay == false && haveTransactionsBeenRetrievedForSelectedAccounts {
Section { Section {
HStack { HStack {
Spacer() Spacer()
@ -148,7 +155,7 @@ struct AccountTransactionsDialog: View {
} }
} }
.executeMutatingMethod { .executeMutatingMethod {
self.showFetchAllTransactionsOverlay = self.shouldShowFetchAllTransactionsOverlay self.setInitialValues()
} }
.alert(message: $errorMessage) .alert(message: $errorMessage)
.showNavigationBarTitle(LocalizedStringKey(title)) .showNavigationBarTitle(LocalizedStringKey(title))
@ -156,6 +163,30 @@ struct AccountTransactionsDialog: View {
} }
private func setInitialValues() {
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedBankAccounts
self.filterTransactions("")
setTransactionsView()
}
private func setTransactionsView() {
let transactionsRetrievalState = presenter.selectedBankAccountsTransactionRetrievalState
self.haveTransactionsBeenRetrievedForSelectedAccounts = transactionsRetrievalState == .retrievedtransactions
self.accountsForWhichNotAllTransactionsHaveBeenFetched = presenter.selectedBankAccountsForWhichNotAllTransactionsHaveBeenFetched
self.haveAllTransactionsBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty
self.showFetchAllTransactionsOverlay = shouldShowFetchAllTransactionsOverlay && haveTransactionsBeenRetrievedForSelectedAccounts
self.showTransactionsList = haveTransactionsBeenRetrievedForSelectedAccounts
self.noTransactionsFetchedMessage = getNoTransactionsFetchedMessage(transactionsRetrievalState)
self.showFetchTransactionsButton = transactionsRetrievalState != .accountdoesnotsupportfetchingtransactions
}
private var fetchAllTransactionsButton: some View { private var fetchAllTransactionsButton: some View {
Button("Fetch all account transactions") { Button("Fetch all account transactions") {
self.fetchAllTransactions(self.accountsForWhichNotAllTransactionsHaveBeenFetched) self.fetchAllTransactions(self.accountsForWhichNotAllTransactionsHaveBeenFetched)
@ -180,27 +211,48 @@ struct AccountTransactionsDialog: View {
} }
} }
private func fetchAllTransactions(_ accounts: [IBankAccount]) { private func fetchTransactions() {
accounts.forEach { account in for account in presenter.selectedBankAccounts {
presenter.fetchAllAccountTransactionsAsync(bankAccount: account, callback: self.handleGetAllTransactionsResult) if account.haveAllTransactionsBeenFetched {
presenter.updateBankAccountTransactionsAsync(bankAccount: account, abortIfTanIsRequired: false, callback: self.handleGetTransactionsResult)
}
else {
presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
}
} }
} }
private func handleGetAllTransactionsResult(_ response: GetTransactionsResponse) { private func fetchAllTransactions(_ accounts: [IBankAccount]) {
self.accountsForWhichNotAllTransactionsHaveBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.filter { $0.haveAllTransactionsBeenFetched == false } accounts.forEach { account in
self.haveAllTransactionsBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty presenter.fetchAllAccountTransactionsAsync(account: account, callback: self.handleGetTransactionsResult)
self.showFetchAllTransactionsOverlay = shouldShowFetchAllTransactionsOverlay }
}
private func handleGetTransactionsResult(_ response: GetTransactionsResponse) {
setTransactionsView()
if response.successful { if response.successful {
self.filterTransactions(self.searchText) self.filterTransactions(self.searchText)
} }
else if response.userCancelledAction == false { else if response.userCancelledAction == false {
if let failedAccount = getAccountThatFailed(response) { if let failedAccount = getAccountThatFailed(response) {
self.errorMessage = Message(title: Text("Could not fetch all transactions"), message: Text("Could not fetch all transactions for \(failedAccount.displayName). Error message from your bank: \(response.errorToShowToUser ?? "").")) self.errorMessage = Message(title: Text("Could not fetch transactions"), message: Text("Could not fetch transactions for \(failedAccount.displayName). Error message from your bank: \(response.errorToShowToUser ?? "")."))
} }
} }
} }
private func getNoTransactionsFetchedMessage(_ state: TransactionsRetrievalState) -> LocalizedStringKey {
if state == .neverretrievedtransactions {
return "No transactions fetched yet"
}
else if state == .notransactionsinretrievedperiod {
return "There haven't been any transactions in retrieved period"
}
else {
return "Account does not support retrieving transactions"
}
}
private func filterTransactions(_ query: String) { private func filterTransactions(_ query: String) {
self.filteredTransactions = presenter.searchSelectedAccountTransactions(query: query).sorted { $0.valueDate.date > $1.valueDate.date } self.filteredTransactions = presenter.searchSelectedAccountTransactions(query: query).sorted { $0.valueDate.date > $1.valueDate.date }
@ -236,9 +288,6 @@ struct AccountTransactionsDialog: View {
struct AccountTransactionsDialog_Previews: PreviewProvider { struct AccountTransactionsDialog_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AccountTransactionsDialog(previewBanks[0].displayName, [ AccountTransactionsDialog(previewBanks[0].displayName, false)
AccountTransaction(bankAccount: previewBanks[0].accounts[0] as! BankAccount, amount: CommonBigDecimal(double: 1234.56), currency: "", unparsedUsage: "Usage", bookingDate: CommonDate(year: 2020, month: 5, day: 7), otherPartyName: "Marieke Musterfrau", otherPartyBankCode: nil, otherPartyAccountId: nil, bookingText: "SEPA Ueberweisung", valueDate: CommonDate(year: 2020, month: 5, day: 7))
],
CommonBigDecimal(double: 84.12))
} }
} }