From be479edc117097320c2805710861dd8990fa02de Mon Sep 17 00:00:00 2001 From: dankl Date: Sat, 25 Jan 2020 18:05:14 +0100 Subject: [PATCH] Fixed that bookedTransactions just got associated with first available bank account, which is just not true; Fixed grouping balances by BankAccount --- .../banking/mapper/fints4javaModelMapper.kt | 19 +++++++++++----- .../kotlin/net/dankito/fints/FinTsClient.kt | 22 ++++++++++--------- .../net/dankito/fints/model/AccountData.kt | 3 +++ .../dankito/fints/model/AccountTransaction.kt | 5 +++-- .../response/client/AddAccountResponse.kt | 5 +++-- .../IAccountTransactionsParser.kt | 3 ++- .../Mt940AccountTransactionsParser.kt | 14 +++++++----- .../Mt940AccountTransactionsParserTest.kt | 3 ++- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt index 5b6d6377..f8a5c863 100644 --- a/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt +++ b/fints4javaBankingClient/src/main/kotlin/net/dankito/banking/mapper/fints4javaModelMapper.kt @@ -26,17 +26,20 @@ open class fints4javaModelMapper { } open fun mapResponse(account: Account, response: net.dankito.fints.response.client.AddAccountResponse): AddAccountResponse { - var bookedTransactions = mapOf>() - var balances = mapOf() + val balances = response.balances.mapKeys { findMatchingBankAccount(account, it.key) }.filter { it.key != null } as Map - 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>() + + 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): List { return transactions.map { mapTransaction(bankAccount, it) } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index fcec6233..0ee62304 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -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() + val balances = mutableMapOf() 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> { + protected open fun getTransactionsFromResponse(response: Response, transactions: ReceivedAccountTransactions, account: AccountData): Pair, List> { 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() // TODO: implement parsing MT942 return Pair(bookedTransactions, unbookedTransactions) diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountData.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountData.kt index 3ff6c84e..87bce611 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountData.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountData.kt @@ -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" } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountTransaction.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountTransaction.kt index 96bfadc9..e4fb559e 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountTransaction.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/model/AccountTransaction.kt @@ -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 { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/client/AddAccountResponse.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/client/AddAccountResponse.kt index fa5bd816..4e87702c 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/client/AddAccountResponse.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/client/AddAccountResponse.kt @@ -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 = listOf(), unbookedTransactionsOfLast90Days: List = listOf(), - balance: BigDecimal? = null + val balances: Map = mapOf() ) - : GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balance) \ No newline at end of file + : GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e }) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/IAccountTransactionsParser.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/IAccountTransactionsParser.kt index bbaa3340..df24fadb 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/IAccountTransactionsParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/IAccountTransactionsParser.kt @@ -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 + fun parseTransactions(transactionsString: String, account: AccountData): List } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParser.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParser.kt index c2526ce5..41e51ff6 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParser.kt @@ -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 { + override fun parseTransactions(transactionsString: String, account: AccountData): List { val accountStatements = mt940Parser.parseMt940String(transactionsString) - return accountStatements.flatMap { mapToAccountTransactions(it) } + return accountStatements.flatMap { mapToAccountTransactions(it, account) } } - protected open fun mapToAccountTransactions(statement: AccountStatement): List { + protected open fun mapToAccountTransactions(statement: AccountStatement, account: AccountData): List { 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 ) } diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt index eb177d56..69fd83e2 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/transactions/Mt940AccountTransactionsParserTest.kt @@ -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