Implemented parsing User parameters and most of account info

This commit is contained in:
dankl 2019-10-06 00:50:12 +02:00 committed by dankito
parent 89fc324cb4
commit 5fc2c2ef5b
11 changed files with 476 additions and 14 deletions

View File

@ -1,6 +1,8 @@
package net.dankito.fints
import net.dankito.fints.messages.MessageBuilder
import net.dankito.fints.messages.datenelemente.implementierte.Dialogsprache
import net.dankito.fints.messages.datenelemente.implementierte.KundensystemStatusWerte
import net.dankito.fints.model.BankData
import net.dankito.fints.model.CustomerData
import net.dankito.fints.model.DialogData
@ -8,7 +10,10 @@ import net.dankito.fints.model.ProductData
import net.dankito.fints.response.InstituteSegmentId
import net.dankito.fints.response.Response
import net.dankito.fints.response.ResponseParser
import net.dankito.fints.response.segments.AccountInfo
import net.dankito.fints.response.segments.BankParameters
import net.dankito.fints.response.segments.ReceivedSynchronization
import net.dankito.fints.response.segments.UserParameters
import net.dankito.fints.util.IBase64Service
import net.dankito.utils.web.client.IWebClient
import net.dankito.utils.web.client.OkHttpWebClient
@ -37,6 +42,8 @@ open class FinTsClient(
val response = getAndHandleResponseForMessage(requestBody, bank)
if (response.successful) {
updateBankData(bank, response)
dialogData.increaseMessageNumber()
response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId }
@ -49,6 +56,23 @@ open class FinTsClient(
}
open fun initDialog(bank: BankData, customer: CustomerData, product: ProductData,
dialogData: DialogData): Response {
val requestBody = messageBuilder.createInitDialogMessage(bank, customer, product, dialogData)
val response = getAndHandleResponseForMessage(requestBody, bank)
if (response.successful) {
updateBankData(bank, response)
updateCustomerData(customer, response)
response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId }
}
return response
}
open fun synchronizeCustomerSystemId(bank: BankData, customer: CustomerData, product: ProductData,
dialogData: DialogData = DialogData()): Response {
@ -57,11 +81,10 @@ open class FinTsClient(
val response = getAndHandleResponseForMessage(requestBody, bank)
if (response.successful) {
response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId }
updateBankData(bank, response)
updateCustomerData(customer, response)
response.getFirstSegmentById<ReceivedSynchronization>(InstituteSegmentId.Synchronization)?.customerSystemId?.let { customerSystemId ->
customer.customerSystemId = customerSystemId
}
response.messageHeader?.let { header -> dialogData.dialogId = header.dialogId }
closeDialog(bank, customer, dialogData)
}
@ -113,4 +136,46 @@ open class FinTsClient(
return base64Service.decode(responseBody.replace("\r", "").replace("\n", ""))
}
protected open fun updateBankData(bank: BankData, response: Response) {
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
bank.bpdVersion = bankParameters.bpdVersion
bank.name = bankParameters.bankName
// bank.bic = bankParameters. // TODO: where's the BIC?
// bank.finTs3ServerAddress = // TODO: parse HIKOM
// TODO: save supported languages and security profiles
}
}
protected open fun updateCustomerData(customer: CustomerData, response: Response) {
response.getFirstSegmentById<BankParameters>(InstituteSegmentId.BankParameters)?.let { bankParameters ->
if (customer.selectedLanguage == Dialogsprache.Default && bankParameters.supportedLanguages.isNotEmpty()) {
customer.selectedLanguage = bankParameters.supportedLanguages.first()
}
}
response.getFirstSegmentById<ReceivedSynchronization>(InstituteSegmentId.Synchronization)?.let { synchronization ->
synchronization.customerSystemId?.let {
customer.customerSystemId = it
customer.customerSystemStatus = KundensystemStatusWerte.Benoetigt // TODO: didn't find out for sure yet, but i think i read somewhere, that this has to be set when customerSystemId is set
}
// TODO: may also save securityReferenceNumbers
}
response.getFirstSegmentById<AccountInfo>(InstituteSegmentId.AccountInfo)?.let { accountInfo ->
customer.iban = accountInfo.iban
customer.name = accountInfo.accountHolderName1
// TODO: may also make use of other info
}
response.getFirstSegmentById<UserParameters>(InstituteSegmentId.UserParameters)?.let { userParameters ->
customer.updVersion = userParameters.updVersion
// TODO: may also make use of other info
}
}
}

