From e0dbd0063465d56b0653d02d7740939dc0890f33 Mon Sep 17 00:00:00 2001 From: dankito Date: Wed, 23 Sep 2020 03:49:24 +0200 Subject: [PATCH] Implemented retrieving credit card transactions --- .../net/dankito/banking/fints/FinTsClient.kt | 9 ++- .../banking/fints/messages/MessageBuilder.kt | 21 +++++++ .../account/MaximaleAnzahlEintraege.kt | 7 ++- .../messages/segmente/id/CustomerSegmentId.kt | 2 + .../KontoumsaetzeZeitraumMt940Base.kt | 2 +- .../umsaetze/KreditkartenUmsaetze.kt | 27 +++++++++ .../banking/fints/model/AccountTransaction.kt | 10 +++- .../fints/model/CreditCardTransaction.kt | 11 ++++ .../fints/response/InstituteSegmentId.kt | 4 +- .../banking/fints/response/ResponseParser.kt | 57 +++++++++++++++++-- .../fints/response/segments/Balance.kt | 1 + .../segments/ReceivedAccountTransactions.kt | 3 +- ...eceivedCreditCardTransactionsAndBalance.kt | 11 ++++ .../fints/response/ResponseParserTest.kt | 35 ++++++++++++ 14 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KreditkartenUmsaetze.kt create mode 100644 fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt create mode 100644 fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedCreditCardTransactionsAndBalance.kt diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt index 8a3b844b..781a1916 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/FinTsClient.kt @@ -42,7 +42,7 @@ open class FinTsClient( ) { companion object { - val SupportedAccountTypes = listOf(AccountType.Girokonto, AccountType.Festgeldkonto) + val SupportedAccountTypes = listOf(AccountType.Girokonto, AccountType.Festgeldkonto, AccountType.Kreditkartenkonto) val FindAccountTransactionsStartRegex = Regex("^HIKAZ:\\d:\\d:\\d\\+@\\d+@", RegexOption.MULTILINE) val FindAccountTransactionsEndRegex = Regex("^-'", RegexOption.MULTILINE) @@ -376,7 +376,7 @@ open class FinTsClient( protected open fun getTransactionsAfterInitAndGetBalance(parameter: GetTransactionsParameter, dialogContext: DialogContext, balanceResponse: BankResponse, callback: (GetTransactionsResponse) -> Unit) { - val balance: Money? = balanceResponse.getFirstSegmentById(InstituteSegmentId.Balance)?.let { + var balance: Money? = balanceResponse.getFirstSegmentById(InstituteSegmentId.Balance)?.let { Money(it.balance, it.currency) } val bookedTransactions = mutableSetOf() @@ -397,6 +397,11 @@ open class FinTsClient( parameter.retrievedChunkListener?.invoke(bookedTransactions) } + + response.getFirstSegmentById(InstituteSegmentId.CreditCardTransactions)?.let { transactionsSegment -> + balance = Money(transactionsSegment.balance.amount, transactionsSegment.balance.currency ?: "EUR") + bookedTransactions.addAll(transactionsSegment.transactions.map { AccountTransaction(parameter.account, it.amount, "", it.bookingDate, it.otherPartyName, null, null, "", it.valueDate) }) + } } getAndHandleResponseForMessage(message, dialogContext) { response -> diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt index c4159d6a..f836b92e 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/MessageBuilder.kt @@ -154,6 +154,12 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg return createGetTransactionsMessageMt940(result, parameter, dialogContext) } + val creditCardResult = supportsGetCreditCardTransactions(parameter.account) + + if (creditCardResult.isJobVersionSupported) { + return createGetCreditCardTransactionsMessage(result, parameter, dialogContext) + } + return result } @@ -181,14 +187,29 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg .firstOrNull { it.settingCountEntriesAllowed } != null } + protected open fun createGetCreditCardTransactionsMessage(result: MessageBuilderResult, parameter: GetTransactionsParameter, + dialogContext: DialogContext): MessageBuilderResult { + + val segments = mutableListOf(KreditkartenUmsaetze(generator.resetSegmentNumber(2), parameter)) + + addTanSegmentIfRequired(CustomerSegmentId.CreditCardTransactions, dialogContext, segments) + + return createSignedMessageBuilderResult(dialogContext, segments) + } + open fun supportsGetTransactions(account: AccountData): Boolean { return supportsGetTransactionsMt940(account).isJobVersionSupported + || supportsGetCreditCardTransactions(account).isJobVersionSupported } protected open fun supportsGetTransactionsMt940(account: AccountData): MessageBuilderResult { return getSupportedVersionsOfJobForAccount(CustomerSegmentId.AccountTransactionsMt940, account, listOf(5, 6, 7)) } + protected open fun supportsGetCreditCardTransactions(account: AccountData): MessageBuilderResult { + return getSupportedVersionsOfJobForAccount(CustomerSegmentId.CreditCardTransactions, account, listOf(2)) + } + open fun createGetBalanceMessage(account: AccountData, dialogContext: DialogContext): MessageBuilderResult { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/account/MaximaleAnzahlEintraege.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/account/MaximaleAnzahlEintraege.kt index 98f4d065..865550a2 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/account/MaximaleAnzahlEintraege.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/datenelemente/implementierte/account/MaximaleAnzahlEintraege.kt @@ -2,10 +2,15 @@ package net.dankito.banking.fints.messages.datenelemente.implementierte.account import net.dankito.banking.fints.messages.Existenzstatus import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement +import net.dankito.banking.fints.model.GetTransactionsParameter /** * Maximale Anzahl rückzumeldender Einträge bei Abholaufträgen, Kreditinstitutsangeboten * oder –informationen (vgl. [Formals], Kap. B.6.3). */ -open class MaximaleAnzahlEintraege(maxAmount: Int?, existenzstatus: Existenzstatus) : NumerischesDatenelement(maxAmount, 4, existenzstatus) \ No newline at end of file +open class MaximaleAnzahlEintraege(maxAmount: Int?, existenzstatus: Existenzstatus) : NumerischesDatenelement(maxAmount, 4, existenzstatus) { + + constructor(parameter: GetTransactionsParameter) : this(parameter.maxCountEntriesIfSettingItIsAllowed, if (parameter.isSettingMaxCountEntriesAllowedByBank) Existenzstatus.Optional else Existenzstatus.NotAllowed) // > 0. O: „Eingabe Anzahl Einträge erlaubt“ (BPD) = „J“. N: sonst + +} \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/id/CustomerSegmentId.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/id/CustomerSegmentId.kt index 4943c2a1..9169332d 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/id/CustomerSegmentId.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/id/CustomerSegmentId.kt @@ -21,6 +21,8 @@ enum class CustomerSegmentId(override val id: String) : ISegmentId { AccountTransactionsMt940("HKKAZ"), + CreditCardTransactions("DKKKU"), + SepaBankTransfer("HKCCS"), SepaInstantPaymentBankTransfer("HKIPZ"), diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt index 465c50af..1b370e9c 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KontoumsaetzeZeitraumMt940Base.kt @@ -34,6 +34,6 @@ abstract class KontoumsaetzeZeitraumMt940Base( AlleKonten(false, Existenzstatus.Mandatory), // currently no supported, we retrieve account transactions account by account (most banks don't support AlleKonten anyway) Datum(parameter.fromDate, Existenzstatus.Optional), Datum(parameter.toDate, Existenzstatus.Optional), - MaximaleAnzahlEintraege(parameter.maxCountEntriesIfSettingItIsAllowed, if (parameter.isSettingMaxCountEntriesAllowedByBank) Existenzstatus.Optional else Existenzstatus.NotAllowed), // > 0. O: „Eingabe Anzahl Einträge erlaubt“ (BPD) = „J“. N: sonst + MaximaleAnzahlEintraege(parameter), Aufsetzpunkt(null, Existenzstatus.Optional) // will be set dynamically, see MessageBuilder.rebuildMessageWithContinuationId(); M: vom Institut wurde ein Aufsetzpunkt rückgemeldet. N: sonst )) \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KreditkartenUmsaetze.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KreditkartenUmsaetze.kt new file mode 100644 index 00000000..86bf0ddc --- /dev/null +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/messages/segmente/implementierte/umsaetze/KreditkartenUmsaetze.kt @@ -0,0 +1,27 @@ +package net.dankito.banking.fints.messages.segmente.implementierte.umsaetze + +import net.dankito.banking.fints.messages.Existenzstatus +import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum +import net.dankito.banking.fints.messages.datenelemente.basisformate.AlphanumerischesDatenelement +import net.dankito.banking.fints.messages.datenelemente.implementierte.Aufsetzpunkt +import net.dankito.banking.fints.messages.datenelemente.implementierte.account.MaximaleAnzahlEintraege +import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.Segmentkopf +import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.account.Kontoverbindung +import net.dankito.banking.fints.messages.segmente.Segment +import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId +import net.dankito.banking.fints.model.GetTransactionsParameter + + +open class KreditkartenUmsaetze( + segmentNumber: Int, + parameter: GetTransactionsParameter +) : Segment(listOf( + Segmentkopf(CustomerSegmentId.CreditCardTransactions, 2, segmentNumber), + Kontoverbindung(parameter.account), + AlphanumerischesDatenelement(parameter.account.accountIdentifier, Existenzstatus.Mandatory), + AlphanumerischesDatenelement(parameter.account.accountIdentifier, Existenzstatus.Mandatory), // TODO: find out what this value really should be; works for Comdirect, but does it work generally? + Datum(parameter.fromDate, Existenzstatus.Optional), + Datum(parameter.toDate, Existenzstatus.Optional), + MaximaleAnzahlEintraege(parameter), + Aufsetzpunkt(null, Existenzstatus.Optional) // will be set dynamically, see MessageBuilder.rebuildMessageWithContinuationId(); M: vom Institut wurde ein Aufsetzpunkt rückgemeldet. N: sonst +)) \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt index ede6dc59..77984170 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/AccountTransaction.kt @@ -44,9 +44,13 @@ open class AccountTransaction( ) { // for object deserializers - internal constructor() : this(AccountData(), Money(Amount.Zero, ""), false, "", Date(0), null, null, null, null, Date(0), 0, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, - null, "", "", null, null, "", null) + internal constructor() : this(AccountData(), Money(Amount.Zero, ""), "", Date(0), null, null, null, null, Date(0)) + + constructor(account: AccountData, amount: Money, unparsedUsage: String, bookingDate: Date, otherPartyName: String?, otherPartyBankCode: String?, otherPartyAccountId: String?, bookingText: String?, valueDate: Date) + : this(account, amount, false, unparsedUsage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, + 0, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, + null, "", "", null, null, "", null) override fun equals(other: Any?): Boolean { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt new file mode 100644 index 00000000..1170f8c4 --- /dev/null +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/model/CreditCardTransaction.kt @@ -0,0 +1,11 @@ +package net.dankito.banking.fints.model + +import net.dankito.utils.multiplatform.Date + + +open class CreditCardTransaction( + val amount: Money, + val otherPartyName: String, + val bookingDate: Date, + val valueDate: Date +) \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/InstituteSegmentId.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/InstituteSegmentId.kt index 93a929d5..db0a5f2b 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/InstituteSegmentId.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/InstituteSegmentId.kt @@ -39,6 +39,8 @@ enum class InstituteSegmentId(override val id: String) : ISegmentId { AccountTransactionsMt940("HIKAZ"), - AccountTransactionsMt940Parameters(AccountTransactionsMt940.id + "S") + AccountTransactionsMt940Parameters(AccountTransactionsMt940.id + "S"), + + CreditCardTransactions("DIKKU") } \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt index 00f7d99d..ae130773 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/ResponseParser.kt @@ -15,6 +15,8 @@ import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.acc import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.signatur.Sicherheitsprofil import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId import net.dankito.banking.fints.model.Amount +import net.dankito.banking.fints.model.CreditCardTransaction +import net.dankito.banking.fints.model.Money import net.dankito.banking.fints.response.segments.* import net.dankito.banking.fints.util.MessageUtils import net.dankito.utils.multiplatform.Date @@ -27,7 +29,7 @@ open class ResponseParser( ) { companion object { - val JobParametersSegmentRegex = Regex("HI[A-Z]{3}S") + val JobParametersSegmentRegex = Regex("[H|D]I[A-Z]{3}S") const val FeedbackParametersSeparator = "; " @@ -116,6 +118,8 @@ open class ResponseParser( InstituteSegmentId.AccountTransactionsMt940.id -> parseMt940AccountTransactions(segment, dataElementGroups) InstituteSegmentId.AccountTransactionsMt940Parameters.id -> parseMt940AccountTransactionsParameters(segment, segmentId, dataElementGroups) + InstituteSegmentId.CreditCardTransactions.id -> parseCreditCardTransactions(segment, dataElementGroups) + else -> { if (JobParametersSegmentRegex.matches(segmentId)) { return parseJobParameters(segment, segmentId, dataElementGroups) @@ -267,9 +271,7 @@ open class ResponseParser( if (dataElements.size > 0) { val jobName = parseString(dataElements[0]) - if (jobName.startsWith("HK")) { // filter out jobs not standardized by Deutsche Kreditwirtschaft (Verbandseigene Geschaeftsvorfaelle) - return jobName - } + return jobName } return null @@ -312,7 +314,7 @@ open class ResponseParser( protected open fun parseJobParameters(segment: String, segmentId: String, dataElementGroups: List): JobParameters { var jobName = segmentId.substring(0, 5) // cut off last 'S' (which stands for 'parameter') - jobName = jobName.replaceFirst("HI", "HK") + jobName = jobName.replaceFirst("HI", "HK").replaceFirst("DI", "DK") val maxCountJobs = parseInt(dataElementGroups[1]) val minimumCountSignatures = parseInt(dataElementGroups[2]) @@ -635,21 +637,28 @@ open class ResponseParser( protected open fun parseBalance(dataElementGroup: String): Balance { val dataElements = getDataElements(dataElementGroup) - val isCredit = parseString(dataElements[0]) == "C" + val isCredit = parseIsCredit(dataElements[0]) + var currency: String? = null var dateIndex = 2 var date: Date? = parseNullableDate(dataElements[dateIndex]) // in older versions dateElements[2] was the currency if (date == null) { + currency = parseString(dataElements[dateIndex]) date = parseDate(dataElements[++dateIndex]) } return Balance( parseAmount(dataElements[1], isCredit), + currency, date, if (dataElements.size > dateIndex + 1) parseTime(dataElements[dateIndex + 1]) else null ) } + protected open fun parseIsCredit(isCredit: String): Boolean { + return parseString(isCredit) == "C" + } + protected open fun parseMt940AccountTransactions(segment: String, dataElementGroups: List): ReceivedAccountTransactions { val bookedTransactionsString = extractBinaryData(dataElementGroups[1]) @@ -673,6 +682,42 @@ open class ResponseParser( } + protected open fun parseCreditCardTransactions(segment: String, dataElementGroups: List): ReceivedCreditCardTransactionsAndBalance { + val balance = parseBalance(dataElementGroups[3]) + val transactionsDataElementGroups = dataElementGroups.subList(6, dataElementGroups.size) + + return ReceivedCreditCardTransactionsAndBalance( + balance, + transactionsDataElementGroups.map { mapCreditCardTransaction(it) }, + segment + ) + } + + protected open fun mapCreditCardTransaction(transactionDataElementGroup: String): CreditCardTransaction { + val dataElements = getDataElements(transactionDataElementGroup) + + val bookingDate = parseDate(dataElements[1]) + val valueDate = parseDate(dataElements[2]) + val amount = parseCreditCardAmount(dataElements.subList(4, 7)) + val otherPartyName = parseString(dataElements[11]) + + return CreditCardTransaction(amount, otherPartyName, bookingDate, valueDate) + } + + private fun parseCreditCardAmount(amountDataElements: List): Money { + val currency = parseString(amountDataElements[1]) + val isCredit = parseIsCredit(amountDataElements[2]) + + var amountString = parseString(amountDataElements[0]) + + if (isCredit == false) { + amountString = "-" + amountString + } + + return Money(Amount(amountString), currency) + } + + protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung { val detailsStrings = getDataElements(dataElementsGroup) diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt index e154fe42..43ce6269 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/Balance.kt @@ -6,6 +6,7 @@ import net.dankito.utils.multiplatform.Date open class Balance( val amount: Amount, + val currency: String?, val date: Date, val time: Date? ) { diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedAccountTransactions.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedAccountTransactions.kt index 44561fef..0bbba54e 100644 --- a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedAccountTransactions.kt +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedAccountTransactions.kt @@ -6,5 +6,4 @@ open class ReceivedAccountTransactions( val unbookedTransactionsString: String?, // TODO segmentString: String -) - : ReceivedSegment(segmentString) \ No newline at end of file +) : ReceivedSegment(segmentString) \ No newline at end of file diff --git a/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedCreditCardTransactionsAndBalance.kt b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedCreditCardTransactionsAndBalance.kt new file mode 100644 index 00000000..9f47b93c --- /dev/null +++ b/fints4k/src/commonMain/kotlin/net/dankito/banking/fints/response/segments/ReceivedCreditCardTransactionsAndBalance.kt @@ -0,0 +1,11 @@ +package net.dankito.banking.fints.response.segments + +import net.dankito.banking.fints.model.CreditCardTransaction + + +open class ReceivedCreditCardTransactionsAndBalance( + val balance: Balance, + val transactions: List, + segmentString: String + +) : ReceivedSegment(segmentString) \ No newline at end of file diff --git a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt index 597041f5..0afc8cf7 100644 --- a/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt +++ b/fints4k/src/commonTest/kotlin/net/dankito/banking/fints/response/ResponseParserTest.kt @@ -1084,6 +1084,41 @@ class ResponseParserTest : FinTsTestBase() { } + @Test + fun parseCreditCardAccountTransactions() { + + // given + val creditCardNumber = "4263540122107989" + val balance = "189,5" + val otherPartyName = "Bundesanzeiger Verlag Koeln 000" + val amount = "6,5" + + // when + val result = underTest.parse("DIKKU:7:2:3+$creditCardNumber++C:$balance:EUR:20200923:021612+++" + + "$creditCardNumber:20200819:20200820::$amount:EUR:D:1,:$amount:EUR:D:$otherPartyName:::::::::J:120082048947201+" + + "$creditCardNumber:20200819:20200820::$amount:EUR:D:1,:$amount:EUR:D:$otherPartyName:::::::::J:120082048947101'") + + // then + assertSuccessfullyParsedSegment(result, InstituteSegmentId.CreditCardTransactions, 7, 2, 3) + + result.getFirstSegmentById(InstituteSegmentId.CreditCardTransactions)?.let { segment -> + expect(segment.balance.amount.string).toBe(balance) + expect(segment.balance.date).toBe(Date(2020, 9, 23)) + expect(segment.balance.time).notToBeNull() + expect(segment.transactions.size).toBe(2) + + segment.transactions.forEach { transaction -> + expect(transaction.otherPartyName).toBe(otherPartyName) + expect(transaction.bookingDate).toBe(Date(2020, 8, 19)) + expect(transaction.valueDate).toBe(Date(2020, 8, 20)) + expect(transaction.amount.amount.string).toBe("-" + amount) + expect(transaction.amount.currency.code).toBe("EUR") + } + } + ?: run { fail("No segment of type ReceivedCreditCardTransactionsAndBalance found in ${result.receivedSegments}") } + } + + private fun assertSuccessfullyParsedSegment(result: BankResponse, segmentId: ISegmentId, segmentNumber: Int, segmentVersion: Int, referenceSegmentNumber: Int? = null) {