From ee3cd937df59ff8f52536b14c35759b7ad6c366e Mon Sep 17 00:00:00 2001 From: dankl Date: Sat, 26 Oct 2019 18:22:06 +0200 Subject: [PATCH] Implemented continueing at Aufsetzpunkt --- build.gradle | 3 + fints4javaLib/build.gradle | 2 + .../kotlin/net/dankito/fints/FinTsClient.kt | 51 ++++++++- .../dankito/fints/messages/MessageBuilder.kt | 49 ++++++-- .../fints/messages/MessageBuilderResult.kt | 8 +- .../AlphanumerischesDatenelement.kt | 20 ++-- .../basisformate/BinaerDatenelement.kt | 2 +- .../basisformate/TextDatenelement.kt | 5 +- .../implementierte/Aufsetzpunkt.kt | 8 +- .../net/dankito/fints/response/Response.kt | 8 ++ .../dankito/fints/response/ResponseParser.kt | 13 ++- .../response/segments/AufsetzpunktFeedback.kt | 10 ++ .../fints/messages/MessageBuilderTest.kt | 105 ++++++++++++++++++ .../fints/response/ResponseParserTest.kt | 13 +++ 14 files changed, 266 insertions(+), 31 deletions(-) create mode 100644 fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/AufsetzpunktFeedback.kt diff --git a/build.gradle b/build.gradle index 5a7a56ba..34e7a119 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,11 @@ ext { kotlinVersion = '1.3.41' javaUtilsVersion = '1.0.8' + androidUtilsVersion = '1.1.0' + javaFxUtilsVersion = '1.0.3' + junitVersion = '4.12' assertJVersion = '3.12.2' diff --git a/fints4javaLib/build.gradle b/fints4javaLib/build.gradle index 2e4b4e68..dc3df577 100644 --- a/fints4javaLib/build.gradle +++ b/fints4javaLib/build.gradle @@ -31,4 +31,6 @@ dependencies { testCompile "ch.qos.logback:logback-core:$logbackVersion" testCompile "ch.qos.logback:logback-classic:$logbackVersion" + + testCompile "net.dankito.utils:java-fx-utils:$javaFxUtilsVersion" } \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index 6fc2155f..97ffbf71 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -255,15 +255,36 @@ open class FinTsClient @JvmOverloads constructor( tryGetTransactionsOfLast90DaysWithoutTan(bank, customer) } + val bookedAndUnbookedTransactions = getTransactionsFromResponse(response, transactions) + return GetTransactionsResponse(response, - transactions.bookedTransactions.sortedByDescending { it.bookingDate }, - transactions.unbookedTransactions, + bookedAndUnbookedTransactions.first.sortedByDescending { it.bookingDate }, + bookedAndUnbookedTransactions.second, balance) } return GetTransactionsResponse(response) } + protected open fun getTransactionsFromResponse(response: Response, transactions: ReceivedAccountTransactions): Pair, List> { + val bookedTransactions = mutableListOf() + val unbookedTransactions = mutableListOf() + + bookedTransactions.addAll(transactions.bookedTransactions) + unbookedTransactions.addAll(transactions.unbookedTransactions) + + response.followUpResponse?.let { followUpResponse -> + followUpResponse.getFirstSegmentById(InstituteSegmentId.AccountTransactionsMt940)?.let { followUpTransactions -> + val followUpBookedAndUnbookedTransactions = getTransactionsFromResponse(followUpResponse, followUpTransactions) + + bookedTransactions.addAll(followUpBookedAndUnbookedTransactions.first) + unbookedTransactions.addAll(followUpBookedAndUnbookedTransactions.second) + } + } + + return Pair(bookedTransactions, unbookedTransactions) + } + protected open fun getBalanceAfterDialogInit(bank: BankData, customer: CustomerData, dialogData: DialogData): Response { @@ -433,6 +454,32 @@ open class FinTsClient @JvmOverloads constructor( customer: CustomerData, dialogData: DialogData): Response { val response = getAndHandleResponseForMessage(message, bank) + val handledResponse = handleMayRequiredTan(response, bank, customer, dialogData) + + // 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 -> + handledResponse.followUpResponse = getFollowUpMessageForContinuationId(handledResponse, continuationId, message, bank, customer, dialogData) + + handledResponse.hasFollowUpMessageButCouldNotReceiveIt = handledResponse.followUpResponse == null + } + + return handledResponse + } + + protected open fun getFollowUpMessageForContinuationId(response: Response, continuationId: String, message: MessageBuilderResult, + bank: BankData, customer: CustomerData, dialogData: DialogData): Response? { + + messageBuilder.rebuildMessageWithContinuationId(message, continuationId, bank, customer, dialogData)?.let { followUpMessage -> + return getAndHandleResponseForMessageThatMayRequiresTan(followUpMessage, bank, customer, dialogData) + } + + return null + } + + protected open fun getAndHandleResponseForMessageThatMayRequiresTan(message: String, bank: BankData, + customer: CustomerData, dialogData: DialogData): Response { + val response = getAndHandleResponseForMessage(message, bank) + return handleMayRequiredTan(response, bank, customer, dialogData) } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt index 7f4001ec..8160701a 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilder.kt @@ -1,6 +1,7 @@ package net.dankito.fints.messages import net.dankito.fints.extensions.containsAny +import net.dankito.fints.messages.datenelemente.implementierte.Aufsetzpunkt import net.dankito.fints.messages.datenelemente.implementierte.Synchronisierungsmodus import net.dankito.fints.messages.datenelemente.implementierte.tan.TanProcess import net.dankito.fints.messages.segmente.ISegmentNumberGenerator @@ -107,11 +108,12 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg 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( + val segments = listOf( transactionsJob, ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.AccountTransactionsMt940) - ))) + ) + + return createMessageBuilderResult(bank, customer, dialogData, segments) } return result @@ -122,24 +124,26 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg val result = getSupportedVersionsOfJob(CustomerSegmentId.Balance, customer, listOf(5)) if (result.isJobVersionSupported) { - return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, listOf( + val segments = listOf( Saldenabfrage(generator.resetSegmentNumber(2), bank, customer), ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Balance) - ))) + ) + + return createMessageBuilderResult(bank, customer, dialogData, segments) } return result } - open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, bank: BankData, customer: CustomerData, dialogData: DialogData): MessageBuilderResult { + open fun createSendEnteredTanMessage(enteredTan: String, tanResponse: TanResponse, bank: BankData, customer: CustomerData, dialogData: DialogData): String { val tanProcess = if (tanResponse.tanProcess == TanProcess.TanProcess1) TanProcess.TanProcess1 else TanProcess.TanProcess2 - return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, enteredTan, listOf( + return createSignedMessage(bank, customer, dialogData, enteredTan, listOf( ZweiSchrittTanEinreichung(generator.resetSegmentNumber(2), tanProcess, null, tanResponse.jobHashValue, tanResponse.jobReference, false, null, tanResponse.tanMediaIdentifier) - ))) + )) } @@ -150,10 +154,12 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg if (result.isJobVersionSupported) { getSepaUrnFor(CustomerSegmentId.SepaAccountInfoParameters, customer, "pain.001.001.03")?.let { urn -> - return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, listOf( + val segments = listOf( SepaEinzelueberweisung(generator.resetSegmentNumber(2), urn, customer, bank.bic, bankTransferData), ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.SepaBankTransfer) - ))) + ) + + return createMessageBuilderResult(bank, customer, dialogData, segments) } return MessageBuilderResult(true, false, result.allowedVersions, result.supportedVersions, null) // TODO: how to tell that we don't support required SEPA pain version? @@ -163,6 +169,29 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg } + open fun rebuildMessageWithContinuationId(message: MessageBuilderResult, continuationId: String, bank: BankData, + customer: CustomerData, dialogData: DialogData): MessageBuilderResult? { + +// val copiedSegments = message.messageBodySegments.map { } + val aufsetzpunkte = message.messageBodySegments.flatMap { it.dataElementsAndGroups }.filterIsInstance() + + if (aufsetzpunkte.isEmpty()) { +// return MessageBuilderResult(message.isJobAllowed, message.isJobVersionSupported, message.allowedVersions, message.supportedVersions, null) + return null + } + + aufsetzpunkte.forEach { it.resetContinuationId(continuationId) } + + dialogData.increaseMessageNumber() + + return createMessageBuilderResult(bank, customer, dialogData, message.messageBodySegments) + } + + protected open fun createMessageBuilderResult(bank: BankData, customer: CustomerData, dialogData: DialogData, segments: List): MessageBuilderResult { + return MessageBuilderResult(createSignedMessage(bank, customer, dialogData, segments), segments) + } + + open fun createSignedMessage(bank: BankData, customer: CustomerData, dialogData: DialogData, payloadSegments: List): String { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilderResult.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilderResult.kt index ab2be4ab..edd05955 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilderResult.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/MessageBuilderResult.kt @@ -1,17 +1,21 @@ package net.dankito.fints.messages +import net.dankito.fints.messages.segmente.Segment + open class MessageBuilderResult( val isJobAllowed: Boolean, val isJobVersionSupported: Boolean, val allowedVersions: List, val supportedVersions: List, - val createdMessage: String? + val createdMessage: String?, + val messageBodySegments: List = listOf() ) { constructor(isJobAllowed: Boolean) : this(isJobAllowed, false, listOf(), listOf(), null) - constructor(createdMessage: String) : this(true, true, listOf(), listOf(), createdMessage) + constructor(createdMessage: String, messageBodySegments: List) + : this(true, true, listOf(), listOf(), createdMessage, messageBodySegments) open fun isAllowed(version: Int): Boolean { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt index 1fd1588b..fdd49198 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/AlphanumerischesDatenelement.kt @@ -14,16 +14,18 @@ abstract class AlphanumerischesDatenelement @JvmOverloads constructor( override fun validate() { super.validate() - if (writeToOutput && value != null) { // if value is null and value has to be written to output then validation already fails above - if (value.contains("\r") || value.contains("\n")) { - throwValidationException("Alphanumerischer Wert '$value' darf kein Carriage Return (\r) oder " + - "Line Feed (\n) enthalten.") - } + if (writeToOutput) { + value?.let { value -> // if value is null and value has to be written to output then validation already fails above + if (value.contains("\r") || value.contains("\n")) { + throwValidationException("Alphanumerischer Wert '$value' darf kein Carriage Return (\r) oder " + + "Line Feed (\n) enthalten.") + } - maxLength?.let { - if (value.length > maxLength) { - throwValidationException("Wert '$value' darf maximal $maxLength Zeichen lang sein, " + - "hat aber ${value.length} Zeichen.") + maxLength?.let { + if (value.length > maxLength) { + throwValidationException("Wert '$value' darf maximal $maxLength Zeichen lang sein, " + + "hat aber ${value.length} Zeichen.") + } } } } diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt index 93f9dfed..785b81b4 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/BinaerDatenelement.kt @@ -37,7 +37,7 @@ open class BinaerDatenelement @JvmOverloads constructor(data: String?, existenzs if (writeToOutput) { checkIfMandatoryValueIsSet() - value?.let { // if value is null and value has to be written to output then validation already fails above + value?.let { value -> // if value is null and value has to be written to output then validation already fails above maxLength?.let { if (value.length > maxLength) { throwValidationException("Binäre Daten dürfen nur eine maximale Größe von $maxLength Bytes " + diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/TextDatenelement.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/TextDatenelement.kt index eb6f3feb..d0f3673e 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/TextDatenelement.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/basisformate/TextDatenelement.kt @@ -9,10 +9,11 @@ import net.dankito.fints.messages.datenelemente.Datenelement /** * Es gilt der vollständige FinTS-Basiszeichensatz. */ -abstract class TextDatenelement(val value: String?, existenzstatus: Existenzstatus) : Datenelement(existenzstatus) { +abstract class TextDatenelement(var value: String?, existenzstatus: Existenzstatus) : Datenelement(existenzstatus) { - override val isValueSet = value != null + override val isValueSet + get() = value != null override fun format(): String { if (writeToOutput) { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/Aufsetzpunkt.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/Aufsetzpunkt.kt index e2bef287..9f7a2ad8 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/Aufsetzpunkt.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/messages/datenelemente/implementierte/Aufsetzpunkt.kt @@ -10,4 +10,10 @@ import net.dankito.fints.messages.datenelemente.basisformate.AlphanumerischesDat * einzigen Auftragssegment erfolgen kann (s. [Formals]). */ open class Aufsetzpunkt(continuationId: String?, existenzstatus: Existenzstatus) - : AlphanumerischesDatenelement(continuationId, existenzstatus, 35) \ No newline at end of file + : AlphanumerischesDatenelement(continuationId, existenzstatus, 35) { + + open fun resetContinuationId(continuationId: String?) { + value = continuationId + } + +} \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt index 70baf3fd..55a38570 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/Response.kt @@ -48,6 +48,9 @@ open class Response constructor( open val segmentFeedbacks: List get() = getSegmentsById(InstituteSegmentId.SegmentFeedback) + + open val aufsetzpunkt: String? // TODO: what to do if there are multiple Aufsetzpunkte? + get() = segmentFeedbacks.flatMap { it.feedbacks }.filterIsInstance().firstOrNull()?.aufsetzpunkt open val errorsToShowToUser: List get() { @@ -66,6 +69,11 @@ open class Response constructor( } + open var followUpResponse: Response? = null + + open var hasFollowUpMessageButCouldNotReceiveIt: Boolean? = false + + /** * Returns an empty list of response didn't contain any job parameters. * diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt index 5bc234d3..e0fd0c09 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -29,13 +29,15 @@ open class ResponseParser @JvmOverloads constructor( ) { companion object { - val EncryptionDataSegmentHeaderPattern = Pattern.compile("${MessageSegmentId.EncryptionData.id}:\\d{1,3}:\\d{1,3}\\+") + val EncryptionDataSegmentHeaderPattern: Pattern = Pattern.compile("${MessageSegmentId.EncryptionData.id}:\\d{1,3}:\\d{1,3}\\+") - val JobParametersSegmentPattern = Pattern.compile("HI[A-Z]{3}S") + val JobParametersSegmentPattern: Pattern = Pattern.compile("HI[A-Z]{3}S") - val FeedbackParametersSeparator = "; " + const val FeedbackParametersSeparator = "; " - val SupportedTanProceduresForUserResponseCode = 3920 + const val AufsetzpunktResponseCode = 3040 + + const val SupportedTanProceduresForUserResponseCode = 3920 private val log = LoggerFactory.getLogger(ResponseParser::class.java) } @@ -138,6 +140,9 @@ open class ResponseParser @JvmOverloads constructor( val supportedProcedures = parseCodeEnum(dataElements.subList(3, dataElements.size), Sicherheitsfunktion.values()) return SupportedTanProceduresForUserFeedback(supportedProcedures, message) } + else if (responseCode == AufsetzpunktResponseCode) { + return AufsetzpunktFeedback(parseString(dataElements[3]), message) + } val parameter = if (dataElements.size > 3) dataElements.subList(3, dataElements.size).joinToString(FeedbackParametersSeparator) else null diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/AufsetzpunktFeedback.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/AufsetzpunktFeedback.kt new file mode 100644 index 00000000..b33d6f34 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/AufsetzpunktFeedback.kt @@ -0,0 +1,10 @@ +package net.dankito.fints.response.segments + +import net.dankito.fints.response.ResponseParser + + +open class AufsetzpunktFeedback( + val aufsetzpunkt: String, + message: String +) + : Feedback(ResponseParser.AufsetzpunktResponseCode, message) \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt index b8f253e6..0fb063d3 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/messages/MessageBuilderTest.kt @@ -1,10 +1,18 @@ package net.dankito.fints.messages import net.dankito.fints.FinTsTestBase +import net.dankito.fints.model.AccountData import net.dankito.fints.model.DialogData +import net.dankito.fints.model.GetTransactionsParameter +import net.dankito.fints.response.segments.AccountType +import net.dankito.fints.response.segments.JobParameters import net.dankito.fints.util.FinTsUtils +import net.dankito.utils.datetime.asUtilDate import org.assertj.core.api.Assertions.assertThat +import org.junit.After import org.junit.Test +import java.time.LocalDate +import java.time.Month import java.util.* @@ -27,6 +35,13 @@ class MessageBuilderTest : FinTsTestBase() { } + @After + fun tearDown() { + Bank.supportedJobs = listOf() + Customer.accounts = listOf() + } + + @Test fun createAnonymousDialogInitMessage() { @@ -102,4 +117,94 @@ class MessageBuilderTest : FinTsTestBase() { )) } + + @Test + fun createGetTransactionsMessage_JobIsNotAllowed() { + + // when + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Bank, Customer, Product, DialogData.DialogInitDialogData) + + // then + assertThat(result.isJobAllowed).isFalse() + } + + @Test + fun createGetTransactionsMessage_JobVersionIsNotSupported() { + + // given + val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5") + val getTransactionsJobWithPreviousVersion = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:72:4") + Bank.supportedJobs = listOf(getTransactionsJob) + val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJobWithPreviousVersion)) + Customer.accounts = listOf(account) + + // when + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(), Bank, Customer, Product, DialogData.DialogInitDialogData) + + // then + assertThat(result.isJobAllowed).isTrue() + assertThat(result.isJobVersionSupported).isFalse() + } + + @Test + fun createGetTransactionsMessage() { + + // given + val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5") + Bank.supportedJobs = listOf(getTransactionsJob) + val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) + Customer.accounts = listOf(account) + + val fromDate = LocalDate.of(2019, Month.AUGUST, 6).asUtilDate() + val toDate = LocalDate.of(2019, Month.OCTOBER, 21).asUtilDate() + val maxCountEntries = 99 + + // when + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries), Bank, Customer, Product, DialogData.DialogInitDialogData) + + // then + assertThat(result.createdMessage).isNotNull() + + assertThat(normalizeBinaryData(result.createdMessage!!)).isEqualTo(normalizeBinaryData( + "HNHBK:1:3+000000000362+300+0+1'" + + "HNVSK:998:3+PIN:2+998+1+1::0+1:$Date:$Time+2:16:14:@8@ :5:1+280:$BankCode:$CustomerId:V:0:0+0'" + + "HNVSD:999:1+@198@" + "HNSHK:2:4+PIN:2+${SecurityFunction.code}+$ControlReference+1+1+1::0+1+1:$Date:$Time+1:999:1+6:10:16+280:$BankCode:$CustomerId:S:0:0'" + + "HKKAZ:3:${getTransactionsJob.segmentVersion}+$CustomerId::280:$BankCode+N+${convertDate(fromDate)}+${convertDate(toDate)}+$maxCountEntries'" + + "HKTAN:4:6+4+HKKAZ'" + + "HNSHA:5:2+$ControlReference++$Pin''" + + "HNHBS:6:1+1'" + )) + } + + @Test + fun createGetTransactionsMessage_WithContinuationIdSet() { + + // given + val getTransactionsJob = JobParameters("HKKAZ", 1, 1, null, "HKKAZ:73:5") + Bank.supportedJobs = listOf(getTransactionsJob) + val account = AccountData(CustomerId, null, BankCountryCode, BankCode, null, CustomerId, AccountType.Girokonto, "EUR", "", null, null, listOf(getTransactionsJob.jobName), listOf(getTransactionsJob)) + Customer.accounts = listOf(account) + + val fromDate = LocalDate.of(2019, Month.AUGUST, 6).asUtilDate() + val toDate = LocalDate.of(2019, Month.OCTOBER, 21).asUtilDate() + val maxCountEntries = 99 + val continuationId = "9345-10-26-11.52.15.693455" + + // when + val result = underTest.createGetTransactionsMessage(GetTransactionsParameter(false, fromDate, toDate, maxCountEntries, false, continuationId), Bank, Customer, Product, DialogData.DialogInitDialogData) + + // then + assertThat(result.createdMessage).isNotNull() + + assertThat(normalizeBinaryData(result.createdMessage!!)).isEqualTo(normalizeBinaryData( + "HNHBK:1:3+000000000389+300+0+1'" + + "HNVSK:998:3+PIN:2+998+1+1::0+1:$Date:$Time+2:16:14:@8@ :5:1+280:$BankCode:$CustomerId:V:0:0+0'" + + "HNVSD:999:1+@225@" + "HNSHK:2:4+PIN:2+${SecurityFunction.code}+$ControlReference+1+1+1::0+1+1:$Date:$Time+1:999:1+6:10:16+280:$BankCode:$CustomerId:S:0:0'" + + "HKKAZ:3:${getTransactionsJob.segmentVersion}+$CustomerId::280:$BankCode+N+${convertDate(fromDate)}+${convertDate(toDate)}+$maxCountEntries+$continuationId'" + + "HKTAN:4:6+4+HKKAZ'" + + "HNSHA:5:2+$ControlReference++$Pin''" + + "HNHBS:6:1+1'" + )) + } + } \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt index 88b380a3..70932802 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt @@ -208,6 +208,19 @@ class ResponseParserTest : FinTsTestBase() { } + @Test + fun parseSegmentFeedback_Aufsetzpunkt() { + + // when + val result = underTest.parse("HIRMS:4:2:3+0020::Der Auftrag wurde ausgeführt.+0020::Die gebuchten Umsätze wurden übermittelt.+3040::Es liegen weitere Informationen vor.:9345-10-26-11.52.15.693455") + + + // then + assertCouldParseSegment(result, InstituteSegmentId.SegmentFeedback, 4, 2, 3) + + assertThat(result.aufsetzpunkt).isEqualTo("9345-10-26-11.52.15.693455") + } + @Test fun parseSegmentFeedback_AllowedUserTanProcedures() {