Implemented checking if allowed jobs are supported

This commit is contained in:
dankl 2019-10-13 21:19:11 +02:00 committed by dankito
parent 7e729cb503
commit 7fcc8db798
7 changed files with 153 additions and 22 deletions

View File

@ -1,6 +1,7 @@
package net.dankito.fints package net.dankito.fints
import net.dankito.fints.messages.MessageBuilder import net.dankito.fints.messages.MessageBuilder
import net.dankito.fints.messages.MessageBuilderResult
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID import net.dankito.fints.messages.datenelemente.implementierte.KundensystemID
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
@ -157,7 +158,7 @@ open class FinTsClient @JvmOverloads constructor(
if (parameter.alsoRetrieveBalance) { if (parameter.alsoRetrieveBalance) {
val balanceResponse = getBalanceAfterDialogInit(bank, customer, dialogData) val balanceResponse = getBalanceAfterDialogInit(bank, customer, dialogData)
if (balanceResponse.successful == false) { if (balanceResponse.successful == false && balanceResponse.couldCreateMessage == true) { // don't break here if required HKSAL message is not implemented
return GetTransactionsResponse(balanceResponse) return GetTransactionsResponse(balanceResponse)
} }
@ -169,9 +170,9 @@ open class FinTsClient @JvmOverloads constructor(
dialogData.increaseMessageNumber() dialogData.increaseMessageNumber()
val requestBody = messageBuilder.createGetTransactionsMessage(parameter, bank, customer, product, dialogData) val message = messageBuilder.createGetTransactionsMessage(parameter, bank, customer, product, dialogData)
val response = getAndHandleResponseForMessage(requestBody, bank) val response = getAndHandleResponseForMessage(message, bank)
closeDialog(bank, customer, dialogData) closeDialog(bank, customer, dialogData)
@ -302,6 +303,14 @@ open class FinTsClient @JvmOverloads constructor(
} }
protected open fun getAndHandleResponseForMessage(message: MessageBuilderResult, bank: BankData): Response {
message.createdMessage?.let { requestBody ->
return getAndHandleResponseForMessage(requestBody, bank)
}
return Response(false, messageCreationError = message)
}
protected open fun getAndHandleResponseForMessage(requestBody: String, bank: BankData): Response { protected open fun getAndHandleResponseForMessage(requestBody: String, bank: BankData): Response {
val webResponse = getResponseForMessage(requestBody, bank) val webResponse = getResponseForMessage(requestBody, bank)

View File

@ -0,0 +1,12 @@
package net.dankito.fints.extensions
fun <T> Collection<T>.containsAny(otherCollection: Collection<T>): Boolean {
for (otherItem in otherCollection) {
if (this.contains(otherItem)) {
return true
}
}
return false
}

View File

@ -1,5 +1,6 @@
package net.dankito.fints.messages package net.dankito.fints.messages
import net.dankito.fints.extensions.containsAny
import net.dankito.fints.messages.datenelemente.implementierte.Synchronisierungsmodus import net.dankito.fints.messages.datenelemente.implementierte.Synchronisierungsmodus
import net.dankito.fints.messages.datenelemente.implementierte.tan.TanProcess import net.dankito.fints.messages.datenelemente.implementierte.tan.TanProcess
import net.dankito.fints.messages.segmente.ISegmentNumberGenerator import net.dankito.fints.messages.segmente.ISegmentNumberGenerator
@ -10,6 +11,8 @@ import net.dankito.fints.messages.segmente.id.CustomerSegmentId
import net.dankito.fints.messages.segmente.implementierte.* import net.dankito.fints.messages.segmente.implementierte.*
import net.dankito.fints.messages.segmente.implementierte.sepa.SepaEinzelueberweisung import net.dankito.fints.messages.segmente.implementierte.sepa.SepaEinzelueberweisung
import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version5 import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version5
import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version6
import net.dankito.fints.messages.segmente.implementierte.umsaetze.KontoumsaetzeZeitraumMt940Version7
import net.dankito.fints.messages.segmente.implementierte.umsaetze.Saldenabfrage import net.dankito.fints.messages.segmente.implementierte.umsaetze.Saldenabfrage
import net.dankito.fints.model.* import net.dankito.fints.model.*
import net.dankito.fints.util.FinTsUtils import net.dankito.fints.util.FinTsUtils
@ -86,29 +89,53 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
open fun createGetTransactionsMessage(parameter: GetTransactionsParameter, bank: BankData, customer: CustomerData, open fun createGetTransactionsMessage(parameter: GetTransactionsParameter, bank: BankData, customer: CustomerData,
product: ProductData, dialogData: DialogData): String { product: ProductData, dialogData: DialogData): MessageBuilderResult {
return createSignedMessage(bank, customer, dialogData, listOf( val result = getSupportedVersionOfJob(CustomerSegmentId.AccountTransactionsMt940, customer, listOf(5, 6, 7))
KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter, bank, customer),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.AccountTransactionsMt940) if (result.isJobVersionSupported) {
)) val transactionsJob = if (result.isAllowed(7)) KontoumsaetzeZeitraumMt940Version7(generator.resetSegmentNumber(2), parameter, bank, customer)
else if (result.isAllowed(6)) KontoumsaetzeZeitraumMt940Version6(generator.resetSegmentNumber(2), parameter, bank, customer)
else KontoumsaetzeZeitraumMt940Version5(generator.resetSegmentNumber(2), parameter, bank, customer)
return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, listOf(
transactionsJob,
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.AccountTransactionsMt940)
)))
}
return result
} }
open fun createGetBalanceMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String { open fun createGetBalanceMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): MessageBuilderResult {
return createSignedMessage(bank, customer, dialogData, listOf( val result = getSupportedVersionOfJob(CustomerSegmentId.Balance, customer, listOf(5))
Saldenabfrage(generator.resetSegmentNumber(2), bank, customer),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Balance) if (result.isJobVersionSupported) {
)) return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, listOf(
Saldenabfrage(generator.resetSegmentNumber(2), bank, customer),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Balance)
)))
}
return result
} }
open fun createBankTransferMessage(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, dialogData: DialogData): String { open fun createBankTransferMessage(bankTransferData: BankTransferData, bank: BankData, customer: CustomerData, dialogData: DialogData): MessageBuilderResult {
return createSignedMessage(bank, customer, dialogData, listOf( val result = getSupportedVersionOfJob(CustomerSegmentId.SepaBankTransfer, customer, listOf(1))
SepaEinzelueberweisung(generator.resetSegmentNumber(2), customer, bank.bic!!, bankTransferData),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.SepaBankTransfer) if (result.isJobVersionSupported) {
))
return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, listOf(
SepaEinzelueberweisung(generator.resetSegmentNumber(2), customer, bank.bic!!, bankTransferData),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.SepaBankTransfer)
)))
}
return result
} }
@ -183,4 +210,20 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
return payload.joinToString(Separators.SegmentSeparator) { it.format() } return payload.joinToString(Separators.SegmentSeparator) { it.format() }
} }
protected open fun getSupportedVersionOfJob(segmentId: CustomerSegmentId, customer: CustomerData,
supportedVersions: List<Int>): MessageBuilderResult {
customer.accounts.firstOrNull()?.let { account -> // TODO: find a better solution / make more generic
val allowedVersions = account.allowedJobs.filter { it.jobName == segmentId.id }
.map { it.segmentVersion }
.sortedDescending()
return MessageBuilderResult(allowedVersions.isNotEmpty(), allowedVersions.containsAny(supportedVersions),
allowedVersions, supportedVersions, null)
}
return MessageBuilderResult(false)
}
} }

