Implemented parsing each received MT940 part immediately and passing it on to UI
This commit is contained in:
parent
56308687fb
commit
e749a88c83
|
@ -307,41 +307,37 @@ open class FinTsClient @JvmOverloads constructor(
|
|||
|
||||
val message = messageBuilder.createGetTransactionsMessage(parameter, account, dialogContext)
|
||||
|
||||
val bookedTransactions = mutableListOf<AccountTransaction>()
|
||||
var remainingMt940String = ""
|
||||
|
||||
dialogContext.chunkedResponseHandler = { response ->
|
||||
response.getFirstSegmentById<ReceivedAccountTransactions>(InstituteSegmentId.AccountTransactionsMt940)?.let { transactionsSegment ->
|
||||
val (chunkTransaction, remainder) = mt940Parser.parseTransactionsChunk(remainingMt940String + transactionsSegment.bookedTransactionsString, account)
|
||||
|
||||
bookedTransactions.addAll(chunkTransaction)
|
||||
remainingMt940String = remainder
|
||||
|
||||
parameter.retrievedChunkListener?.invoke(bookedTransactions)
|
||||
}
|
||||
}
|
||||
|
||||
val response = getAndHandleResponseForMessage(message, dialogContext)
|
||||
|
||||
closeDialog(dialogContext)
|
||||
|
||||
|
||||
response.getFirstSegmentById<ReceivedAccountTransactions>(InstituteSegmentId.AccountTransactionsMt940)?.let { transactions ->
|
||||
// just retrieved all transactions -> check if retrieving that ones of last 90 days is possible without entering TAN
|
||||
if (account.supportsRetrievingTransactionsOfLast90DaysWithoutTan == null &&
|
||||
response.successful && transactions.bookedTransactionsString.isNotEmpty() && parameter.fromDate == null) {
|
||||
response.successful && bookedTransactions.isNotEmpty() && parameter.fromDate == null) {
|
||||
tryGetTransactionsOfLast90DaysWithoutTan(bank, customer, account, true)
|
||||
}
|
||||
|
||||
val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions, account)
|
||||
|
||||
return GetTransactionsResponse(response,
|
||||
bookedAndUnbookedTransactions.first.sortedByDescending { it.bookingDate },
|
||||
bookedAndUnbookedTransactions.second,
|
||||
bookedTransactions.sortedByDescending { it.bookingDate },
|
||||
listOf(), // TODO: implement parsing MT942
|
||||
balance)
|
||||
}
|
||||
|
||||
return GetTransactionsResponse(response)
|
||||
}
|
||||
|
||||
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(), account)
|
||||
val unbookedTransactions = listOf<Any>() // TODO: implement parsing MT942
|
||||
|
||||
return Pair(bookedTransactions, unbookedTransactions)
|
||||
}
|
||||
|
||||
protected open fun getTransactionsFromResponse(response: Response, transactions: ReceivedAccountTransactions,
|
||||
bookedTransactionsString: StringBuilder, unbookedTransactionsString: StringBuilder) {
|
||||
|
||||
|
@ -561,11 +557,16 @@ open class FinTsClient @JvmOverloads constructor(
|
|||
// if there's a Aufsetzpunkt (continuationId) set, then response is not complete yet, there's more information to fetch by sending this Aufsetzpunkt
|
||||
handledResponse.aufsetzpunkt?.let { continuationId ->
|
||||
if (handledResponse.followUpResponse == null) { // for re-sent messages followUpResponse is already set and dialog already closed -> would be overwritten with an error response that dialog is closed
|
||||
if (message.isSendEnteredTanMessage() == false) { // for sending TAN no follow up message can be created -> filter out, otherwise chunkedResponseHandler would get called twice for same response
|
||||
dialogContext.chunkedResponseHandler?.invoke(handledResponse)
|
||||
}
|
||||
|
||||
handledResponse.followUpResponse = getFollowUpMessageForContinuationId(handledResponse, continuationId, message, dialogContext)
|
||||
|
||||
handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null
|
||||
}
|
||||
}
|
||||
?: run { dialogContext.chunkedResponseHandler?.invoke(handledResponse) }
|
||||
|
||||
return handledResponse
|
||||
}
|
||||
|
@ -794,7 +795,7 @@ open class FinTsClient @JvmOverloads constructor(
|
|||
protected open fun resendMessageInNewDialog(lastCreatedMessage: MessageBuilderResult?, previousDialogContext: DialogContext): Response {
|
||||
|
||||
lastCreatedMessage?.let { // do not use previousDialogContext.currentMessage as this may is previous dialog's dialog close message
|
||||
val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product)
|
||||
val newDialogContext = DialogContext(previousDialogContext.bank, previousDialogContext.customer, previousDialogContext.product, chunkedResponseHandler = previousDialogContext.chunkedResponseHandler)
|
||||
|
||||
val initDialogResponse = initDialog(newDialogContext)
|
||||
if (initDialogResponse.successful == false) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.dankito.banking.fints.messages
|
||||
|
||||
import net.dankito.banking.fints.messages.segmente.Segment
|
||||
import net.dankito.banking.fints.messages.segmente.implementierte.ZweiSchrittTanEinreichung
|
||||
|
||||
|
||||
open class MessageBuilderResult(
|
||||
|
@ -25,4 +26,10 @@ open class MessageBuilderResult(
|
|||
open val getHighestAllowedVersion: Int?
|
||||
get() = allowedVersions.max()
|
||||
|
||||
open fun isSendEnteredTanMessage(): Boolean {
|
||||
// contains only a ZweiSchrittTanEinreichung segment
|
||||
return messageBodySegments.size == 1
|
||||
&& messageBodySegments.first() is ZweiSchrittTanEinreichung
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,8 @@ open class DialogContext(
|
|||
var dialogId: String = InitialDialogId,
|
||||
var response: Response? = null,
|
||||
var didBankCloseDialog: Boolean = false,
|
||||
var previousMessageInDialog: MessageBuilderResult? = null
|
||||
var previousMessageInDialog: MessageBuilderResult? = null,
|
||||
var chunkedResponseHandler: ((Response) -> Unit)? = null
|
||||
) : MessageBaseData(bank, customer, product) {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -7,5 +7,6 @@ open class GetTransactionsParameter @JvmOverloads constructor(
|
|||
val alsoRetrieveBalance: Boolean = true,
|
||||
val fromDate: Date? = null,
|
||||
val toDate: Date? = null,
|
||||
val maxCountEntries: Int? = null
|
||||
val maxCountEntries: Int? = null,
|
||||
val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null
|
||||
)
|
|
@ -8,4 +8,6 @@ interface IAccountTransactionsParser {
|
|||
|
||||
fun parseTransactions(transactionsString: String, account: AccountData): List<AccountTransaction>
|
||||
|
||||
fun parseTransactionsChunk(transactionsChunk: String, account: AccountData): Pair<List<AccountTransaction>, String>
|
||||
|
||||
}
|
|
@ -27,6 +27,12 @@ open class Mt940AccountTransactionsParser @JvmOverloads constructor(
|
|||
return accountStatements.flatMap { mapToAccountTransactions(it, account) }
|
||||
}
|
||||
|
||||
override fun parseTransactionsChunk(transactionsChunk: String, account: AccountData): Pair<List<AccountTransaction>, String> {
|
||||
val (accountStatements, remainder) = mt940Parser.parseTransactionsChunk(transactionsChunk)
|
||||
|
||||
return Pair(accountStatements.flatMap { mapToAccountTransactions(it, account) }, remainder)
|
||||
}
|
||||
|
||||
protected open fun mapToAccountTransactions(statement: AccountStatement, account: AccountData): List<AccountTransaction> {
|
||||
try {
|
||||
return statement.transactions.map { mapToAccountTransaction(statement, it, account) }
|
||||
|
|
|
@ -7,4 +7,6 @@ interface IMt940Parser {
|
|||
|
||||
fun parseMt940String(mt940String: String): List<AccountStatement>
|
||||
|
||||
fun parseTransactionsChunk(mt940Chunk: String): Pair<List<AccountStatement>, String>
|
||||
|
||||
}
|
|
@ -73,6 +73,23 @@ open class Mt940Parser : IMt940Parser {
|
|||
return listOf()
|
||||
}
|
||||
|
||||
override fun parseTransactionsChunk(mt940Chunk: String): Pair<List<AccountStatement>, String> {
|
||||
try {
|
||||
val singleAccountStatementsStrings = splitIntoSingleAccountStatements(mt940Chunk)
|
||||
|
||||
val transactions = singleAccountStatementsStrings.mapNotNull { parseAccountStatement(it) }
|
||||
|
||||
val remainder = if (singleAccountStatementsStrings.size == transactions.size + 1) singleAccountStatementsStrings.last()
|
||||
else ""
|
||||
|
||||
return Pair(transactions, remainder)
|
||||
} catch (e: Exception) {
|
||||
log.error("Could not parse account statements from MT940 string:\n$mt940Chunk", e)
|
||||
}
|
||||
|
||||
return Pair(listOf(), "")
|
||||
}
|
||||
|
||||
|
||||
protected open fun splitIntoSingleAccountStatements(mt940String: String): List<String> {
|
||||
return mt940String.split(AccountStatementsSeparatorPattern)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package net.dankito.banking.ui.model.parameters
|
||||
|
||||
import net.dankito.banking.ui.model.AccountTransaction
|
||||
import java.util.*
|
||||
|
||||
|
||||
open class GetTransactionsParameter(
|
||||
val alsoRetrieveBalance: Boolean = true,
|
||||
val fromDate: Date? = null,
|
||||
val toDate: Date? = null
|
||||
val toDate: Date? = null,
|
||||
val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null
|
||||
) {
|
||||
|
||||
constructor() : this(true, null, null) // for Java
|
||||
|
|
|
@ -268,7 +268,7 @@ open class BankingPresenter(
|
|||
getClientForAccount(bankAccount.account)?.let { client ->
|
||||
val startDate = Date()
|
||||
|
||||
client.getTransactionsAsync(bankAccount, net.dankito.banking.ui.model.parameters.GetTransactionsParameter(true, fromDate)) { response ->
|
||||
client.getTransactionsAsync(bankAccount, net.dankito.banking.ui.model.parameters.GetTransactionsParameter(true, fromDate, null, { receivedAccountsTransactionChunk(bankAccount, it) } )) { response ->
|
||||
|
||||
retrievedAccountTransactions(bankAccount, startDate, response)
|
||||
|
||||
|
@ -299,6 +299,14 @@ open class BankingPresenter(
|
|||
callRetrievedAccountTransactionsResponseListener(bankAccount, response)
|
||||
}
|
||||
|
||||
protected open fun receivedAccountsTransactionChunk(bankAccount: BankAccount, accountTransactionsChunk: List<AccountTransaction>) {
|
||||
if (accountTransactionsChunk.isNotEmpty()) {
|
||||
bankAccount.addBookedTransactions(accountTransactionsChunk)
|
||||
|
||||
callRetrievedAccountTransactionsResponseListener(bankAccount, GetTransactionsResponse(true, null, mapOf(bankAccount to accountTransactionsChunk)))
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun updateAccountTransactionsAndBalances(bankAccount: BankAccount, response: GetTransactionsResponse) {
|
||||
|
||||
response.bookedTransactions.forEach { entry ->
|
||||
|
|
|
@ -106,7 +106,8 @@ open class fints4kBankingClient(
|
|||
callback(GetTransactionsResponse(false, "Cannot find account for ${bankAccount.identifier}")) // TODO: translate
|
||||
}
|
||||
else {
|
||||
client.getTransactionsAsync(net.dankito.banking.fints.model.GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, parameter.toDate), account) { response ->
|
||||
client.getTransactionsAsync(net.dankito.banking.fints.model.GetTransactionsParameter(parameter.alsoRetrieveBalance, parameter.fromDate, parameter.toDate, null,
|
||||
{ parameter.retrievedChunkListener?.invoke(mapper.mapTransactions(bankAccount, it)) } ), account) { response ->
|
||||
|
||||
val mappedResponse = mapper.mapResponse(bankAccount, response)
|
||||
|
||||
|
|
Loading…
Reference in New Issue