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 { open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse {
var bookedTransactions = mapOf<BankAccount, List<AccountTransaction>>() val balances = response.balances.mapKeys { findMatchingBankAccount(account, it.key) }.filter { it.key != null } as Map<BankAccount, BigDecimal>
var balances = mapOf<BankAccount, BigDecimal>()
account.bankAccounts.firstOrNull()?.let { bankAccount -> // TODO: set bank account also on net.dankito.fints.response.client.GetTransactionsResponse val bookedTransactions = response.bookedTransactions.associateBy { it.account }
bookedTransactions = mapOf(bankAccount to mapTransactions(bankAccount, response.bookedTransactions)) val mappedBookedTransactions = mutableMapOf<BankAccount, List<AccountTransaction>>()
response.balance?.let { balances = mapOf(bankAccount to it) }
bookedTransactions.keys.forEach { accountData ->
findMatchingBankAccount(account, accountData)?.let { bankAccount ->
mappedBookedTransactions.put(bankAccount, mapTransactions(bankAccount, response.bookedTransactions))
}
} }
return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response), return AddAccountResponse(response.isSuccessful, mapErrorToShowToUser(response),
account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan, account, response.supportsRetrievingTransactionsOfLast90DaysWithoutTan,
bookedTransactions, mappedBookedTransactions,
mapOf(), // TODO: map unbooked transactions mapOf(), // TODO: map unbooked transactions
balances, balances,
response.exception) response.exception)
@ -96,6 +99,10 @@ open class fints4javaModelMapper {
return customer.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier } 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> { open fun mapTransactions(bankAccount: BankAccount, transactions: List<net.dankito.fints.model.AccountTransaction>): List<AccountTransaction> {
return transactions.map { mapTransaction(bankAccount, it) } 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) // 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 transactionsOfLast90DaysResponses = mutableListOf<GetTransactionsResponse>()
val balances = mutableMapOf<AccountData, BigDecimal>()
customer.accounts.forEach { account -> customer.accounts.forEach { account ->
if (account.supportsRetrievingAccountTransactions) { if (account.supportsRetrievingAccountTransactions) {
transactionsOfLast90DaysResponses.add( val response = tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, false)
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) { if (didOverwriteUserUnselectedTanProcedure) {
customer.resetSelectedTanProcedure() 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, return AddAccountResponse(synchronizeCustomerResponse.toResponse(), bank, customer,
transactionsOfLast90DaysResponse.isSuccessful, supportsRetrievingTransactionsOfLast90DaysWithoutTan, bookedTransactions, unbookedTransactions, balances)
transactionsOfLast90DaysResponse.bookedTransactions,
transactionsOfLast90DaysResponse.unbookedTransactions,
transactionsOfLast90DaysResponse.balance)
} }
@ -253,7 +255,7 @@ open class FinTsClient @JvmOverloads constructor(
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, true) tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, true)
} }
val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions) val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions, account)
return GetTransactionsResponse(response, return GetTransactionsResponse(response,
bookedAndUnbookedTransactions.first.sortedByDescending { it.bookingDate }, bookedAndUnbookedTransactions.first.sortedByDescending { it.bookingDate },
@ -264,13 +266,13 @@ open class FinTsClient @JvmOverloads constructor(
return GetTransactionsResponse(response) 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 bookedTransactionsString = StringBuilder()
val unbookedTransactionsString = StringBuilder() val unbookedTransactionsString = StringBuilder()
getTransactionsFromResponse(response, transactions, bookedTransactionsString, unbookedTransactionsString) 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 val unbookedTransactions = listOf<Any>() // TODO: implement parsing MT942
return Pair(bookedTransactions, unbookedTransactions) return Pair(bookedTransactions, unbookedTransactions)

View File

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

View File

@ -16,12 +16,13 @@ open class AccountTransaction(
val bookingText: String?, val bookingText: String?,
val valueDate: Date?, val valueDate: Date?,
val openingBalance: BigDecimal?, val openingBalance: BigDecimal?,
val closingBalance: BigDecimal? val closingBalance: BigDecimal?,
val account: AccountData
// TODO: may also add other values from parsed usage lines // TODO: may also add other values from parsed usage lines
) { ) {
// for object deserializers // 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 { override fun toString(): String {

View File

@ -1,5 +1,6 @@
package net.dankito.fints.response.client package net.dankito.fints.response.client
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction import net.dankito.fints.model.AccountTransaction
import net.dankito.fints.model.BankData import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData import net.dankito.fints.model.CustomerData
@ -14,6 +15,6 @@ open class AddAccountResponse(
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false, val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
bookedTransactionsOfLast90Days: List<AccountTransaction> = listOf(), bookedTransactionsOfLast90Days: List<AccountTransaction> = listOf(),
unbookedTransactionsOfLast90Days: List<Any> = 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 package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction import net.dankito.fints.model.AccountTransaction
interface IAccountTransactionsParser { 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 package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import net.dankito.fints.model.AccountTransaction import net.dankito.fints.model.AccountTransaction
import net.dankito.fints.transactions.mt940.IMt940Parser import net.dankito.fints.transactions.mt940.IMt940Parser
import net.dankito.fints.transactions.mt940.Mt940Parser 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) 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 { try {
return statement.transactions.map { mapToAccountTransaction(statement, it) } return statement.transactions.map { mapToAccountTransaction(statement, it, account) }
} catch (e: Exception) { } catch (e: Exception) {
log.error("Could not map AccountStatement '$statement' to AccountTransactions", e) log.error("Could not map AccountStatement '$statement' to AccountTransactions", e)
} }
@ -36,7 +37,7 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
return listOf() return listOf()
} }
protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction): AccountTransaction { protected open fun mapToAccountTransaction(statement: AccountStatement, transaction: Transaction, account: AccountData): AccountTransaction {
return AccountTransaction( return AccountTransaction(
mapAmount(transaction.turnover), mapAmount(transaction.turnover),
statement.closingBalance.currency, statement.closingBalance.currency,
@ -48,7 +49,8 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
transaction.details?.bookingText, transaction.details?.bookingText,
transaction.turnover.valueDate, transaction.turnover.valueDate,
mapAmount(statement.openingBalance), // TODO: that's not true, these are the opening and closing balance of 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 package net.dankito.fints.transactions
import net.dankito.fints.model.AccountData
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
@ -24,7 +25,7 @@ class Mt940AccountTransactionsParserTest {
// when // when
val result = underTest.parseTransactions(transactionsString) val result = underTest.parseTransactions(transactionsString, AccountData())
// then // then