View File

@ -0,0 +1,21 @@
package net.dankito.fints.messages
open class MessageBuilderResult(
val isJobAllowed: Boolean,
val isJobVersionSupported: Boolean,
val allowedVersions: List<Int>,
val supportedVersions: List<Int>,
val createdMessage: String?
) {
constructor(isJobAllowed: Boolean) : this(isJobAllowed, false, listOf(), listOf(), null)
constructor(createdMessage: String) : this(true, true, listOf(), listOf(), createdMessage)
open fun isAllowed(version: Int): Boolean {
return allowedVersions.contains(version)
}
}

View File

@ -0,0 +1,32 @@
package net.dankito.fints.messages.segmente.implementierte.umsaetze
import net.dankito.fints.messages.datenelementgruppen.implementierte.account.Kontoverbindung
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.GetTransactionsParameter
/**
* Die Lösung bietet dem Kunden die Möglichkeit, auf seinem System verlorengegangene Buchungen erneut zu erhalten.
*
* Die maximale Anzahl der rückzumeldenden Buchungspositionen kann begrenzt werden. Eine Buchungsposition besteht
* aus einem :61:/:86:-Block eines MT 940-Formats. Es muss davon unabhängig immer ein gültiges MT 940-Format
* zurückgemeldet werden, d.h. die Felder :20: bis :60: und :62: bis :86: sind obligatorischer Bestandteil der Rückmeldung.
*
* Der maximale Zeitraum, für den rückwirkend Buchungen beim Kreditinstitut gespeichert sind, wird in den
* Bankparameterdaten übermittelt.
*/
open class KontoumsaetzeZeitraumMt940Version6(
segmentNumber: Int,
parameter: GetTransactionsParameter,
bank: BankData,
customer: CustomerData,
subAccountAttribute: String? = null
)
: KontoumsaetzeZeitraumMt940Base(
6,
segmentNumber,
Kontoverbindung(bank.countryCode, bank.bankCode, customer.customerId, subAccountAttribute),
parameter
)

