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:
parent
438772a6a2
commit
be479edc11
|
@ -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) }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 })
|
|
@ -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>
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue