From 9672136a77148db94b23301836ce944341d9e5a2 Mon Sep 17 00:00:00 2001 From: dankl Date: Thu, 17 Oct 2019 15:13:02 +0200 Subject: [PATCH] Implemented parsing SepaAccountInfo --- .../kotlin/net/dankito/fints/FinTsClient.kt | 15 ++++++++++++ .../fints/response/InstituteSegmentId.kt | 2 ++ .../dankito/fints/response/ResponseParser.kt | 23 ++++++++++++++++++- .../KontoverbindungZvInternational.kt | 13 +++++++++++ .../response/segments/SepaAccountInfo.kt | 8 +++++++ .../kotlin/net/dankito/fints/FinTsTestBase.kt | 8 +++++-- .../fints/response/ResponseParserTest.kt | 21 +++++++++++++++++ 7 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/KontoverbindungZvInternational.kt create mode 100644 fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/SepaAccountInfo.kt diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt index ad107997..98590a35 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/FinTsClient.kt @@ -485,11 +485,19 @@ open class FinTsClient @JvmOverloads constructor( } response.getFirstSegmentById(InstituteSegmentId.CommunicationInfo)?.let { communicationInfo -> + // TODO: set default language, also for user + communicationInfo.parameters.firstOrNull { it.type == Kommunikationsdienst.Https }?.address?.let { address -> bank.finTs3ServerAddress = if (address.startsWith("https://", true)) address else "https://$address" } } + response.getFirstSegmentById(InstituteSegmentId.SepaAccountInfo)?.let { sepaAccountInfo -> + sepaAccountInfo.account.bic?.let { + bank.bic = it // TODO: really set BIC on bank then? + } + } + if (response.supportedJobs.isNotEmpty()) { bank.supportedJobs = response.supportedJobs } @@ -537,6 +545,13 @@ open class FinTsClient @JvmOverloads constructor( // TODO: may also make use of other info } + response.getFirstSegmentById(InstituteSegmentId.SepaAccountInfo)?.let { sepaAccountInfo -> + // TODO: may also make use of other info + sepaAccountInfo.account.iban?.let { + customer.iban = it + } + } + response.getFirstSegmentById(InstituteSegmentId.UserParameters)?.let { userParameters -> customer.updVersion = userParameters.updVersion diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt index d25d049a..7ed585a1 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/InstituteSegmentId.kt @@ -21,6 +21,8 @@ enum class InstituteSegmentId(override val id: String) : ISegmentId { AccountInfo("HIUPD"), + SepaAccountInfo("HISPA"), + TanInfo("HITANS"), Tan("HITAN"), 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 ada92a90..8fedd2c6 100644 --- a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/ResponseParser.kt @@ -87,6 +87,7 @@ open class ResponseParser @JvmOverloads constructor( InstituteSegmentId.UserParameters.id -> parseUserParameters(segment, dataElementGroups) InstituteSegmentId.AccountInfo.id -> parseAccountInfo(segment, dataElementGroups) + InstituteSegmentId.SepaAccountInfo.id -> parseSepaAccountInfo(segment, dataElementGroups) InstituteSegmentId.TanInfo.id -> parseTanInfo(segment, segmentId, dataElementGroups) InstituteSegmentId.Tan.id -> parseTanResponse(segment, dataElementGroups) @@ -249,6 +250,22 @@ open class ResponseParser @JvmOverloads constructor( return null } + protected open fun parseSepaAccountInfo(segment: String, dataElementGroups: List): SepaAccountInfo { + val accountDataElements = getDataElements(dataElementGroups[1]) + + return SepaAccountInfo( + KontoverbindungZvInternational( + parseBoolean(accountDataElements[0]), + parseStringToNullIfEmpty(accountDataElements[1]), + parseStringToNullIfEmpty(accountDataElements[2]), + parseString(accountDataElements[3]), + parseStringToNullIfEmpty(accountDataElements[4]), + parseBankDetails(accountDataElements[5], accountDataElements[6]) + ), + segment + ) + } + protected open fun parseAllowedJob(segment: String, segmentId: String, dataElementGroups: List): SupportedJob { var jobName = segmentId.substring(0, 5) // cut off last 'S' (which stands for 'parameter') @@ -404,7 +421,11 @@ open class ResponseParser @JvmOverloads constructor( protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung { val detailsStrings = getDataElements(dataElementsGroup) - return Kreditinstitutskennung(parseInt(detailsStrings[0]), parseString(detailsStrings[1])) + return parseBankDetails(detailsStrings[0], detailsStrings[1]) + } + + protected open fun parseBankDetails(countryCodeDE: String, bankCodeDE: String): Kreditinstitutskennung { + return Kreditinstitutskennung(parseInt(countryCodeDE), parseString(bankCodeDE)) } protected open fun parseLanguages(dataElementsGroup: String): List { diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/KontoverbindungZvInternational.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/KontoverbindungZvInternational.kt new file mode 100644 index 00000000..69b13918 --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/KontoverbindungZvInternational.kt @@ -0,0 +1,13 @@ +package net.dankito.fints.response.segments + +import net.dankito.fints.messages.datenelementgruppen.implementierte.Kreditinstitutskennung + + +open class KontoverbindungZvInternational( + val isSepaAccount: Boolean, + val iban: String?, + val bic: String?, + val accountIdentifier: String, + val subAccountAttribute: String? = null, + val bankInfo: Kreditinstitutskennung +) \ No newline at end of file diff --git a/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/SepaAccountInfo.kt b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/SepaAccountInfo.kt new file mode 100644 index 00000000..9501875b --- /dev/null +++ b/fints4javaLib/src/main/kotlin/net/dankito/fints/response/segments/SepaAccountInfo.kt @@ -0,0 +1,8 @@ +package net.dankito.fints.response.segments + + +open class SepaAccountInfo( + val account: KontoverbindungZvInternational, + segmentString: String +) + : ReceivedSegment(segmentString) \ No newline at end of file diff --git a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt index a9e0a906..321ca6d0 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/FinTsTestBase.kt @@ -14,9 +14,9 @@ abstract class FinTsTestBase { companion object { const val BankCode = "12345678" - val BankCountryCode = Laenderkennzeichen.Germany + const val BankCountryCode = Laenderkennzeichen.Germany - val BankFinTsServerAddress = "banking.supi-dupi-bank.de/fints30" + const val BankFinTsServerAddress = "banking.supi-dupi-bank.de/fints30" val Bank = BankData(BankCode, BankCountryCode, "") @@ -24,6 +24,10 @@ abstract class FinTsTestBase { const val Pin = "12345" + const val Iban = "DE11$BankCode$CustomerId" + + const val Bic = "ABCDDEMM123" + val Language = Dialogsprache.German val SecurityFunction = Sicherheitsfunktion.PIN_TAN_911 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 e419c0bd..db7d749f 100644 --- a/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt +++ b/fints4javaLib/src/test/kotlin/net/dankito/fints/response/ResponseParserTest.kt @@ -426,6 +426,27 @@ class ResponseParserTest : FinTsTestBase() { ?: run { Assert.fail("No segment of type AccountInfo found in ${result.receivedSegments}") } } + @Test + fun parseSepaAccountInfo() { + + // when + val result = underTest.parse("HISPA:5:1:3+J:$Iban:$Bic:$CustomerId::280:$BankCode") + + // then + assertSuccessfullyParsedSegment(result, InstituteSegmentId.SepaAccountInfo, 5, 1, 3) + + result.getFirstSegmentById(InstituteSegmentId.SepaAccountInfo)?.let { segment -> + assertThat(segment.account.isSepaAccount).isTrue() + assertThat(segment.account.iban).isEqualTo(Iban) + assertThat(segment.account.bic).isEqualTo(Bic) + assertThat(segment.account.accountIdentifier).isEqualTo(CustomerId) + assertThat(segment.account.subAccountAttribute).isNull() + assertThat(segment.account.bankInfo.bankCountryCode).isEqualTo(BankCountryCode) + assertThat(segment.account.bankInfo.bankCode).isEqualTo(BankCode) + } + ?: run { Assert.fail("No segment of type SepaAccountInfo found in ${result.receivedSegments}") } + } + @Test fun parseSupportedJobs() {