View File

@ -1,5 +1,6 @@
package net.dankito.fints.response package net.dankito.fints.response
import net.dankito.fints.messages.MessageBuilderResult
import net.dankito.fints.messages.Separators import net.dankito.fints.messages.Separators
import net.dankito.fints.messages.segmente.id.ISegmentId import net.dankito.fints.messages.segmente.id.ISegmentId
import net.dankito.fints.messages.segmente.id.MessageSegmentId import net.dankito.fints.messages.segmente.id.MessageSegmentId
@ -14,14 +15,18 @@ open class Response constructor(
/** /**
* When a serious error occurred during web request or response parsing. * When a serious error occurred during web request or response parsing.
*/ */
val exception: Exception? = null val exception: Exception? = null,
val messageCreationError: MessageBuilderResult? = null
) { ) {
open val couldCreateMessage: Boolean
get() = messageCreationError == null
open val responseContainsErrors: Boolean open val responseContainsErrors: Boolean
get() = exception == null && messageFeedback?.isError == true get() = exception == null && messageFeedback?.isError == true
open val successful: Boolean open val successful: Boolean
get() = didReceiveResponse && responseContainsErrors == false get() = couldCreateMessage && didReceiveResponse && responseContainsErrors == false
open val isStrongAuthenticationRequired: Boolean open val isStrongAuthenticationRequired: Boolean
get() = tanResponse?.isStrongAuthenticationRequired == true get() = tanResponse?.isStrongAuthenticationRequired == true

View File

@ -16,11 +16,20 @@ open class FinTsClientResponse(
/** /**
* When a serious error occurred during web request or response parsing. * When a serious error occurred during web request or response parsing.
*/ */
val exception: Exception? = null val exception: Exception? = null,
val isJobAllowed: Boolean = true,
val isJobVersionSupported: Boolean = true,
val allowedVersions: List<Int> = listOf(),
val supportedVersions: List<Int> = listOf()
) { ) {
constructor(response: Response) : this(response.successful, response.isStrongAuthenticationRequired, constructor(response: Response) : this(response.successful, response.isStrongAuthenticationRequired,
response.tanResponse, response.errorsToShowToUser, response.exception) response.tanResponse, response.errorsToShowToUser, response.exception,
response.messageCreationError?.isJobAllowed ?: true,
response.messageCreationError?.isJobVersionSupported ?: true,
response.messageCreationError?.allowedVersions ?: listOf(),
response.messageCreationError?.supportedVersions ?: listOf())
} }