Fixed that bookedTransactions just got associated with first available bank account, which is just not true; Fixed grouping balances by BankAccount

This commit is contained in:
dankl 2020-01-25 18:05:14 +01:00 committed by dankito
parent 438772a6a2
commit be479edc11
8 changed files with 46 additions and 28 deletions

View File

@ -26,17 +26,20 @@ open class fints4javaModelMapper {
}
open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>()
var balances = mapOf<BankAccount, BigDecimal>()
val balances = response.balances.mapKeys { findMatchingBankAccount(account, it.key) }.filter { it.key != null } as Map<BankAccount, BigDecimal>
account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse
bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions))
response.balance?.let { balances = mapOf(bankAccount to it) }
val bookedTransactions = response.bookedTransactions.associateBy { it.account }
val mappedBookedTransactions = mutableMapOf<BankAccount, List<AccountTransaction>>()
bookedTransactions.keys.forEach { accountData ->
findMatchingBankAccount(account, accountData)?.let { bankAccount ->
mappedBookedTransactions.put(bankAccount, mapTransactions(bankAccount, response.bookedTransactions))
}
}
return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response),
account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan,
bookedTransactions,
mappedBookedTransactions,
mapOf(), // TODO: map unbooked transactions
balances,
response.exception)
@ -96,6 +99,10 @@ open class fints4javaModelMapper {
return customer.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier }
}
open fun findMatchingBankAccount(account: Account, accountData: AccountData): BankAccount? {
return account.bankAccounts.firstOrNull { it.identifier == accountData.accountIdentifier }
}
open fun mapTransactions(bankAccount: BankAccount, transactions: List<net.dankito.fints.model.AccountTransaction>): List<AccountTransaction> {
return transactions.map { mapTransaction(bankAccount, it) }

View File

@ -154,23 +154,25 @@ open class FinTsClient @JvmOverloads constructor(
// also check if retrieving account transactions of last 90 days without tan is supported (and thereby may retrieve first account transactions)
val transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>()
val balances = mutableMapOf<AccountData, BigDecimal>()
customer.accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) {
transactionsOfLast90DaysResponses.add(
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false))
val response = tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false)
transactionsOfLast90DaysResponses.add(response)
response.balance?.let { balances.put(account, it) }
}
}
val transactionsOfLast90DaysResponse = transactionsOfLast90DaysResponses.firstOrNull { it.isSuccessful } ?: GetTransactionsResponse(Response(false))
if (didOverwriteUserUnselectedTanProcedure) {
customer.resetSelectedTanProcedure()
}
val supportsRetrievingTransactionsOfLast90DaysWithoutTan = transactionsOfLast90DaysResponses.firstOrNull { it.isSuccessful } != null
val unbookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.unbookedTransactions }
val bookedTransactions = transactionsOfLast90DaysResponses.flatMap { it.bookedTransactions }
return AddAccountResponse(synchronizeCustomerResponse.toResponse(), bank, customer,
transactionsOfLast90DaysResponse.isSuccessful,
transactionsOfLast90DaysResponse.bookedTransactions,
transactionsOfLast90DaysResponse.unbookedTransactions,
transactionsOfLast90DaysResponse.balance)
supportsRetrievingTransactionsOfLast90DaysWithoutTan, bookedTransactions, unbookedTransactions, balances)
}
@ -253,7 +255,7 @@ open class FinTsClient @JvmOverloads constructor(
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, true)
}
val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions)
val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions, account)
return GetTransactionsResponse(response,
bookedAndUnbookedTransactions.first.sortedByDescending { it.bookingDate },
@ -264,13 +266,13 @@ open class FinTsClient @JvmOverloads constructor(
return GetTransactionsResponse(response)
}
protected open fun getTransactionsFromResponse(response: Response, transactions: ReceivedAccountTransactions): Pair<List<AccountTransaction>, List<Any>> {
protected open fun getTransactionsFromResponse(response: Response, transactions: ReceivedAccountTransactions, account: AccountData): Pair<List<AccountTransaction>, List<Any>> {
val bookedTransactionsString = StringBuilder()
val unbookedTransactionsString = StringBuilder()
getTransactionsFromResponse(response, transactions, bookedTransactionsString, unbookedTransactionsString)
val bookedTransactions = mt940Parser.parseTransactions(bookedTransactionsString.toString())
val bookedTransactions = mt940Parser.parseTransactions(bookedTransactionsString.toString(), account)
val unbookedTransactions = listOf<Any>() // TODO: implement parsing MT942
return Pair(bookedTransactions, unbookedTransactions)

View File

@ -1,5 +1,6 @@
package net.dankito.fints.model
import net.dankito.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
import net.dankito.fints.response.segments.AccountType
import net.dankito.fints.response.segments.JobParameters
@ -25,6 +26,8 @@ open class AccountData(
var triedToRetrieveTransactionsOfLast90DaysWithoutTan: Boolean = false
) {
internal constructor() : this("", null, Laenderkennzeichen.Germany, "", null, "", null, null, "", null, null, listOf())
override fun toString(): String {
return "$productName $accountIdentifier $accountHolderName"
}

View File

@ -16,12 +16,13 @@ open class AccountTransaction(
val bookingText: String?,
val valueDate: Date?,
val openingBalance: BigDecimal?,
val closingBalance: BigDecimal?
val closingBalance: BigDecimal?,
val account: AccountData
// TODO: may also add other values from parsed usage lines
) {
// for object deserializers
private constructor() : this(0.toBigDecimal(),"", "", Date(), null, null, null, null, null, null, null)
internal constructor() : this(0.toBigDecimal(),"", "", Date(), null, null, null, null, null, null, null, AccountData())
override fun toString(): String {

View File

@ -1,5 +1,6 @@
package net.dankito.fints.response.client
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
@ -14,6 +15,6 @@ open class AddAccountResponse(
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
bookedTransactionsOfLast90Days: List<AccountTransaction> = listOf(),
unbookedTransactionsOfLast90Days: List<Any> = listOf(),
balance: BigDecimal? = null
val balances: Map<AccountData, BigDecimal> = mapOf()
)
: GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balance)
: GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e })

View File

@ -1,10 +1,11 @@
package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction
interface IAccountTransactionsParser {
fun parseTransactions(transactionsString: String): List<AccountTransaction>
fun parseTransactions(transactionsString: String, account: AccountData): List<AccountTransaction>
}

View File

@ -1,5 +1,6 @@
package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction
import net.dankito.fints.transactions.mt940.IMt940Parser
import net.dankito.fints.transactions.mt940.Mt940Parser
@ -20,15 +21,15 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
}
override fun parseTransactions(transactionsString: String): List<AccountTransaction> {
override fun parseTransactions(transactionsString: String, account: AccountData): List<AccountTransaction> {
val accountStatements = mt940Parser.parseMt940String(transactionsString)
return accountStatements.flatMap { mapToAccountTransactions(it) }
return accountStatements.flatMap { mapToAccountTransactions(it, account) }
}
protected open fun mapToAccountTransactions(statement: AccountStatement): List<AccountTransaction> {
protected open fun mapToAccountTransactions(statement: AccountStatement, account: AccountData): List<AccountTransaction> {
try {
return statement.transactions.map { mapToAccountTransaction(statement, it) }
return statement.transactions.map { mapToAccountTransaction(statement, it, account) }
} catch (e: Exception) {
log.error("Could not map AccountStatement '$statement' to AccountTransactions", e)
}
@ -36,7 +37,7 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
return listOf()
}
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction): AccountTransaction {
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction {
return AccountTransaction(
mapAmount(transaction.turnover),
statement.closingBalance.currency,
@ -48,7 +49,8 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
transaction.details?.bookingText,
transaction.turnover.valueDate,
mapAmount(statement.openingBalance), // TODO: that's not true, these are the opening and closing balance of
mapAmount(statement.closingBalance) // all transactions of this day, not this specific transaction's ones
mapAmount(statement.closingBalance), // all transactions of this day, not this specific transaction's ones
account
)
}

View File

@ -1,5 +1,6 @@
package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
@ -24,7 +25,7 @@ class Mt940AccountTransactionsParserTest {
// when
val result = underTest.parseTransactions(transactionsString)
val result = underTest.parseTransactions(transactionsString, AccountData())
// then