View File

@ -58,6 +58,15 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
}
open fun createInitDialogMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String {
return createMessage(true, true, bank, customer, dialogData, listOf(
IdentifikationsSegment(generator.resetSegmentNumber(2), bank, customer),
Verarbeitungsvorbereitung(generator.getNextSegmentNumber(), bank, customer, product),
ZweiSchrittTanEinreichung(generator.getNextSegmentNumber(), TanProcess.TanProcess4, CustomerSegmentId.Identification.id)
))
}
open fun createSynchronizeCustomerSystemIdMessage(bank: BankData, customer: CustomerData, product: ProductData, dialogData: DialogData): String {
return createMessage(true, true, bank, customer, dialogData, listOf(

View File

@ -9,6 +9,7 @@ open class BankData(
val countryCode: Int,
var finTs3ServerAddress: String,
var bpdVersion: Int = BPDVersion.VersionNotReceivedYet,
var name: String = "",
var bic: String? = null,
var supportedLanguages: List<Dialogsprache> = listOf()
) {

View File

@ -9,6 +9,7 @@ open class CustomerData(
val customerId: String,
var pin: String,
val userId: String = customerId,
var name: String = "",
var iban: String? = null,
var updVersion: Int = UPDVersion.VersionNotReceivedYet,
var availableTanProcedures: List<TanProcedure> = listOf(),

View File

@ -9,6 +9,10 @@ enum class InstituteSegmentId(override val id: String) : ISegmentId {
BankParameters("HIBPA"),
SecurityMethods("HISHV")
SecurityMethods("HISHV"),
UserParameters("HIUPA"),
AccountInfo("HIUPD")
}

View File

@ -58,9 +58,14 @@ open class ResponseParser {
protected open fun parseSegment(segment: String, segmentId: String, dataElementGroups: List<String>): ReceivedSegment? {
return when (segmentId) {
MessageSegmentId.MessageHeader.id -> parseMessageHeaderSegment(segment, dataElementGroups)
InstituteSegmentId.Synchronization.id -> parseSynchronization(segment, dataElementGroups)
InstituteSegmentId.BankParameters.id -> parseBankParameters(segment, dataElementGroups)
InstituteSegmentId.SecurityMethods.id -> parseSecurityMethods(segment, dataElementGroups)
InstituteSegmentId.UserParameters.id -> parseUserParameters(segment, dataElementGroups)
InstituteSegmentId.AccountInfo.id -> parseAccountInfo(segment, dataElementGroups)
else -> null
}
}
@ -110,6 +115,41 @@ open class ResponseParser {
}
protected open fun parseUserParameters(segment: String, dataElementGroups: List<String>): UserParameters {
val customerId = dataElementGroups[1]
val updVersion = dataElementGroups[2].toInt()
val areListedJobsBlocked = dataElementGroups[3] == "0"
val username = if (dataElementGroups.size > 4) returnNullIfEmpty(dataElementGroups[4]) else null
val extension = if (dataElementGroups.size > 5) returnNullIfEmpty(dataElementGroups[5]) else null
return UserParameters(customerId, updVersion, areListedJobsBlocked, username, extension, segment)
}
protected open fun parseAccountInfo(segment: String, dataElementGroups: List<String>): AccountInfo {
// this is parsing a Kontoverbindung. May extract a method for it.
val accountDetails = getDataElements(dataElementGroups[1])
val accountNumber = accountDetails[0]
val subAccountAttribute = returnNullIfEmpty(accountDetails[1])
val bankCountryCode = accountDetails[2].toInt()
val bankCode = accountDetails[3]
val iban = returnNullIfEmpty(dataElementGroups[2])
val customerId = dataElementGroups[3]
val accountType = parseCodeEnum(listOf(dataElementGroups[4]), AccountTypeCode.values()).first().type
val currency = dataElementGroups[5]
val accountHolderName1 = dataElementGroups[6]
val accountHolderName2 = returnNullIfEmpty(dataElementGroups[7])
val productName = returnNullIfEmpty(dataElementGroups[8])
val limit = returnNullIfEmpty(dataElementGroups[9]) // TODO: parse limit
// TODO: parse allowed jobs
// TODO: parse extension
return AccountInfo(accountNumber, subAccountAttribute, bankCountryCode, bankCode, iban, customerId, accountType,
currency, accountHolderName1, accountHolderName2, productName, limit, null, segment)
}
protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung {
val detailsStrings = getDataElements(dataElementsGroup)
@ -119,13 +159,13 @@ open class ResponseParser {
protected open fun parseLanguages(dataElementsGroup: String): List<Dialogsprache> {
val languageStrings = getDataElements(dataElementsGroup)
return parseFromCode(languageStrings, Dialogsprache.values())
return parseCodeEnum(languageStrings, Dialogsprache.values())
}
protected open fun parseHbciVersions(dataElementsGroup: String): List<HbciVersion> {
val versionStrings = getDataElements(dataElementsGroup)
return parseFromCode(versionStrings, HbciVersion.values())
return parseCodeEnum(versionStrings, HbciVersion.values())
}
protected open fun parseSecurityProfiles(dataElementsGroups: List<String>): List<Sicherheitsprofil> {
@ -141,7 +181,7 @@ open class ResponseParser {
}
protected open fun parseSecurityMethod(methodString: String): Sicherheitsverfahren {
return parseFromCode(listOf(methodString), Sicherheitsverfahren.values()).first()
return parseCodeEnum(listOf(methodString), Sicherheitsverfahren.values()).first()
}
protected open fun parseSecurityMethodVersion(versionString: String): VersionDesSicherheitsverfahrens {
@ -150,7 +190,7 @@ open class ResponseParser {
return VersionDesSicherheitsverfahrens.values().first { it.methodNumber == versionInt }
}
protected open fun <T : ICodeEnum> parseFromCode(codeValues: List<String>, allValues: Array<T>): List<T> {
protected open fun <T : ICodeEnum> parseCodeEnum(codeValues: List<String>, allValues: Array<T>): List<T> {
// mapNotNull: don't crash if new, at time of implementation unknown values get introduced / returned by bank
return codeValues.mapNotNull { code -> allValues.first { it.code == code } }
}
@ -168,4 +208,8 @@ open class ResponseParser {
return false
}
protected open fun returnNullIfEmpty(string: String): String? {
return if (string.isEmpty()) null else string
}
}

View File

@ -0,0 +1,22 @@
package net.dankito.fints.response.segments
open class AccountInfo(
val accountNumber: String,
val subAccountAttribute: String?,
val bankCountryCode: Int,
val bankCode: String,
val iban: String?,
val customerId: String,
val accountType: AccountType,
val currency: String, // TODO: may parse to a value object
val accountHolderName1: String,
val accountHolderName2: String?,
val productName: String?,
val accountLimit: String?, // TODO: parse
val extension: String?, // TODO: parse
segmentString: String
)
: ReceivedSegment(segmentString)

View File

@ -0,0 +1,26 @@
package net.dankito.fints.response.segments
enum class AccountType {
Girokonto,
Sparkonto,
Festgeldkonto,
Wertpapierdepot,
Darlehenskonto,
Kreditkartenkonto,
FondsDepot,
Bausparvertrag,
Versicherungsvertrag,
Sonstige
}

View File

@ -0,0 +1,222 @@
package net.dankito.fints.response.segments
import net.dankito.fints.messages.datenelemente.implementierte.ICodeEnum
/**
* Klassifizierung der Konten. Innerhalb der vorgegebenen Codebereiche sind kreditinstitutsindividuell bei Bedarf
* weitere Kontoarten möglich.
*
* Codierung:
* - 1 9: Kontokorrent-/Girokonto
* - 10 19: Sparkonto
* - 20 29: Festgeldkonto (Termineinlagen)
* - 30 39: Wertpapierdepot
* - 40 49: Kredit-/Darlehenskonto
* - 50 59: Kreditkartenkonto
* - 60 69: Fonds-Depot bei einer Kapitalanlagegesellschaft
* - 70 79: Bausparvertrag
* - 80 89: Versicherungsvertrag
* - 90 99: Sonstige (nicht zuordenbar)
*/
enum class AccountTypeCode(override val code: String, val type: AccountType) : ICodeEnum {
Girokonto_1("1", AccountType.Girokonto),
Girokonto_2("2", AccountType.Girokonto),
Girokonto_3("3", AccountType.Girokonto),
Girokonto_4("4", AccountType.Girokonto),
Girokonto_5("5", AccountType.Girokonto),
Girokonto_6("6", AccountType.Girokonto),
Girokonto_7("7", AccountType.Girokonto),
Girokonto_8("8", AccountType.Girokonto),
Girokonto_9("9", AccountType.Girokonto),
Sparkonto_1("10", AccountType.Sparkonto),
Sparkonto_2("11", AccountType.Sparkonto),
Sparkonto_3("12", AccountType.Sparkonto),
Sparkonto_4("13", AccountType.Sparkonto),
Sparkonto_5("14", AccountType.Sparkonto),
Sparkonto_6("15", AccountType.Sparkonto),
Sparkonto_7("16", AccountType.Sparkonto),
Sparkonto_8("17", AccountType.Sparkonto),
Sparkonto_9("18", AccountType.Sparkonto),
Sparkonto_10("19", AccountType.Sparkonto),
Festgeldkonto_1("20", AccountType.Festgeldkonto),
Festgeldkonto_2("21", AccountType.Festgeldkonto),
Festgeldkonto_3("22", AccountType.Festgeldkonto),
Festgeldkonto_4("23", AccountType.Festgeldkonto),
Festgeldkonto_5("24", AccountType.Festgeldkonto),
Festgeldkonto_6("25", AccountType.Festgeldkonto),
Festgeldkonto_7("26", AccountType.Festgeldkonto),
Festgeldkonto_8("27", AccountType.Festgeldkonto),
Festgeldkonto_9("28", AccountType.Festgeldkonto),
Festgeldkonto_10("29", AccountType.Festgeldkonto),
Wertpapierdepot_1("30", AccountType.Wertpapierdepot),
Wertpapierdepot_2("31", AccountType.Wertpapierdepot),
Wertpapierdepot_3("32", AccountType.Wertpapierdepot),
Wertpapierdepot_4("33", AccountType.Wertpapierdepot),
Wertpapierdepot_5("34", AccountType.Wertpapierdepot),
Wertpapierdepot_6("35", AccountType.Wertpapierdepot),
Wertpapierdepot_7("36", AccountType.Wertpapierdepot),
Wertpapierdepot_8("37", AccountType.Wertpapierdepot),
Wertpapierdepot_9("38", AccountType.Wertpapierdepot),
Wertpapierdepot_10("39", AccountType.Wertpapierdepot),
Darlehenskonto_1("40", AccountType.Darlehenskonto),
Darlehenskonto_2("41", AccountType.Darlehenskonto),
Darlehenskonto_3("42", AccountType.Darlehenskonto),
Darlehenskonto_4("43", AccountType.Darlehenskonto),
Darlehenskonto_5("44", AccountType.Darlehenskonto),
Darlehenskonto_6("45", AccountType.Darlehenskonto),
Darlehenskonto_7("46", AccountType.Darlehenskonto),
Darlehenskonto_8("47", AccountType.Darlehenskonto),
Darlehenskonto_9("48", AccountType.Darlehenskonto),
Darlehenskonto_10("49", AccountType.Darlehenskonto),
Kreditkartenkonto_1("50", AccountType.Kreditkartenkonto),
Kreditkartenkonto_2("51", AccountType.Kreditkartenkonto),
Kreditkartenkonto_3("52", AccountType.Kreditkartenkonto),
Kreditkartenkonto_4("53", AccountType.Kreditkartenkonto),
Kreditkartenkonto_5("54", AccountType.Kreditkartenkonto),
Kreditkartenkonto_6("55", AccountType.Kreditkartenkonto),
Kreditkartenkonto_7("56", AccountType.Kreditkartenkonto),
Kreditkartenkonto_8("57", AccountType.Kreditkartenkonto),
Kreditkartenkonto_9("58", AccountType.Kreditkartenkonto),
Kreditkartenkonto_10("59", AccountType.Kreditkartenkonto),
FondsDepot_1("60", AccountType.FondsDepot),
FondsDepot_2("61", AccountType.FondsDepot),
FondsDepot_3("62", AccountType.FondsDepot),
FondsDepot_4("63", AccountType.FondsDepot),
FondsDepot_5("64", AccountType.FondsDepot),
FondsDepot_6("65", AccountType.FondsDepot),
FondsDepot_7("66", AccountType.FondsDepot),
FondsDepot_8("67", AccountType.FondsDepot),
FondsDepot_9("68", AccountType.FondsDepot),
FondsDepot_10("69", AccountType.FondsDepot),
Bausparvertrag_1("70", AccountType.Bausparvertrag),
Bausparvertrag_2("71", AccountType.Bausparvertrag),
Bausparvertrag_3("72", AccountType.Bausparvertrag),
Bausparvertrag_4("73", AccountType.Bausparvertrag),
Bausparvertrag_5("74", AccountType.Bausparvertrag),
Bausparvertrag_6("75", AccountType.Bausparvertrag),
Bausparvertrag_7("76", AccountType.Bausparvertrag),
Bausparvertrag_8("77", AccountType.Bausparvertrag),
Bausparvertrag_9("78", AccountType.Bausparvertrag),
Bausparvertrag_10("79", AccountType.Bausparvertrag),
Versicherungsvertrag_1("80", AccountType.Versicherungsvertrag),
Versicherungsvertrag_2("81", AccountType.Versicherungsvertrag),
Versicherungsvertrag_3("82", AccountType.Versicherungsvertrag),
Versicherungsvertrag_4("83", AccountType.Versicherungsvertrag),
Versicherungsvertrag_5("84", AccountType.Versicherungsvertrag),
Versicherungsvertrag_6("85", AccountType.Versicherungsvertrag),
Versicherungsvertrag_7("86", AccountType.Versicherungsvertrag),
Versicherungsvertrag_8("87", AccountType.Versicherungsvertrag),
Versicherungsvertrag_9("88", AccountType.Versicherungsvertrag),
Versicherungsvertrag_10("89", AccountType.Versicherungsvertrag),
Sonstige_1("90", AccountType.Sonstige),
Sonstige_2("91", AccountType.Sonstige),
Sonstige_3("92", AccountType.Sonstige),
Sonstige_4("93", AccountType.Sonstige),
Sonstige_5("94", AccountType.Sonstige),
Sonstige_6("95", AccountType.Sonstige),
Sonstige_7("96", AccountType.Sonstige),
Sonstige_8("97", AccountType.Sonstige),
Sonstige_9("98", AccountType.Sonstige),
Sonstige_10("99", AccountType.Sonstige)
}

View File

@ -0,0 +1,25 @@
package net.dankito.fints.response.segments
open class UserParameters(
val userIdentifier: String,
/**
* Antwortet ein Kreditinstitut auf das Kundensegment HKVVB und der UPD-Version=0 im Segment HIUPA ebenfalls mit
* einer UPD-Version=0, so müssen im aktuellen Dialog diese übermittelten UPD verwendet werden; die UPD sind dann
* nur für diesen Dialog gültig.
*/
val updVersion: Int,
val areListedJobsBlocked: Boolean,
val username: String? = null,
/**
* Die innere Struktur dieses Parameterfeldes ist nicht weiter spezifiziert und kann von den Partnern bilateral
* verwendet werden. Zur Selektion dieses neuen Datenelementes muss HKVVB (Verarbeitungsvorbereitung) mindestens
* in der Segmentversion 3 gesendet werden.
*/
val extension: String? = null,
segmentString: String
)
: ReceivedSegment(segmentString)

View File

@ -7,10 +7,7 @@ import net.dankito.fints.messages.datenelemente.implementierte.signatur.VersionD
import net.dankito.fints.messages.datenelementgruppen.implementierte.signatur.Sicherheitsprofil
import net.dankito.fints.messages.segmente.id.ISegmentId
import net.dankito.fints.messages.segmente.id.MessageSegmentId
import net.dankito.fints.response.segments.BankParameters
import net.dankito.fints.response.segments.ReceivedMessageHeader
import net.dankito.fints.response.segments.ReceivedSynchronization
import net.dankito.fints.response.segments.SecurityMethods
import net.dankito.fints.response.segments.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
import org.junit.Test
@ -82,7 +79,6 @@ class ResponseParserTest {
?: run { Assert.fail("No segment of type BankParameters found in ${result.receivedSegments}") }
}
@Test
fun parseSecurityMethods() {
@ -107,6 +103,53 @@ class ResponseParserTest {
}
@Test
fun parseUserParameters() {
// when
val result = underTest.parse("HIUPA:6:4:4+3498443795+34+0++PERSNR0010789316542'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.UserParameters, 6, 4, 4)
result.getFirstSegmentById<UserParameters>(InstituteSegmentId.UserParameters)?.let { segment ->
assertThat(segment.userIdentifier).isEqualTo("3498443795")
assertThat(segment.updVersion).isEqualTo(34)
assertThat(segment.areListedJobsBlocked).isTrue()
assertThat(segment.username).isNull()
assertThat(segment.extension).isEqualTo("PERSNR0010789316542")
}
?: run { Assert.fail("No segment of type UserParameters found in ${result.receivedSegments}") }
}
@Test
fun parseAccountInfo() {
// when
val result = underTest.parse("HIUPD:7:6:4+0987654321::280:12345678+DE11123456780987654321+2197654321+1+EUR+Hans Dampf++Sichteinlagen++HKSAK:1+HKISA:1+HKSSP:1+HKPAE:1+HKTSY:1+HKTAB:1+HKTAU:1+HKTAZ:1+HKSPA:1+HKPKA:1+HKPKB:1+HKPWE:1+HKPWA:1+HKPWB:1+HKPWL:1+HKCAZ:1+HKCCM:1+HKCCS:1+HKCDB:1+HKCDE:1+HKCDL:1+HKCDN:1+HKCDU:1+HKCMB:1+HKCME:1+HKCML:1+HKCSA:1+HKCSB:1+HKCSE:1+HKCSL:1+HKCUB:1+HKCUM:1+HKDSB:1+HKDSW:1+HKIPS:1+HKIPZ:1+HKPCR:1+HKPPD:1+DKPSA:1+DKPSP:1+HKTAN:1+DKANA:1+DKANL:1+DKKBA:1+DKDKL:1+DKBDK:1+DKBAZ:1+DKTCK:1+DKZDF:1+DKZDL:1+HKFRD:1+HKKDM:1+HKKAZ:1+HKKIF:1+HKSAL:1+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++{\n" +
"umsltzt\n" +
"?:'")
// then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.AccountInfo, 7, 6, 4)
result.getFirstSegmentById<AccountInfo>(InstituteSegmentId.AccountInfo)?.let { segment ->
assertThat(segment.accountNumber).isEqualTo("0987654321")
assertThat(segment.subAccountAttribute).isNull()
assertThat(segment.bankCountryCode).isEqualTo(280)
assertThat(segment.bankCode).isEqualTo("12345678")
assertThat(segment.iban).isEqualTo("DE11123456780987654321")
assertThat(segment.customerId).isEqualTo("2197654321")
assertThat(segment.accountType).isEqualTo(AccountType.Girokonto)
assertThat(segment.currency).isEqualTo("EUR")
assertThat(segment.accountHolderName1).isEqualTo("Hans Dampf")
assertThat(segment.accountHolderName2).isNull()
assertThat(segment.productName).isEqualTo("Sichteinlagen")
}
?: run { Assert.fail("No segment of type AccountInfo found in ${result.receivedSegments}") }
}
private fun assertSuccessfullyParsedSegment(result: Response, segmentId: ISegmentId, segmentNumber: Int,
segmentVersion: Int, referenceSegmentNumber: Int? = null) {