Implemented storing and displaying for which period account transactions have been fetched

This commit is contained in:
dankito 2020-09-22 03:20:03 +02:00
parent 700e3e6da9
commit 13d95f4bda
18 changed files with 62 additions and 26 deletions

View File

@ -47,7 +47,8 @@ open class FinTsClient(
val FindAccountTransactionsStartRegex = Regex("^HIKAZ:\\d:\\d:\\d\\+@\\d+@", RegexOption.MULTILINE)
val FindAccountTransactionsEndRegex = Regex("^-'", RegexOption.MULTILINE)
const val NinetyDaysMillis = 90 * 24 * 60 * 60 * 1000L
const val OneDayMillis = 24 * 60 * 60 * 1000L
const val NinetyDaysMillis = 90 * OneDayMillis
private val log = LoggerFactory.getLogger(FinTsClient::class)
@ -404,8 +405,12 @@ open class FinTsClient(
closeDialog(dialogContext)
val successful = response.successful && (parameter.alsoRetrieveBalance == false || balance != null)
val fromDate = parameter.fromDate
?: dialogContext.bank.countDaysForWhichTransactionsAreKept?.let { Date(Date.today.millisSinceEpoch - it * OneDayMillis) }
?: bookedTransactions.map { it.valueDate }.sortedBy { it.millisSinceEpoch }.firstOrNull()
val retrievedData = RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions, fromDate, parameter.toDate ?: Date.today)
callback(GetTransactionsResponse(response, listOf(RetrievedAccountData(parameter.account, successful, balance, bookedTransactions, unbookedTransactions)),
callback(GetTransactionsResponse(response, listOf(retrievedData),
if (parameter.maxCountEntries != null) parameter.isSettingMaxCountEntriesAllowedByBank else null
))
}

View File

@ -1,18 +1,22 @@
package net.dankito.banking.fints.model
import net.dankito.utils.multiplatform.Date
open class RetrievedAccountData(
open val accountData: AccountData,
open val successfullyRetrievedData: Boolean,
open val balance: Money?,
open var bookedTransactions: Collection<AccountTransaction>,
open var unbookedTransactions: Collection<Any>
open var unbookedTransactions: Collection<Any>,
open val retrievedTransactionsFrom: Date?,
open val retrievedTransactionsTo: Date?
) {
companion object {
fun unsuccessful(account: AccountData): RetrievedAccountData {
return RetrievedAccountData(account, false, null, listOf(), listOf())
return RetrievedAccountData(account, false, null, listOf(), listOf(), null, null)
}
fun unsuccessfulList(account: AccountData): List<RetrievedAccountData> {

View File

@ -24,7 +24,8 @@ open class BankAccount(
override var type: BankAccountType = BankAccountType.Girokonto,
override var productName: String? = null,
override var accountLimit: String? = null,
override var lastRetrievedTransactionsTimestamp: Date? = null,
override var retrievedTransactionsFromOn: Date? = null,
override var retrievedTransactionsUpTo: Date? = null,
override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false,

View File

@ -22,7 +22,8 @@ open class BankAccountEntity(
override var type: BankAccountType = BankAccountType.Girokonto,
override var productName: String? = null,
override var accountLimit: String? = null,
override var lastRetrievedTransactionsTimestamp: Date? = null,
override var retrievedTransactionsFromOn: Date? = null,
override var retrievedTransactionsUpTo: Date? = null,
override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false,
override var supportsTransferringMoney: Boolean = false,

View File

@ -27,6 +27,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.extensions.getDimension
import net.dankito.utils.multiplatform.sum
import java.text.DateFormat
import javax.inject.Inject
@ -36,6 +37,8 @@ class HomeFragment : Fragment() {
val TransactionsCannotBeRetrievedStates = listOf(TransactionsRetrievalState.AccountTypeNotSupported, TransactionsRetrievalState.AccountDoesNotSupportFetchingTransactions)
val RetrievedTransactionsPeriodDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM)
}
@ -272,14 +275,20 @@ class HomeFragment : Fragment() {
btnRetrieveTransactions.visibility = if (TransactionsCannotBeRetrievedStates.contains(transactionsRetrievalState)) View.GONE else View.VISIBLE
btnAddAccount.visibility = if (noAccountsAddedYet) View.VISIBLE else View.GONE
var messageArgs = mutableListOf<String>()
val transactionsRetrievalStateMessageId = when (transactionsRetrievalState) {
TransactionsRetrievalState.AccountTypeNotSupported -> R.string.fragment_home_transactions_retrieval_state_account_type_not_supported
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.NoTransactionsInRetrievedPeriod -> {
val account = presenter.selectedBankAccounts.first()
account.retrievedTransactionsFromOn?.let { messageArgs.add(RetrievedTransactionsPeriodDateFormat.format(it)) }
account.retrievedTransactionsUpTo?.let { messageArgs.add(RetrievedTransactionsPeriodDateFormat.format(it)) }
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) } ?: ""
txtNoTransactionsFetchedMessage.text = transactionsRetrievalStateMessageId?.let { requireContext().getString(transactionsRetrievalStateMessageId, *messageArgs.toTypedArray()) } ?: ""
}
private fun setFetchAllTransactionsView() {

View File

@ -42,7 +42,7 @@
<string name="fragment_home_transactions_retrieval_state_account_type_not_supported">Kontotyp wird von App nicht unterstützt</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_transactions_retrieval_state_no_transactions_in_retrieved_period">Empfangener Zeitraum vom %1$s - %2$s enthält keine Umsätze</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_fetch_all_account_transactions">Ältere Umsätze abrufen (erfordert TAN)</string>

View File

@ -42,7 +42,7 @@
<string name="fragment_home_transactions_retrieval_state_account_type_not_supported">Account type not supported by app</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_transactions_retrieval_state_no_transactions_in_retrieved_period">There haven\'t been any transactions in retrieved period from %1$s - %2$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_fetch_all_account_transactions">Fetch earlier transactions (requires TAN)</string>

View File

@ -18,7 +18,8 @@ open class BankAccount @JvmOverloads constructor(
override var type: BankAccountType = BankAccountType.Girokonto,
override var productName: String? = null,
override var accountLimit: String? = null,
override var lastRetrievedTransactionsTimestamp: Date? = null,
override var retrievedTransactionsFromOn: Date? = null,
override var retrievedTransactionsUpTo: Date? = null,
override var supportsRetrievingAccountTransactions: Boolean = false,
override var supportsRetrievingBalance: Boolean = false,
override var supportsTransferringMoney: Boolean = false,

View File

@ -19,7 +19,8 @@ interface IBankAccount<TTransaction: IAccountTransaction> : OrderedDisplayable {
var type: BankAccountType
var productName: String?
var accountLimit: String?
var lastRetrievedTransactionsTimestamp: Date?
var retrievedTransactionsFromOn: Date?
var retrievedTransactionsUpTo: Date?
var supportsRetrievingAccountTransactions: Boolean
var supportsRetrievingBalance: Boolean
var supportsTransferringMoney: Boolean

View File

@ -1,6 +1,7 @@
package net.dankito.banking.ui.model
import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date
open class RetrievedAccountData(
@ -8,5 +9,7 @@ open class RetrievedAccountData(
open val successfullyRetrievedData: Boolean,
open val balance: BigDecimal?,
open val bookedTransactions: Collection<IAccountTransaction>,
open val unbookedTransactions: List<Any>
open val unbookedTransactions: List<Any>,
open val retrievedTransactionsFrom: Date?,
open val retrievedTransactionsTo: Date?
)

View File

@ -11,7 +11,7 @@ open class GetTransactionsResponse(
open val tanRequiredButWeWereToldToAbortIfSo: Boolean = false
) : BankingClientResponse(true /* any value */, errorToShowToUser, userCancelledAction) {
constructor(account: TypedBankAccount, errorToShowToUser: String) : this(listOf(RetrievedAccountData(account, false, null, listOf(), listOf())), errorToShowToUser)
constructor(account: TypedBankAccount, errorToShowToUser: String) : this(listOf(RetrievedAccountData(account, false, null, listOf(), listOf(), null, null)), errorToShowToUser)
constructor(retrievedData: RetrievedAccountData) : this(listOf(retrievedData))

View File

@ -327,7 +327,7 @@ open class BankingPresenter(
}
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.retrievedTransactionsUpTo?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
}
@ -336,7 +336,10 @@ open class BankingPresenter(
if (response.successful) {
response.retrievedData.forEach { retrievedData ->
val account = retrievedData.account
account.lastRetrievedTransactionsTimestamp = startDate
account.retrievedTransactionsUpTo = startDate
if (account.retrievedTransactionsFromOn == null || retrievedData.retrievedTransactionsFrom?.isBefore(account.retrievedTransactionsFromOn!!) == true) {
account.retrievedTransactionsFromOn = retrievedData.retrievedTransactionsFrom
}
if (didFetchAllTransactions || didFetchAllTransactionsStoredOnBankServer(account, retrievedData.bookedTransactions)) {
account.haveAllTransactionsBeenFetched = true
@ -368,7 +371,7 @@ open class BankingPresenter(
asyncRunner.runAsync { // don't block retrieving next chunk by blocked saving to db / json
updateAccountTransactions(bankAccount, accountTransactionsChunk)
callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(RetrievedAccountData(bankAccount, true, null, accountTransactionsChunk, listOf())))
callRetrievedAccountTransactionsResponseListener(GetTransactionsResponse(RetrievedAccountData(bankAccount, true, null, accountTransactionsChunk, listOf(), null, null)))
}
}
}
@ -770,7 +773,7 @@ open class BankingPresenter(
return TransactionsRetrievalState.RetrievedTransactions
}
if (account.lastRetrievedTransactionsTimestamp != null) {
if (account.retrievedTransactionsUpTo != null) {
return TransactionsRetrievalState.NoTransactionsInRetrievedPeriod
}

View File

@ -79,7 +79,7 @@
"Account type not supported by app" = "Account type not supported by app";
"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";
"There haven't been any transactions in retrieved period from %@ - %@" = "There haven't been any transactions in retrieved period from %@ - %@";
"Transfer money to %@" = "Transfer money to %@";
"New transfer with same data" = "New transfer with same data";

View File

@ -79,7 +79,7 @@
"Account type not supported by app" = "Kontotyp wird von App nicht unterstützt";
"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";
"There haven't been any transactions in retrieved period from %@ - %@" = "Empfangener Zeitraum vom %@ - %@ enthält keine Umsätze";
"Transfer money to %@" = "Neue Überweisung an %@";
"New transfer with same data" = "Neue Überweisung mit gleichen Daten";

View File

@ -58,7 +58,7 @@ class Mapper {
}
func map(_ customer: ICustomer, _ account: PersistedBankAccount) -> IBankAccount {
let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, lastRetrievedTransactionsTimestamp: map(account.lastRetrievedTransactionsTimestamp), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: [])
let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, retrievedTransactionsFromOn: map(account.retrievedTransactionsFromOn), retrievedTransactionsUpTo: map(account.retrievedTransactionsUpTo), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: [])
mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
mapped.isAccountTypeSupported = account.isAccountTypeSupported
@ -92,7 +92,8 @@ class Mapper {
mapped.isAccountTypeSupported = account.isAccountTypeSupported
mapped.productName = account.productName
mapped.accountLimit = account.accountLimit
mapped.lastRetrievedTransactionsTimestamp = account.lastRetrievedTransactionsTimestamp?.date
mapped.retrievedTransactionsFromOn = account.retrievedTransactionsFromOn?.date
mapped.retrievedTransactionsUpTo = account.retrievedTransactionsUpTo?.date
mapped.supportsRetrievingAccountTransactions = account.supportsRetrievingAccountTransactions
mapped.supportsRetrievingBalance = account.supportsRetrievingBalance
mapped.supportsTransferringMoney = account.supportsTransferringMoney

View File

@ -6,6 +6,8 @@ struct AccountTransactionsDialog: View {
static private let DoNotShowFetchAllTransactionsOverlayForUserDefaultsKeyPrefix = "DoNotShowFetchAllTransactionsOverlayFor_"
static private let RetrievedTransactionsPeriodDateFormat = DateFormatter()
private let title: String
@ -73,6 +75,8 @@ struct AccountTransactionsDialog: View {
self.title = title
self.showBankIcons = showBankIcons
Self.RetrievedTransactionsPeriodDateFormat.dateStyle = .medium
}
@ -246,7 +250,8 @@ struct AccountTransactionsDialog: View {
return "No transactions fetched yet"
}
else if state == .notransactionsinretrievedperiod {
return "There haven't been any transactions in retrieved period"
let account = presenter.selectedBankAccounts.first!
return "There haven't been any transactions in retrieved period from \(mapDate(account.retrievedTransactionsFromOn)) - \(mapDate(account.retrievedTransactionsUpTo))"
}
else if state == .accountdoesnotsupportfetchingtransactions {
return "Account does not support retrieving transactions"

View File

@ -56,7 +56,9 @@ open class fints4kModelMapper(protected val modelCreator: IModelCreator) {
retrievedData.successfullyRetrievedData,
retrievedData.balance?.toBigDecimal(),
mapTransactions(account, retrievedData.bookedTransactions),
listOf() // TODO: map unbooked transactions
listOf(), // TODO: map unbooked transactions
retrievedData.retrievedTransactionsFrom,
retrievedData.retrievedTransactionsTo
)
}

View File

@ -102,7 +102,7 @@ open class hbci4jBankingClient(
response.retrievedData.first()
}
else {
RetrievedAccountData(account, false, null, listOf(), listOf())
RetrievedAccountData(account, false, null, listOf(), listOf(), null)
}
}
@ -180,7 +180,7 @@ open class hbci4jBankingClient(
}
return GetTransactionsResponse(RetrievedAccountData(account, true, balance.toBigDecimal(),
accountTransactionMapper.mapAccountTransactions(account, result), listOf()))
accountTransactionMapper.mapAccountTransactions(account, result), listOf(), parameter.fromDate, parameter.toDate))
}
catch(e: Exception) {
log.error("Could not get accounting details for bank ${credentials.bankCode}", e)