Implemented retrieving credit card transactions
This commit is contained in:
parent
d16450d46b
commit
e0dbd00634
|
@ -42,7 +42,7 @@ open class FinTsClient(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
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 FindAccountTransactionsStartRegex = Regex("^HIKAZ:\\d:\\d:\\d\\+@\\d+@", RegexOption.MULTILINE)
|
||||||
val FindAccountTransactionsEndRegex = Regex("^-'", RegexOption.MULTILINE)
|
val FindAccountTransactionsEndRegex = Regex("^-'", RegexOption.MULTILINE)
|
||||||
|
@ -376,7 +376,7 @@ open class FinTsClient(
|
||||||
|
|
||||||
protected open fun getTransactionsAfterInitAndGetBalance(parameter: GetTransactionsParameter, dialogContext: DialogContext,
|
protected open fun getTransactionsAfterInitAndGetBalance(parameter: GetTransactionsParameter, dialogContext: DialogContext,
|
||||||
balanceResponse: BankResponse, callback: (GetTransactionsResponse) -> Unit) {
|
balanceResponse: BankResponse, callback: (GetTransactionsResponse) -> Unit) {
|
||||||
val balance: Money? = balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
|
var balance: Money? = balanceResponse.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let {
|
||||||
Money(it.balance, it.currency)
|
Money(it.balance, it.currency)
|
||||||
}
|
}
|
||||||
val bookedTransactions = mutableSetOf<AccountTransaction>()
|
val bookedTransactions = mutableSetOf<AccountTransaction>()
|
||||||
|
@ -397,6 +397,11 @@ open class FinTsClient(
|
||||||
|
|
||||||
parameter.retrievedChunkListener?.invoke(bookedTransactions)
|
parameter.retrievedChunkListener?.invoke(bookedTransactions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.getFirstSegmentById<ReceivedCreditCardTransactionsAndBalance>(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 ->
|
getAndHandleResponseForMessage(message, dialogContext) { response ->
|
||||||
|
|
|
@ -154,6 +154,12 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
return createGetTransactionsMessageMt940(result, parameter, dialogContext)
|
return createGetTransactionsMessageMt940(result, parameter, dialogContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val creditCardResult = supportsGetCreditCardTransactions(parameter.account)
|
||||||
|
|
||||||
|
if (creditCardResult.isJobVersionSupported) {
|
||||||
|
return createGetCreditCardTransactionsMessage(result, parameter, dialogContext)
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,14 +187,29 @@ open class MessageBuilder(protected val generator: ISegmentNumberGenerator = Seg
|
||||||
.firstOrNull { it.settingCountEntriesAllowed } != null
|
.firstOrNull { it.settingCountEntriesAllowed } != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun createGetCreditCardTransactionsMessage(result: MessageBuilderResult, parameter: GetTransactionsParameter,
|
||||||
|
dialogContext: DialogContext): MessageBuilderResult {
|
||||||
|
|
||||||
|
val segments = mutableListOf<Segment>(KreditkartenUmsaetze(generator.resetSegmentNumber(2), parameter))
|
||||||
|
|
||||||
|
addTanSegmentIfRequired(CustomerSegmentId.CreditCardTransactions, dialogContext, segments)
|
||||||
|
|
||||||
|
return createSignedMessageBuilderResult(dialogContext, segments)
|
||||||
|
}
|
||||||
|
|
||||||
open fun supportsGetTransactions(account: AccountData): Boolean {
|
open fun supportsGetTransactions(account: AccountData): Boolean {
|
||||||
return supportsGetTransactionsMt940(account).isJobVersionSupported
|
return supportsGetTransactionsMt940(account).isJobVersionSupported
|
||||||
|
|| supportsGetCreditCardTransactions(account).isJobVersionSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun supportsGetTransactionsMt940(account: AccountData): MessageBuilderResult {
|
protected open fun supportsGetTransactionsMt940(account: AccountData): MessageBuilderResult {
|
||||||
return getSupportedVersionsOfJobForAccount(CustomerSegmentId.AccountTransactionsMt940, account, listOf(5, 6, 7))
|
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 {
|
open fun createGetBalanceMessage(account: AccountData, dialogContext: DialogContext): MessageBuilderResult {
|
||||||
|
|
||||||
|
|
|
@ -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.Existenzstatus
|
||||||
import net.dankito.banking.fints.messages.datenelemente.basisformate.NumerischesDatenelement
|
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
|
* Maximale Anzahl rückzumeldender Einträge bei Abholaufträgen, Kreditinstitutsangeboten
|
||||||
* oder –informationen (vgl. [Formals], Kap. B.6.3).
|
* oder –informationen (vgl. [Formals], Kap. B.6.3).
|
||||||
*/
|
*/
|
||||||
open class MaximaleAnzahlEintraege(maxAmount: Int?, existenzstatus: Existenzstatus) : NumerischesDatenelement(maxAmount, 4, existenzstatus)
|
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
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ enum class CustomerSegmentId(override val id: String) : ISegmentId {
|
||||||
|
|
||||||
AccountTransactionsMt940("HKKAZ"),
|
AccountTransactionsMt940("HKKAZ"),
|
||||||
|
|
||||||
|
CreditCardTransactions("DKKKU"),
|
||||||
|
|
||||||
SepaBankTransfer("HKCCS"),
|
SepaBankTransfer("HKCCS"),
|
||||||
|
|
||||||
SepaInstantPaymentBankTransfer("HKIPZ"),
|
SepaInstantPaymentBankTransfer("HKIPZ"),
|
||||||
|
|
|
@ -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)
|
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.fromDate, Existenzstatus.Optional),
|
||||||
Datum(parameter.toDate, 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
|
Aufsetzpunkt(null, Existenzstatus.Optional) // will be set dynamically, see MessageBuilder.rebuildMessageWithContinuationId(); M: vom Institut wurde ein Aufsetzpunkt rückgemeldet. N: sonst
|
||||||
))
|
))
|
|
@ -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
|
||||||
|
))
|
|
@ -44,7 +44,11 @@ open class AccountTransaction(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// for object deserializers
|
// for object deserializers
|
||||||
internal constructor() : this(AccountData(), Money(Amount.Zero, ""), false, "", Date(0), null, null, null, null, Date(0), 0, 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, null, null, null, null, null, null, null, null, null,
|
||||||
null, "", "", null, null, "", null)
|
null, "", "", null, null, "", null)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
|
@ -39,6 +39,8 @@ enum class InstituteSegmentId(override val id: String) : ISegmentId {
|
||||||
|
|
||||||
AccountTransactionsMt940("HIKAZ"),
|
AccountTransactionsMt940("HIKAZ"),
|
||||||
|
|
||||||
AccountTransactionsMt940Parameters(AccountTransactionsMt940.id + "S")
|
AccountTransactionsMt940Parameters(AccountTransactionsMt940.id + "S"),
|
||||||
|
|
||||||
|
CreditCardTransactions("DIKKU")
|
||||||
|
|
||||||
}
|
}
|
|
@ -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.datenelementgruppen.implementierte.signatur.Sicherheitsprofil
|
||||||
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
|
||||||
import net.dankito.banking.fints.model.Amount
|
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.response.segments.*
|
||||||
import net.dankito.banking.fints.util.MessageUtils
|
import net.dankito.banking.fints.util.MessageUtils
|
||||||
import net.dankito.utils.multiplatform.Date
|
import net.dankito.utils.multiplatform.Date
|
||||||
|
@ -27,7 +29,7 @@ open class ResponseParser(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val JobParametersSegmentRegex = Regex("HI[A-Z]{3}S")
|
val JobParametersSegmentRegex = Regex("[H|D]I[A-Z]{3}S")
|
||||||
|
|
||||||
const val FeedbackParametersSeparator = "; "
|
const val FeedbackParametersSeparator = "; "
|
||||||
|
|
||||||
|
@ -116,6 +118,8 @@ open class ResponseParser(
|
||||||
InstituteSegmentId.AccountTransactionsMt940.id -> parseMt940AccountTransactions(segment, dataElementGroups)
|
InstituteSegmentId.AccountTransactionsMt940.id -> parseMt940AccountTransactions(segment, dataElementGroups)
|
||||||
InstituteSegmentId.AccountTransactionsMt940Parameters.id -> parseMt940AccountTransactionsParameters(segment, segmentId, dataElementGroups)
|
InstituteSegmentId.AccountTransactionsMt940Parameters.id -> parseMt940AccountTransactionsParameters(segment, segmentId, dataElementGroups)
|
||||||
|
|
||||||
|
InstituteSegmentId.CreditCardTransactions.id -> parseCreditCardTransactions(segment, dataElementGroups)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
if (JobParametersSegmentRegex.matches(segmentId)) {
|
if (JobParametersSegmentRegex.matches(segmentId)) {
|
||||||
return parseJobParameters(segment, segmentId, dataElementGroups)
|
return parseJobParameters(segment, segmentId, dataElementGroups)
|
||||||
|
@ -267,10 +271,8 @@ open class ResponseParser(
|
||||||
|
|
||||||
if (dataElements.size > 0) {
|
if (dataElements.size > 0) {
|
||||||
val jobName = parseString(dataElements[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
|
return null
|
||||||
}
|
}
|
||||||
|
@ -312,7 +314,7 @@ open class ResponseParser(
|
||||||
|
|
||||||
protected open fun parseJobParameters(segment: String, segmentId: String, dataElementGroups: List<String>): JobParameters {
|
protected open fun parseJobParameters(segment: String, segmentId: String, dataElementGroups: List<String>): JobParameters {
|
||||||
var jobName = segmentId.substring(0, 5) // cut off last 'S' (which stands for 'parameter')
|
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 maxCountJobs = parseInt(dataElementGroups[1])
|
||||||
val minimumCountSignatures = parseInt(dataElementGroups[2])
|
val minimumCountSignatures = parseInt(dataElementGroups[2])
|
||||||
|
@ -635,21 +637,28 @@ open class ResponseParser(
|
||||||
protected open fun parseBalance(dataElementGroup: String): Balance {
|
protected open fun parseBalance(dataElementGroup: String): Balance {
|
||||||
val dataElements = getDataElements(dataElementGroup)
|
val dataElements = getDataElements(dataElementGroup)
|
||||||
|
|
||||||
val isCredit = parseString(dataElements[0]) == "C"
|
val isCredit = parseIsCredit(dataElements[0])
|
||||||
|
var currency: String? = null
|
||||||
|
|
||||||
var dateIndex = 2
|
var dateIndex = 2
|
||||||
var date: Date? = parseNullableDate(dataElements[dateIndex]) // in older versions dateElements[2] was the currency
|
var date: Date? = parseNullableDate(dataElements[dateIndex]) // in older versions dateElements[2] was the currency
|
||||||
if (date == null) {
|
if (date == null) {
|
||||||
|
currency = parseString(dataElements[dateIndex])
|
||||||
date = parseDate(dataElements[++dateIndex])
|
date = parseDate(dataElements[++dateIndex])
|
||||||
}
|
}
|
||||||
|
|
||||||
return Balance(
|
return Balance(
|
||||||
parseAmount(dataElements[1], isCredit),
|
parseAmount(dataElements[1], isCredit),
|
||||||
|
currency,
|
||||||
date,
|
date,
|
||||||
if (dataElements.size > dateIndex + 1) parseTime(dataElements[dateIndex + 1]) else null
|
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<String>): ReceivedAccountTransactions {
|
protected open fun parseMt940AccountTransactions(segment: String, dataElementGroups: List<String>): ReceivedAccountTransactions {
|
||||||
val bookedTransactionsString = extractBinaryData(dataElementGroups[1])
|
val bookedTransactionsString = extractBinaryData(dataElementGroups[1])
|
||||||
|
@ -673,6 +682,42 @@ open class ResponseParser(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected open fun parseCreditCardTransactions(segment: String, dataElementGroups: List<String>): 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<String>): 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 {
|
protected open fun parseBankDetails(dataElementsGroup: String): Kreditinstitutskennung {
|
||||||
val detailsStrings = getDataElements(dataElementsGroup)
|
val detailsStrings = getDataElements(dataElementsGroup)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import net.dankito.utils.multiplatform.Date
|
||||||
|
|
||||||
open class Balance(
|
open class Balance(
|
||||||
val amount: Amount,
|
val amount: Amount,
|
||||||
|
val currency: String?,
|
||||||
val date: Date,
|
val date: Date,
|
||||||
val time: Date?
|
val time: Date?
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -6,5 +6,4 @@ open class ReceivedAccountTransactions(
|
||||||
val unbookedTransactionsString: String?, // TODO
|
val unbookedTransactionsString: String?, // TODO
|
||||||
segmentString: String
|
segmentString: String
|
||||||
|
|
||||||
)
|
) : ReceivedSegment(segmentString)
|
||||||
: ReceivedSegment(segmentString)
|
|
|
@ -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<CreditCardTransaction>,
|
||||||
|
segmentString: String
|
||||||
|
|
||||||
|
) : ReceivedSegment(segmentString)
|
|
@ -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<ReceivedCreditCardTransactionsAndBalance>(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,
|
private fun assertSuccessfullyParsedSegment(result: BankResponse, segmentId: ISegmentId, segmentNumber: Int,
|
||||||
segmentVersion: Int, referenceSegmentNumber: Int? = null) {
|
segmentVersion: Int, referenceSegmentNumber: Int? = null) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue