Implemented Mt942Parser
This commit is contained in:
parent
d1de7f5eb0
commit
90a7543641
|
@ -0,0 +1,86 @@
|
||||||
|
package net.codinux.banking.fints.transactions.mt940
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import net.codinux.banking.fints.log.IMessageLogAppender
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.AmountAndCurrency
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.InterimAccountStatement
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.NumberOfPostingsAndAmount
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.Transaction
|
||||||
|
|
||||||
|
open class Mt942Parser(
|
||||||
|
logAppender: IMessageLogAppender? = null
|
||||||
|
) : Mt94xParserBase<InterimAccountStatement>(logAppender) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a whole MT 942 statements string, that is one that ends with a "-" line.
|
||||||
|
*/
|
||||||
|
open fun parseMt942String(mt942String: String): List<InterimAccountStatement> =
|
||||||
|
super.parseMt94xString(mt942String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses incomplete MT 942 statements string, that is ones that not end with a "-" line,
|
||||||
|
* as they are returned e.g. if a HKKAZ response is dispersed over multiple messages.
|
||||||
|
*
|
||||||
|
* Tries to parse all statements in the string except an incomplete last one and returns an
|
||||||
|
* incomplete last MT 942 statement (if any) as remainder.
|
||||||
|
*
|
||||||
|
* So each single HKKAZ partial response can be parsed immediately, its statements/transactions
|
||||||
|
* be displayed immediately to user and the remainder then be passed together with next partial
|
||||||
|
* HKKAZ response to this method till this whole MT 942 statement is parsed.
|
||||||
|
*/
|
||||||
|
open fun parseMt942Chunk(mt942Chunk: String): Pair<List<InterimAccountStatement>, String?> =
|
||||||
|
super.parseMt94xChunk(mt942Chunk)
|
||||||
|
|
||||||
|
|
||||||
|
override fun createAccountStatement(
|
||||||
|
orderReferenceNumber: String,
|
||||||
|
referenceNumber: String?,
|
||||||
|
bankCodeBicOrIban: String,
|
||||||
|
accountIdentifier: String?,
|
||||||
|
statementNumber: Int,
|
||||||
|
sheetNumber: Int?,
|
||||||
|
transactions: List<Transaction>,
|
||||||
|
fieldsByCode: List<Pair<String, String>>
|
||||||
|
): InterimAccountStatement {
|
||||||
|
val smallestAmounts = fieldsByCode.filter { it.first.startsWith(SmallestAmountCode) } // should we parse it? i see no use in it
|
||||||
|
.mapIndexed { index, field -> parseAmountAndCurrency(field.second, index == 0) }
|
||||||
|
val creationTime = fieldsByCode.first { it.first == CreationTimeCode || it.first.startsWith(CreationTimeStartCode) } // should we parse it? i see no use in it
|
||||||
|
|
||||||
|
val numberAndTotalOfDebitPostings = fieldsByCode.firstOrNull { it.first.equals(AmountOfDebitPostingsCode) }
|
||||||
|
?.let { parseNumberAndTotalOfPostings(it.second) }
|
||||||
|
val numberAndTotalOfCreditPostings = fieldsByCode.firstOrNull { it.first.equals(AmountOfCreditPostingsCode) }
|
||||||
|
?.let { parseNumberAndTotalOfPostings(it.second) }
|
||||||
|
|
||||||
|
return InterimAccountStatement(
|
||||||
|
orderReferenceNumber, referenceNumber,
|
||||||
|
bankCodeBicOrIban, accountIdentifier,
|
||||||
|
statementNumber, sheetNumber,
|
||||||
|
smallestAmounts.first(),
|
||||||
|
if (smallestAmounts.size > 1) smallestAmounts[1] else null,
|
||||||
|
Instant.DISTANT_PAST,
|
||||||
|
transactions,
|
||||||
|
numberAndTotalOfDebitPostings,
|
||||||
|
numberAndTotalOfCreditPostings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseAmountAndCurrency(fieldValue: String, isCreditCharOptional: Boolean = false): AmountAndCurrency {
|
||||||
|
val currency = fieldValue.substring(0, 3)
|
||||||
|
val hasCreditChar = isCreditCharOptional == false || fieldValue[3].isLetter()
|
||||||
|
val isCredit = if (hasCreditChar) fieldValue[3] == 'C' else false
|
||||||
|
val amount = fieldValue.substring(if (hasCreditChar) 4 else 3)
|
||||||
|
|
||||||
|
return AmountAndCurrency(amount, currency, isCredit)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun parseNumberAndTotalOfPostings(fieldValue: String): NumberOfPostingsAndAmount {
|
||||||
|
val currencyStartIndex = fieldValue.indexOfFirst { it.isLetter() }
|
||||||
|
|
||||||
|
val numberOfPostings = fieldValue.substring(0, currencyStartIndex).toInt()
|
||||||
|
val currency = fieldValue.substring(currencyStartIndex, currencyStartIndex + 3)
|
||||||
|
val amount = fieldValue.substring(currencyStartIndex + 3)
|
||||||
|
|
||||||
|
return NumberOfPostingsAndAmount(numberOfPostings, amount, currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -41,15 +41,31 @@ abstract class Mt94xParserBase<T: AccountStatementCommon>(
|
||||||
|
|
||||||
const val StatementNumberCode = "28C"
|
const val StatementNumberCode = "28C"
|
||||||
|
|
||||||
const val OpeningBalanceCode = "60"
|
|
||||||
|
|
||||||
const val StatementLineCode = "61"
|
const val StatementLineCode = "61"
|
||||||
|
|
||||||
const val RemittanceInformationFieldCode = "86"
|
const val RemittanceInformationFieldCode = "86"
|
||||||
|
|
||||||
|
|
||||||
|
// MT 940 codes
|
||||||
|
|
||||||
|
const val OpeningBalanceCode = "60"
|
||||||
|
|
||||||
const val ClosingBalanceCode = "62"
|
const val ClosingBalanceCode = "62"
|
||||||
|
|
||||||
|
|
||||||
|
// MT 942 codes
|
||||||
|
|
||||||
|
const val SmallestAmountCode = "34F"
|
||||||
|
const val SmallestAmountStartCode = "34"
|
||||||
|
|
||||||
|
const val CreationTimeCode = "13D"
|
||||||
|
const val CreationTimeStartCode = "13" // Deutsche Bank and Sparkasse both use "13" instead of correct "13D"
|
||||||
|
|
||||||
|
const val AmountOfDebitPostingsCode = "90D"
|
||||||
|
|
||||||
|
const val AmountOfCreditPostingsCode = "90C"
|
||||||
|
|
||||||
|
|
||||||
val DateFormatter = DateFormatter("yyMMdd") // TODO: replace with LocalDate.Format { }
|
val DateFormatter = DateFormatter("yyMMdd") // TODO: replace with LocalDate.Format { }
|
||||||
|
|
||||||
val CreditDebitCancellationRegex = Regex("C|D|RC|RD")
|
val CreditDebitCancellationRegex = Regex("C|D|RC|RD")
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package net.codinux.banking.fints.transactions.mt940.model
|
||||||
|
|
||||||
|
class AmountAndCurrency(
|
||||||
|
val amount: String,
|
||||||
|
val currency: String,
|
||||||
|
val isCredit: Boolean
|
||||||
|
) {
|
||||||
|
internal constructor() : this("not an amount", "not a currency", false) // for object deserializers
|
||||||
|
|
||||||
|
override fun toString() = "${if (isCredit == false) "-" else ""}$amount $currency"
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package net.codinux.banking.fints.transactions.mt940.model
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
|
||||||
|
open class InterimAccountStatement(
|
||||||
|
orderReferenceNumber: String,
|
||||||
|
referenceNumber: String?,
|
||||||
|
|
||||||
|
bankCodeBicOrIban: String,
|
||||||
|
accountIdentifier: String?,
|
||||||
|
|
||||||
|
statementNumber: Int,
|
||||||
|
sheetNumber: Int?,
|
||||||
|
|
||||||
|
val smallestAmountOfReportedTransactions: AmountAndCurrency,
|
||||||
|
|
||||||
|
val smallestAmountOfReportedCreditTransactions: AmountAndCurrency? = null,
|
||||||
|
|
||||||
|
val creationTime: Instant,
|
||||||
|
|
||||||
|
transactions: List<Transaction>,
|
||||||
|
|
||||||
|
val amountAndTotalOfDebitPostings: NumberOfPostingsAndAmount? = null,
|
||||||
|
|
||||||
|
val amountAndTotalOfCreditPostings: NumberOfPostingsAndAmount? = null,
|
||||||
|
|
||||||
|
) : AccountStatementCommon(orderReferenceNumber, referenceNumber, bankCodeBicOrIban, accountIdentifier, statementNumber, sheetNumber, transactions) {
|
||||||
|
|
||||||
|
// for object deserializers
|
||||||
|
private constructor() : this("", "", "", null, 0, null, AmountAndCurrency(), null, Instant.DISTANT_PAST, listOf())
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$smallestAmountOfReportedTransactions ${super.toString()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package net.codinux.banking.fints.transactions.mt940.model
|
||||||
|
|
||||||
|
class NumberOfPostingsAndAmount(
|
||||||
|
val numberOfPostings: Int,
|
||||||
|
val amount: String,
|
||||||
|
val currency: String
|
||||||
|
) {
|
||||||
|
private constructor() : this(-1, "not an amount", "not a currency") // for object deserializers
|
||||||
|
|
||||||
|
override fun toString() = "$amount $currency, $numberOfPostings posting(s)"
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
package net.codinux.banking.fints.transactions
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.fints.test.assertEquals
|
||||||
|
import net.codinux.banking.fints.test.assertNull
|
||||||
|
import net.codinux.banking.fints.test.assertSize
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.Mt942Parser
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.InterimAccountStatement
|
||||||
|
import net.codinux.banking.fints.transactions.mt940.model.Transaction
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
class Mt942ParserTest {
|
||||||
|
|
||||||
|
private val underTest = Mt942Parser()
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseNullValuesMt942String() {
|
||||||
|
// speciality of Deutsche Bank, it adds a MT942 if there are prebookings or not, so in most cases contains simply empty values
|
||||||
|
val mt942String = """
|
||||||
|
:20:DEUTDEFFXXXX
|
||||||
|
:25:70070024/01234560000
|
||||||
|
:28C:00000/001
|
||||||
|
:34F:EUR0,
|
||||||
|
:13:2408212359
|
||||||
|
:90D:0EUR0,
|
||||||
|
:90C:0EUR0,
|
||||||
|
-
|
||||||
|
:20:DEUTDEFFXXXX
|
||||||
|
:25:00000000/DE08700700240012345600
|
||||||
|
:28C:00000/001
|
||||||
|
:34F:EURC0,
|
||||||
|
:13D:2408210442+0200
|
||||||
|
:90D:0EUR0,
|
||||||
|
:90C:0EUR0,
|
||||||
|
-
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
|
||||||
|
val result = underTest.parseMt942String(mt942String)
|
||||||
|
|
||||||
|
|
||||||
|
assertSize(2, result)
|
||||||
|
|
||||||
|
|
||||||
|
val firstStatement = result.first()
|
||||||
|
|
||||||
|
assertNullValuesStatement(firstStatement)
|
||||||
|
assertEquals("70070024", firstStatement.bankCodeBicOrIban)
|
||||||
|
assertEquals("01234560000", firstStatement.accountIdentifier)
|
||||||
|
|
||||||
|
|
||||||
|
val secondStatement = result[1]
|
||||||
|
|
||||||
|
assertNullValuesStatement(secondStatement)
|
||||||
|
assertEquals("00000000", secondStatement.bankCodeBicOrIban)
|
||||||
|
assertEquals("DE08700700240012345600", secondStatement.accountIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseDkExampleMt942String() {
|
||||||
|
// see
|
||||||
|
val mt942String = """
|
||||||
|
:20:1234567
|
||||||
|
:21:9876543210
|
||||||
|
:25:10020030/1234567
|
||||||
|
:28C:5/1
|
||||||
|
:34F:EURD20,50
|
||||||
|
:34F:EURC155,34
|
||||||
|
:13D:C1311130945+0000
|
||||||
|
:61:1311131113CR155,34NTRFNONREF//55555
|
||||||
|
:86:166?00SEPA-UEBERWEISUNG?109315
|
||||||
|
?20EREF+987654123456?21SVWZ+Invoice no.
|
||||||
|
123455056?22734 und 123455056735
|
||||||
|
?30COLSDE33XXX?31DE91370501980100558000
|
||||||
|
?32Max Mustermann
|
||||||
|
:61:1311131113DR20,50NDDTNONREF//55555
|
||||||
|
:86:105?00SEPA-BASIS-LASTSCHRIFT?109316
|
||||||
|
?20EREF+987654123497?21MREF+10023?22CRED+DE5
|
||||||
|
4ZZZ09999999999?23SVWZ+Insurance premium 2
|
||||||
|
?24013?30WELADED1MST?31DE87240501501234567890
|
||||||
|
?32XYZ Insurance limited?34991
|
||||||
|
:90D:1EUR20,50
|
||||||
|
:90C:1EUR155,34
|
||||||
|
-
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
|
||||||
|
val result = underTest.parseMt942String(mt942String)
|
||||||
|
|
||||||
|
|
||||||
|
assertSize(1, result)
|
||||||
|
|
||||||
|
val statement = result.first()
|
||||||
|
|
||||||
|
assertEquals("1234567", statement.orderReferenceNumber)
|
||||||
|
assertEquals("9876543210", statement.referenceNumber)
|
||||||
|
|
||||||
|
assertEquals("10020030", statement.bankCodeBicOrIban)
|
||||||
|
assertEquals("1234567", statement.accountIdentifier)
|
||||||
|
|
||||||
|
assertEquals(5, statement.statementNumber)
|
||||||
|
assertEquals(1, statement.sheetNumber)
|
||||||
|
|
||||||
|
assertEquals("20,50", statement.smallestAmountOfReportedTransactions.amount)
|
||||||
|
assertEquals("EUR", statement.smallestAmountOfReportedTransactions.currency)
|
||||||
|
assertEquals(false, statement.smallestAmountOfReportedTransactions.isCredit)
|
||||||
|
|
||||||
|
assertEquals("155,34", statement.smallestAmountOfReportedCreditTransactions?.amount)
|
||||||
|
assertEquals("EUR", statement.smallestAmountOfReportedCreditTransactions?.currency)
|
||||||
|
assertEquals(true, statement.smallestAmountOfReportedCreditTransactions?.isCredit)
|
||||||
|
|
||||||
|
assertEquals(1, statement.amountAndTotalOfDebitPostings?.numberOfPostings)
|
||||||
|
assertEquals("20,50", statement.amountAndTotalOfDebitPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfDebitPostings?.currency)
|
||||||
|
|
||||||
|
assertEquals(1, statement.amountAndTotalOfCreditPostings?.numberOfPostings)
|
||||||
|
assertEquals("155,34", statement.amountAndTotalOfCreditPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfCreditPostings?.currency)
|
||||||
|
|
||||||
|
|
||||||
|
assertSize(2, statement.transactions)
|
||||||
|
|
||||||
|
val firstTransaction = statement.transactions.first()
|
||||||
|
assertTransactionStatementLine(firstTransaction, LocalDate(2013, 11, 13), LocalDate(2013, 11, 13), "155,34", true)
|
||||||
|
assertTransactionReference(firstTransaction, "SEPA-UEBERWEISUNG", "Max Mustermann", "COLSDE33XXX", "DE91370501980100558000", "Invoice no.123455056734 und 123455056735", "987654123456 ", null, null)
|
||||||
|
|
||||||
|
val secondTransaction = statement.transactions[1]
|
||||||
|
assertTransactionStatementLine(secondTransaction, LocalDate(2013, 11, 13), LocalDate(2013, 11, 13), "20,50", false)
|
||||||
|
assertTransactionReference(secondTransaction, "SEPA-BASIS-LASTSCHRIFT", "XYZ Insurance limited", "WELADED1MST", "DE87240501501234567890", "Insurance premium 2013", "987654123497 ", "10023 ", "DE54ZZZ09999999999 ")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parseSparkasseMt942String() {
|
||||||
|
val mt942String = """
|
||||||
|
:20:STARTDISPE
|
||||||
|
:25:70050000/0123456789
|
||||||
|
:28C:00000/001
|
||||||
|
:34F:EURD60,77
|
||||||
|
:13:2408232156
|
||||||
|
:61:2408260823DR60,77NDDTNONREF
|
||||||
|
:86:105?00FOLGELASTSCHRIFT?109248?20EREF+R0012345678?21MREF+M-K12
|
||||||
|
34567890-0001?22CRED+DE63ZZZ00000012345?23SVWZ+Rechnungsnr.. R001
|
||||||
|
2345?246789 - Kundennr.. K123456789?251?30HYVEDEMM406?31DE80765200
|
||||||
|
710123456789?32Dein Cloud Provider?34992
|
||||||
|
:90D:1EUR60,77
|
||||||
|
:90C:0EUR0,00
|
||||||
|
-
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
|
||||||
|
val result = underTest.parseMt942String(mt942String)
|
||||||
|
|
||||||
|
|
||||||
|
assertSize(1, result)
|
||||||
|
|
||||||
|
val statement = result.first()
|
||||||
|
|
||||||
|
assertEquals("STARTDISPE", statement.orderReferenceNumber)
|
||||||
|
assertNull(statement.referenceNumber)
|
||||||
|
|
||||||
|
assertEquals("70050000", statement.bankCodeBicOrIban)
|
||||||
|
assertEquals("0123456789", statement.accountIdentifier)
|
||||||
|
|
||||||
|
assertEquals(0, statement.statementNumber)
|
||||||
|
assertEquals(1, statement.sheetNumber)
|
||||||
|
|
||||||
|
assertEquals("60,77", statement.smallestAmountOfReportedTransactions.amount)
|
||||||
|
assertEquals("EUR", statement.smallestAmountOfReportedTransactions.currency)
|
||||||
|
assertEquals(false, statement.smallestAmountOfReportedTransactions.isCredit)
|
||||||
|
assertNull(statement.smallestAmountOfReportedCreditTransactions)
|
||||||
|
|
||||||
|
assertEquals(1, statement.amountAndTotalOfDebitPostings?.numberOfPostings)
|
||||||
|
assertEquals("60,77", statement.amountAndTotalOfDebitPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfDebitPostings?.currency)
|
||||||
|
|
||||||
|
assertEquals(0, statement.amountAndTotalOfCreditPostings?.numberOfPostings)
|
||||||
|
assertEquals("0,00", statement.amountAndTotalOfCreditPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfCreditPostings?.currency)
|
||||||
|
|
||||||
|
|
||||||
|
assertSize(1, statement.transactions)
|
||||||
|
|
||||||
|
val transaction = statement.transactions.first()
|
||||||
|
|
||||||
|
assertTransactionStatementLine(transaction, LocalDate(2024, 8, 23), LocalDate(2024, 8, 26), "60,77", false)
|
||||||
|
|
||||||
|
assertTransactionReference(transaction, "FOLGELASTSCHRIFT", "Dein Cloud Provider", "HYVEDEMM406", "DE80765200710123456789",
|
||||||
|
"Rechnungsnr.. R00123456789 - Kundennr.. K1234567891", "R0012345678 ", "M-K1234567890-0001 ", "DE63ZZZ00000012345 ")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun assertNullValuesStatement(statement: InterimAccountStatement) {
|
||||||
|
assertEquals("DEUTDEFFXXXX", statement.orderReferenceNumber)
|
||||||
|
assertNull(statement.referenceNumber)
|
||||||
|
|
||||||
|
assertEquals(0, statement.statementNumber)
|
||||||
|
assertEquals(1, statement.sheetNumber)
|
||||||
|
|
||||||
|
assertEquals("0,", statement.smallestAmountOfReportedTransactions.amount)
|
||||||
|
assertEquals("EUR", statement.smallestAmountOfReportedTransactions.currency)
|
||||||
|
|
||||||
|
assertSize(0, statement.transactions)
|
||||||
|
|
||||||
|
assertEquals(0, statement.amountAndTotalOfDebitPostings?.numberOfPostings)
|
||||||
|
assertEquals("0,", statement.amountAndTotalOfDebitPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfDebitPostings?.currency)
|
||||||
|
|
||||||
|
assertEquals(0, statement.amountAndTotalOfCreditPostings?.numberOfPostings)
|
||||||
|
assertEquals("0,", statement.amountAndTotalOfCreditPostings?.amount)
|
||||||
|
assertEquals("EUR", statement.amountAndTotalOfCreditPostings?.currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertTransactionStatementLine(transaction: Transaction, bookingDate: LocalDate, valueDate: LocalDate, amount: String, isCredit: Boolean, isReversal: Boolean = false) {
|
||||||
|
assertEquals(bookingDate, transaction.statementLine.bookingDate)
|
||||||
|
assertEquals(valueDate, transaction.statementLine.valueDate)
|
||||||
|
assertEquals(amount, transaction.statementLine.amount.string)
|
||||||
|
assertEquals(isCredit, transaction.statementLine.isCredit)
|
||||||
|
assertEquals(isReversal, transaction.statementLine.isReversal)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertTransactionReference(transaction: Transaction,
|
||||||
|
postingText: String, otherPartyName: String?, otherPartyBankId: String?, otherPartyAccountId: String?,
|
||||||
|
sepaReference: String, endToEndReference: String? = null, mandateReference: String? = null, creditorIdentifier: String? = null
|
||||||
|
) {
|
||||||
|
assertEquals(postingText, transaction.information?.postingText)
|
||||||
|
assertEquals(otherPartyName, transaction.information?.otherPartyName)
|
||||||
|
assertEquals(otherPartyBankId, transaction.information?.otherPartyBankId)
|
||||||
|
assertEquals(otherPartyAccountId, transaction.information?.otherPartyAccountId)
|
||||||
|
|
||||||
|
assertEquals(sepaReference, transaction.information?.sepaReference)
|
||||||
|
assertEquals(endToEndReference, transaction.information?.endToEndReference)
|
||||||
|
assertEquals(mandateReference, transaction.information?.mandateReference)
|
||||||
|
assertEquals(creditorIdentifier, transaction.information?.creditorIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue