Introduced Amount to be able to remove Ionspin BigDecimal dependency

This commit is contained in:
dankito 2020-07-02 23:08:35 +02:00
parent 9ea9095e8b
commit 03bdb19668
21 changed files with 127 additions and 117 deletions

View File

@ -53,8 +53,6 @@ kotlin {
api "com.soywiz.korlibs.klock:klock:$klockVersion" api "com.soywiz.korlibs.klock:klock:$klockVersion"
api "com.ionspin.kotlin:bignum:$bigNumVersion"
implementation "com.benasher44:uuid:$uuidVersion" implementation "com.benasher44:uuid:$uuidVersion"
} }
} }
@ -129,8 +127,6 @@ kotlin {
implementation "com.soywiz.korlibs.klock:klock-js:$klockVersion" implementation "com.soywiz.korlibs.klock:klock-js:$klockVersion"
implementation "com.ionspin.kotlin:bignum-js:$bigNumVersion"
} }
} }

View File

@ -32,7 +32,7 @@ open class SepaBankTransferBase(
"CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName), "CreditorName" to messageCreator.convertDiacriticsAndReservedXmlCharacters(data.creditorName),
"CreditorIban" to data.creditorIban.replace(" ", ""), "CreditorIban" to data.creditorIban.replace(" ", ""),
"CreditorBic" to data.creditorBic.replace(" ", ""), "CreditorBic" to data.creditorBic.replace(" ", ""),
"Amount" to data.amount.toStringExpanded(), // TODO: check if ',' or '.' should be used as decimal separator "Amount" to data.amount.amount.string.replace(',', '.'), // TODO: check if ',' or '.' should be used as decimal separator
"Usage" to if (data.usage.isEmpty()) " " else messageCreator.convertDiacriticsAndReservedXmlCharacters(data.usage), "Usage" to if (data.usage.isEmpty()) " " else messageCreator.convertDiacriticsAndReservedXmlCharacters(data.usage),
"RequestedExecutionDate" to RequestedExecutionDateValueForNotScheduledTransfers "RequestedExecutionDate" to RequestedExecutionDateValueForNotScheduledTransfers
), ),

View File

@ -1,6 +1,5 @@
package net.dankito.banking.fints.model package net.dankito.banking.fints.model
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
@ -46,7 +45,7 @@ open class AccountTransaction(
) { ) {
// for object deserializers // for object deserializers
internal constructor() : this(AccountData(), Money(BigDecimal.ZERO, ""), false, "", DateTime.EPOCH.date, null, null, null, null, DateTime.EPOCH.date, 0, null, null, null, internal constructor() : this(AccountData(), Money(Amount.Zero, ""), false, "", DateTime.EPOCH.date, null, null, null, null, DateTime.EPOCH.date, 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)

View File

@ -0,0 +1,34 @@
package net.dankito.banking.fints.model
open class Amount(
val string: String
) {
companion object {
val Zero = Amount("0,")
}
internal constructor() : this("") // for object deserializers
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Amount) return false
if (string != other.string) return false
return true
}
override fun hashCode(): Int {
return string.hashCode()
}
override fun toString(): String {
return string
}
}

View File

@ -1,13 +1,11 @@
package net.dankito.banking.fints.model package net.dankito.banking.fints.model
import com.ionspin.kotlin.bignum.decimal.BigDecimal
open class BankTransferData( open class BankTransferData(
val creditorName: String, val creditorName: String,
val creditorIban: String, val creditorIban: String,
val creditorBic: String, val creditorBic: String,
val amount: BigDecimal, // TODO: use Money val amount: Money,
val usage: String, val usage: String,
val instantPayment: Boolean = false val instantPayment: Boolean = false
) )

View File

@ -1,16 +1,15 @@
package net.dankito.banking.fints.model package net.dankito.banking.fints.model
import com.ionspin.kotlin.bignum.decimal.BigDecimal
open class Money( open class Money(
val amount: BigDecimal, val amount: Amount,
val currency: Currency val currency: Currency
) { ) {
constructor(amount: BigDecimal, currencyCode: String) : this(amount, Currency(currencyCode))
internal constructor() : this(BigDecimal.ZERO, "") // for object deserializers constructor(amount: Amount, currencyCode: String) : this(amount, Currency(currencyCode))
internal constructor() : this(Amount.Zero, "") // for object deserializers
open val displayString: String open val displayString: String

View File

@ -1,7 +1,5 @@
package net.dankito.banking.fints.response package net.dankito.banking.fints.response
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.Time import com.soywiz.klock.Time
@ -19,6 +17,7 @@ import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.Kre
import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.account.KontoverbindungInternational import net.dankito.banking.fints.messages.datenelementgruppen.implementierte.account.KontoverbindungInternational
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.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.banking.fints.util.log.LoggerFactory import net.dankito.banking.fints.util.log.LoggerFactory
@ -618,7 +617,7 @@ open class ResponseParser(
val parsedBalance = parseBalance(dataElementGroup) val parsedBalance = parseBalance(dataElementGroup)
if (BigDecimal.ZERO.equals(parsedBalance.amount)) { if (Amount.Zero.equals(parsedBalance.amount)) {
return null return null
} }
@ -830,16 +829,14 @@ open class ResponseParser(
return null return null
} }
protected open fun parseAmount(amountString: String, isPositive: Boolean = true): BigDecimal { protected open fun parseAmount(amountString: String, isPositive: Boolean = true): Amount {
val adjustedAmountString = amountString.replace(',', '.') // Hbci amount format uses comma instead dot as decimal separator var adjustedAmountString = amountString // Hbci amount format uses comma instead dot as decimal separator
val amount = adjustedAmountString.toBigDecimal()
if (isPositive == false) { if (isPositive == false) {
return amount.negate() adjustedAmountString = "-" + adjustedAmountString
} }
return amount return Amount(adjustedAmountString)
} }
protected open fun parseNullableDateTime(dataElementGroup: String): DateTime? { protected open fun parseNullableDateTime(dataElementGroup: String): DateTime? {

View File

@ -1,6 +1,5 @@
package net.dankito.banking.fints.response.client package net.dankito.banking.fints.response.client
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import net.dankito.banking.fints.model.* import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.response.Response import net.dankito.banking.fints.response.Response
@ -15,4 +14,4 @@ open class AddAccountResponse(
val balances: Map<AccountData, Money> = mapOf() val balances: Map<AccountData, Money> = mapOf()
) )
: GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days, : GetTransactionsResponse(response, bookedTransactionsOfLast90Days, unbookedTransactionsOfLast90Days,
Money(balances.values.fold(BigDecimal.ZERO) { acc, e -> acc + e.amount }, balances.values.firstOrNull()?.currency?.code ?: "EUR")) Money(Amount.Zero, balances.values.firstOrNull()?.currency?.code ?: "EUR")) // TODO: sum balances

View File

@ -1,12 +1,12 @@
package net.dankito.banking.fints.response.segments package net.dankito.banking.fints.response.segments
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import com.soywiz.klock.Time import com.soywiz.klock.Time
import net.dankito.banking.fints.model.Amount
open class Balance( open class Balance(
val amount: BigDecimal, val amount: Amount,
val date: Date, val date: Date,
val time: Time? val time: Time?
) { ) {

View File

@ -1,15 +1,15 @@
package net.dankito.banking.fints.response.segments package net.dankito.banking.fints.response.segments
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import net.dankito.banking.fints.model.Amount
open class BalanceSegment( open class BalanceSegment(
val balance: BigDecimal, val balance: Amount,
val currency: String, val currency: String,
val date: Date, val date: Date,
val accountProductName: String, val accountProductName: String,
val balanceOfPreBookedTransactions: BigDecimal?, val balanceOfPreBookedTransactions: Amount?,
segmentString: String segmentString: String
) )
: ReceivedSegment(segmentString) : ReceivedSegment(segmentString)

View File

@ -1,8 +1,8 @@
package net.dankito.banking.fints.transactions package net.dankito.banking.fints.transactions
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import net.dankito.banking.fints.model.AccountData import net.dankito.banking.fints.model.AccountData
import net.dankito.banking.fints.model.AccountTransaction import net.dankito.banking.fints.model.AccountTransaction
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.model.Money import net.dankito.banking.fints.model.Money
import net.dankito.banking.fints.transactions.mt940.IMt940Parser import net.dankito.banking.fints.transactions.mt940.IMt940Parser
import net.dankito.banking.fints.transactions.mt940.Mt940Parser import net.dankito.banking.fints.transactions.mt940.Mt940Parser
@ -91,23 +91,23 @@ open class Mt940AccountTransactionsParser(
/** /**
* In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit. * In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit.
*/ */
protected open fun mapAmount(balance: Balance): BigDecimal { protected open fun mapAmount(balance: Balance): Amount {
return mapAmount(balance.amount, balance.isCredit) return mapAmount(balance.amount, balance.isCredit)
} }
/** /**
* In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit. * In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit.
*/ */
protected open fun mapAmount(statementLine: StatementLine): BigDecimal { protected open fun mapAmount(statementLine: StatementLine): Amount {
return mapAmount(statementLine.amount, statementLine.isCredit) return mapAmount(statementLine.amount, statementLine.isCredit)
} }
/** /**
* In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit. * In MT940 amounts are always stated as a positive number and flag isCredit decides if it's a credit or debit.
*/ */
protected open fun mapAmount(positiveAmount: BigDecimal, isCredit: Boolean): BigDecimal { protected open fun mapAmount(positiveAmount: Amount, isCredit: Boolean): Amount {
if (isCredit == false) { if (isCredit == false && positiveAmount.string.startsWith('-') == false) {
return positiveAmount.negate() return Amount("-" + positiveAmount)
} }
return positiveAmount return positiveAmount

View File

@ -1,8 +1,7 @@
package net.dankito.banking.fints.transactions.mt940 package net.dankito.banking.fints.transactions.mt940
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
import com.soywiz.klock.* import com.soywiz.klock.*
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.transactions.mt940.model.* import net.dankito.banking.fints.transactions.mt940.model.*
import net.dankito.banking.fints.util.log.LoggerFactory import net.dankito.banking.fints.util.log.LoggerFactory
@ -460,8 +459,8 @@ open class Mt940Parser : IMt940Parser {
return bookingDate return bookingDate
} }
protected open fun parseAmount(amountString: String): BigDecimal { protected open fun parseAmount(amountString: String): Amount {
return amountString.replace(',', '.').toBigDecimal() return Amount(amountString)
} }
} }

View File

@ -1,8 +1,8 @@
package net.dankito.banking.fints.transactions.mt940.model package net.dankito.banking.fints.transactions.mt940.model
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import net.dankito.banking.fints.model.Amount
open class Balance( open class Balance(
@ -38,11 +38,11 @@ open class Balance(
* *
* Max Length = 15 * Max Length = 15
*/ */
val amount: BigDecimal val amount: Amount
) { ) {
internal constructor() : this(false, false, DateTime.EPOCH.date, "", BigDecimal.ZERO) // for object deserializers internal constructor() : this(false, false, DateTime.EPOCH.date, "", Amount.Zero) // for object deserializers
override fun toString(): String { override fun toString(): String {

View File

@ -1,7 +1,7 @@
package net.dankito.banking.fints.transactions.mt940.model package net.dankito.banking.fints.transactions.mt940.model
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import net.dankito.banking.fints.model.Amount
open class StatementLine( open class StatementLine(
@ -48,7 +48,7 @@ open class StatementLine(
* *
* Max length = 15 * Max length = 15
*/ */
val amount: BigDecimal, val amount: Amount,
/** /**
* in Kontowährung * in Kontowährung

View File

@ -1,7 +1,6 @@
package net.dankito.banking.fints package net.dankito.banking.fints
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.soywiz.klock.Date import com.soywiz.klock.Date
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Datum
import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen import net.dankito.banking.fints.messages.datenelemente.abgeleiteteformate.Laenderkennzeichen
@ -68,10 +67,6 @@ abstract class FinTsTestBase {
return uuid4().toString().replace("-", "") return uuid4().toString().replace("-", "")
} }
protected open fun convertAmount(amount: BigDecimal): String {
return amount.toStringExpanded().replace('.', ',')
}
protected open fun convertDate(date: Date): String { protected open fun convertDate(date: Date): String {
return Datum.format(date) return Datum.format(date)
} }

View File

@ -2,11 +2,8 @@ package net.dankito.banking.fints.messages.segmente.implementierte.sepa
import ch.tutteli.atrium.api.fluent.en_GB.contains import ch.tutteli.atrium.api.fluent.en_GB.contains
import ch.tutteli.atrium.api.verbs.expect import ch.tutteli.atrium.api.verbs.expect
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId import net.dankito.banking.fints.messages.segmente.id.CustomerSegmentId
import net.dankito.banking.fints.model.AccountData import net.dankito.banking.fints.model.*
import net.dankito.banking.fints.model.BankTransferData
import net.dankito.banking.fints.model.CustomerData
import kotlin.test.Test import kotlin.test.Test
@ -21,7 +18,7 @@ class SepaBankTransferBaseTest {
val creditorName = "Mahatma Gandhi" val creditorName = "Mahatma Gandhi"
val creditorIban = "IN123456780987654321" val creditorIban = "IN123456780987654321"
val creditorBic = "ABCDINEFXXX" val creditorBic = "ABCDINEFXXX"
val amount = 1234.56.toBigDecimal() val amount = Amount("1234,56")
val usage = "What should Mahatma Gandhi want with money?" val usage = "What should Mahatma Gandhi want with money?"
} }
@ -36,7 +33,7 @@ class SepaBankTransferBaseTest {
CustomerData("", "", "", debitorName), CustomerData("", "", "", debitorName),
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()), AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
debitorBic, debitorBic,
BankTransferData(creditorName, creditorIban, creditorBic, amount, usage) BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
) )
@ -46,7 +43,7 @@ class SepaBankTransferBaseTest {
// then // then
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic, expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic,
amount.toString(), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03") amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.001.03")
} }
@Test @Test
@ -58,7 +55,7 @@ class SepaBankTransferBaseTest {
CustomerData("", "", "", debitorName), CustomerData("", "", "", debitorName),
AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()), AccountData("", null, 0, "", debitorIban, "", null, null, "", null, null, listOf()),
debitorBic, debitorBic,
BankTransferData(creditorName, creditorIban, creditorBic, amount, usage) BankTransferData(creditorName, creditorIban, creditorBic, Money(amount, "EUR"), usage)
) )
@ -68,7 +65,7 @@ class SepaBankTransferBaseTest {
// then // then
expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic, expect(result).contains(debitorName, debitorIban, debitorBic, creditorName, creditorIban, creditorBic,
amount.toString(), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03") amount.toString().replace(',', '.'), usage, "urn?:iso?:std?:iso?:20022?:tech?:xsd?:pain.001.003.03")
} }
} }

View File

@ -13,10 +13,9 @@ import net.dankito.banking.fints.messages.segmente.id.ISegmentId
import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId import net.dankito.banking.fints.messages.segmente.id.MessageSegmentId
import net.dankito.banking.fints.response.segments.* import net.dankito.banking.fints.response.segments.*
import ch.tutteli.atrium.api.verbs.expect import ch.tutteli.atrium.api.verbs.expect
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
import net.dankito.banking.fints.extensions.isFalse import net.dankito.banking.fints.extensions.isFalse
import net.dankito.banking.fints.extensions.isTrue import net.dankito.banking.fints.extensions.isTrue
import net.dankito.banking.fints.model.Amount
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.fail import kotlin.test.fail
@ -965,7 +964,7 @@ class ResponseParserTest : FinTsTestBase() {
fun parseBalance() { fun parseBalance() {
// given // given
val balance = 1234.56.toBigDecimal() val balance = "1234,56"
val date = com.soywiz.klock.Date(1988, 3, 27) val date = com.soywiz.klock.Date(1988, 3, 27)
val bankCode = "12345678" val bankCode = "12345678"
val accountId = "0987654321" val accountId = "0987654321"
@ -973,13 +972,13 @@ class ResponseParserTest : FinTsTestBase() {
// when // when
val result = underTest.parse("HISAL:8:5:3+$accountId::280:$bankCode+$accountProductName+EUR+" + val result = underTest.parse("HISAL:8:5:3+$accountId::280:$bankCode+$accountProductName+EUR+" +
"C:${convertAmount(balance)}:EUR:${convertDate(date)}+C:0,:EUR:20191006++${convertAmount(balance)}:EUR") "C:$balance:EUR:${convertDate(date)}+C:0,:EUR:20191006++$balance:EUR")
// then // then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 8, 5, 3) assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 8, 5, 3)
result.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let { segment -> result.getFirstSegmentById<BalanceSegment>(InstituteSegmentId.Balance)?.let { segment ->
expect(segment.balance).toBe(balance) expect(segment.balance).toBe(Amount(balance))
expect(segment.currency).toBe("EUR") expect(segment.currency).toBe("EUR")
expect(segment.date).toBe(date) expect(segment.date).toBe(date)
expect(segment.accountProductName).toBe(accountProductName) expect(segment.accountProductName).toBe(accountProductName)
@ -992,7 +991,7 @@ class ResponseParserTest : FinTsTestBase() {
fun parseBalance_BalanceOfPreBookedTransactionsIsZero() { fun parseBalance_BalanceOfPreBookedTransactionsIsZero() {
// given // given
val balance = BigDecimal.ZERO val balance = Amount.Zero
val date = com.soywiz.klock.Date(2020, 6, 11) val date = com.soywiz.klock.Date(2020, 6, 11)
val bankCode = "12345678" val bankCode = "12345678"
val accountId = "0987654321" val accountId = "0987654321"
@ -1000,7 +999,7 @@ class ResponseParserTest : FinTsTestBase() {
// when // when
// "HISAL:7:5:3+0987654321:Girokonto:280:12345678+Girokonto+EUR+C:0,:EUR:20200511:204204'" // "HISAL:7:5:3+0987654321:Girokonto:280:12345678+Girokonto+EUR+C:0,:EUR:20200511:204204'"
val result = underTest.parse("HISAL:7:5:3+$accountId:$accountProductName:280:$bankCode+$accountProductName+EUR+C:0,:EUR:${convertDate(date)}:204204'") val result = underTest.parse("HISAL:7:5:3+$accountId:$accountProductName:280:$bankCode+$accountProductName+EUR+C:$balance:EUR:${convertDate(date)}:204204'")
// then // then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 7, 5, 3) assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 7, 5, 3)
@ -1019,7 +1018,7 @@ class ResponseParserTest : FinTsTestBase() {
fun parseBalance_BalanceOfPreBookedTransactionsIsEmpty() { fun parseBalance_BalanceOfPreBookedTransactionsIsEmpty() {
// given // given
val balance = BigDecimal.ZERO val balance = Amount.Zero
val date = com.soywiz.klock.Date(2020, 6, 11) val date = com.soywiz.klock.Date(2020, 6, 11)
val bankCode = "12345678" val bankCode = "12345678"
val accountId = "0987654321" val accountId = "0987654321"
@ -1027,7 +1026,7 @@ class ResponseParserTest : FinTsTestBase() {
// when // when
// "HISAL:7:5:3+0987654321:Girokonto:280:12345678+Girokonto+EUR++0,:EUR'" // "HISAL:7:5:3+0987654321:Girokonto:280:12345678+Girokonto+EUR++0,:EUR'"
val result = underTest.parse("HISAL:7:5:3+$accountId:$accountProductName:280:$bankCode+$accountProductName+EUR+C:0,:EUR:${convertDate(date)}:204204++0,:EUR'") val result = underTest.parse("HISAL:7:5:3+$accountId:$accountProductName:280:$bankCode+$accountProductName+EUR+C:0,:EUR:${convertDate(date)}:204204++$balance:EUR'")
// then // then
assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 7, 5, 3) assertSuccessfullyParsedSegment(result, InstituteSegmentId.Balance, 7, 5, 3)

View File

@ -9,12 +9,11 @@ import net.dankito.banking.fints.transactions.mt940.model.Balance
import net.dankito.banking.fints.transactions.mt940.model.InformationToAccountOwner import net.dankito.banking.fints.transactions.mt940.model.InformationToAccountOwner
import net.dankito.banking.fints.transactions.mt940.model.StatementLine import net.dankito.banking.fints.transactions.mt940.model.StatementLine
import ch.tutteli.atrium.api.verbs.expect import ch.tutteli.atrium.api.verbs.expect
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
import kotlin.test.Test import kotlin.test.Test
import com.soywiz.klock.Date import com.soywiz.klock.Date
import com.soywiz.klock.DateFormat import com.soywiz.klock.DateFormat
import net.dankito.banking.fints.extensions.isFalse import net.dankito.banking.fints.extensions.isFalse
import net.dankito.banking.fints.model.Amount
class Mt940ParserTest : FinTsTestBase() { class Mt940ParserTest : FinTsTestBase() {
@ -26,20 +25,20 @@ class Mt940ParserTest : FinTsTestBase() {
val AccountStatement1PreviousStatementBookingDate = Date(1988, 2, 26) val AccountStatement1PreviousStatementBookingDate = Date(1988, 2, 26)
val AccountStatement1BookingDate = Date(1988, 2, 27) val AccountStatement1BookingDate = Date(1988, 2, 27)
val AccountStatement1OpeningBalanceAmount = 12345.67.toBigDecimal() val AccountStatement1OpeningBalanceAmount = Amount("12345,67")
val AccountStatement1Transaction1Amount = 1234.56.toBigDecimal() val AccountStatement1Transaction1Amount = Amount("1234,56")
val AccountStatement1Transaction1OtherPartyName = "Sender1" val AccountStatement1Transaction1OtherPartyName = "Sender1"
val AccountStatement1Transaction1OtherPartyBankCode = "AAAADE12" val AccountStatement1Transaction1OtherPartyBankCode = "AAAADE12"
val AccountStatement1Transaction1OtherPartyAccountId = "DE99876543210987654321" val AccountStatement1Transaction1OtherPartyAccountId = "DE99876543210987654321"
val AccountStatement1Transaction2Amount = 432.10.toBigDecimal() val AccountStatement1Transaction2Amount = Amount("432,10")
val AccountStatement1Transaction2OtherPartyName = "Receiver2" val AccountStatement1Transaction2OtherPartyName = "Receiver2"
val AccountStatement1Transaction2OtherPartyBankCode = "BBBBDE56" val AccountStatement1Transaction2OtherPartyBankCode = "BBBBDE56"
val AccountStatement1Transaction2OtherPartyAccountId = "DE77987654321234567890" val AccountStatement1Transaction2OtherPartyAccountId = "DE77987654321234567890"
val AccountStatement1ClosingBalanceAmount = AccountStatement1OpeningBalanceAmount + AccountStatement1Transaction1Amount val AccountStatement1ClosingBalanceAmount = Amount("13580,23")
val AccountStatement1With2TransactionsClosingBalanceAmount = AccountStatement1OpeningBalanceAmount + AccountStatement1Transaction1Amount - AccountStatement1Transaction2Amount val AccountStatement1With2TransactionsClosingBalanceAmount = Amount("13148,13")
} }
private val underTest = Mt940Parser() private val underTest = Mt940Parser()
@ -74,7 +73,7 @@ class Mt940ParserTest : FinTsTestBase() {
fun accountStatementWithSingleTransaction_SheetNumberOmitted() { fun accountStatementWithSingleTransaction_SheetNumberOmitted() {
// given // given
val amount = BigDecimal.parseString("15.00") val amount = Amount("15,00")
val isCredit = false val isCredit = false
val bookingDate = Date(2020, 5, 11) val bookingDate = Date(2020, 5, 11)
@ -92,7 +91,7 @@ class Mt940ParserTest : FinTsTestBase() {
expect(statement.statementNumber).toBe(0) expect(statement.statementNumber).toBe(0)
expect(statement.sequenceNumber).toBe(null) expect(statement.sequenceNumber).toBe(null)
assertBalance(statement.openingBalance, true, bookingDate, BigDecimal.parseString("0.00")) assertBalance(statement.openingBalance, true, bookingDate, Amount("0,00"))
assertBalance(statement.closingBalance, isCredit, bookingDate, amount) assertBalance(statement.closingBalance, isCredit, bookingDate, amount)
expect(statement.transactions).hasSize(1) expect(statement.transactions).hasSize(1)
@ -276,14 +275,14 @@ class Mt940ParserTest : FinTsTestBase() {
} }
private fun assertBalance(balance: Balance, isCredit: Boolean, bookingDate: Date, amount: BigDecimal) { private fun assertBalance(balance: Balance, isCredit: Boolean, bookingDate: Date, amount: Amount) {
expect(balance.isCredit).toBe(isCredit) expect(balance.isCredit).toBe(isCredit)
expect(balance.bookingDate).toBe(bookingDate) expect(balance.bookingDate).toBe(bookingDate)
expect(balance.amount).toBe(amount) expect(balance.amount).toBe(amount)
expect(balance.currency).toBe(Currency) expect(balance.currency).toBe(Currency)
} }
private fun assertTurnover(statementLine: StatementLine, valueDate: Date, amount: BigDecimal, isCredit: Boolean = true, private fun assertTurnover(statementLine: StatementLine, valueDate: Date, amount: Amount, isCredit: Boolean = true,
bookingDate: Date? = valueDate) { bookingDate: Date? = valueDate) {
expect(statementLine.isCredit).toBe(isCredit) expect(statementLine.isCredit).toBe(isCredit)
@ -308,12 +307,12 @@ class Mt940ParserTest : FinTsTestBase() {
:20:STARTUMSE :20:STARTUMSE
:25:$BankCode/$CustomerId :25:$BankCode/$CustomerId
:28C:00000/001 :28C:00000/001
:60F:C${convertMt940Date(AccountStatement1PreviousStatementBookingDate)}EUR${convertAmount(AccountStatement1OpeningBalanceAmount)} :60F:C${convertMt940Date(AccountStatement1PreviousStatementBookingDate)}EUR$AccountStatement1OpeningBalanceAmount
:61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}CR${convertAmount(AccountStatement1Transaction1Amount)}N062NONREF :61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}CR${AccountStatement1Transaction1Amount}N062NONREF
:86:166?00GUTSCHR. UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/ :86:166?00GUTSCHR. UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/
EUR ${convertAmount(AccountStatement1Transaction1Amount)}/20?2219-10-02/...?30$AccountStatement1Transaction1OtherPartyBankCode?31$AccountStatement1Transaction1OtherPartyAccountId EUR ${AccountStatement1Transaction1Amount}/20?2219-10-02/...?30$AccountStatement1Transaction1OtherPartyBankCode?31$AccountStatement1Transaction1OtherPartyAccountId
?32$AccountStatement1Transaction1OtherPartyName ?32$AccountStatement1Transaction1OtherPartyName
:62F:C${convertMt940Date(AccountStatement1BookingDate)}EUR${convertAmount(AccountStatement1ClosingBalanceAmount)} :62F:C${convertMt940Date(AccountStatement1BookingDate)}EUR$AccountStatement1ClosingBalanceAmount
- -
""".trimIndent() """.trimIndent()
@ -321,16 +320,16 @@ class Mt940ParserTest : FinTsTestBase() {
:20:STARTUMSE :20:STARTUMSE
:25:$BankCode/$CustomerId :25:$BankCode/$CustomerId
:28C:00000/001 :28C:00000/001
:60F:C${convertMt940Date(AccountStatement1PreviousStatementBookingDate)}EUR${convertAmount(AccountStatement1OpeningBalanceAmount)} :60F:C${convertMt940Date(AccountStatement1PreviousStatementBookingDate)}EUR$AccountStatement1OpeningBalanceAmount
:61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}CR${convertAmount(AccountStatement1Transaction1Amount)}N062NONREF :61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}CR${AccountStatement1Transaction1Amount}N062NONREF
:86:166?00GUTSCHR. UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/ :86:166?00GUTSCHR. UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/
EUR ${convertAmount(AccountStatement1Transaction1Amount)}/20?2219-10-02/...?30$AccountStatement1Transaction1OtherPartyBankCode?31$AccountStatement1Transaction1OtherPartyAccountId EUR ${AccountStatement1Transaction1Amount}/20?2219-10-02/...?30$AccountStatement1Transaction1OtherPartyBankCode?31$AccountStatement1Transaction1OtherPartyAccountId
?32$AccountStatement1Transaction1OtherPartyName ?32$AccountStatement1Transaction1OtherPartyName
:61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}DR${convertAmount(AccountStatement1Transaction2Amount)}N062NONREF :61:${convertMt940Date(AccountStatement1BookingDate)}${convertToShortBookingDate(AccountStatement1BookingDate)}DR${AccountStatement1Transaction2Amount}N062NONREF
:86:166?00ONLINE-UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/ :86:166?00ONLINE-UEBERWEISUNG?109249?20EREF+674?21SVWZ+1908301/
EUR ${convertAmount(AccountStatement1Transaction2Amount)}/20?2219-10-02/...?30$AccountStatement1Transaction2OtherPartyBankCode?31$AccountStatement1Transaction2OtherPartyAccountId EUR ${AccountStatement1Transaction2Amount}/20?2219-10-02/...?30$AccountStatement1Transaction2OtherPartyBankCode?31$AccountStatement1Transaction2OtherPartyAccountId
?32$AccountStatement1Transaction2OtherPartyName ?32$AccountStatement1Transaction2OtherPartyName
:62F:C${convertMt940Date(AccountStatement1BookingDate)}EUR${convertAmount(AccountStatement1With2TransactionsClosingBalanceAmount)} :62F:C${convertMt940Date(AccountStatement1BookingDate)}EUR${AccountStatement1With2TransactionsClosingBalanceAmount}
- -
""".trimIndent() """.trimIndent()

View File

@ -0,0 +1,22 @@
package net.dankito.banking.extensions
import net.dankito.banking.fints.model.Amount
import net.dankito.banking.fints.model.Money
import java.math.BigDecimal
fun BigDecimal.toAmount(): Amount {
return Amount(this.toString())
}
fun BigDecimal.toMoney(): Money {
return Money(this.toAmount(), "EUR")
}
fun Amount.toJavaBigDecimal(): BigDecimal {
return BigDecimal(this.string)
}
fun Money.toJavaBigDecimal(): BigDecimal {
return this.amount.toJavaBigDecimal()
}

View File

@ -1,24 +0,0 @@
package net.dankito.banking.extensions
import com.ionspin.kotlin.bignum.integer.Sign
import net.dankito.banking.fints.model.Money
import java.math.BigDecimal
fun BigDecimal.toIonspinBigDecimal(): com.ionspin.kotlin.bignum.decimal.BigDecimal {
return com.ionspin.kotlin.bignum.decimal.BigDecimal.parseString(this.toString()) // TODO: couldn't figure out how to create BigDecimal from unscaledValue and scale
}
fun com.ionspin.kotlin.bignum.decimal.BigDecimal.toJavaBigDecimal(): BigDecimal {
val converted = BigDecimal.valueOf(this.significand.longValue(), (this.precision - this.exponent - 1).toInt())
if (this.significand.sign == Sign.NEGATIVE) {
return converted.negate()
}
return converted
}
fun Money.toJavaBigDecimal(): BigDecimal {
return this.amount.toJavaBigDecimal()
}

View File

@ -1,7 +1,5 @@
package net.dankito.banking package net.dankito.banking
import net.dankito.banking.extensions.toIonspinBigDecimal
import net.dankito.banking.extensions.toJavaBigDecimal
import com.soywiz.klock.jvm.toDate import com.soywiz.klock.jvm.toDate
import net.dankito.banking.extensions.toKlockDate import net.dankito.banking.extensions.toKlockDate
import net.dankito.banking.ui.BankingClientCallback import net.dankito.banking.ui.BankingClientCallback
@ -25,8 +23,11 @@ import net.dankito.utils.serialization.JacksonJsonSerializer
import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.fints.webclient.KtorWebClient import net.dankito.banking.fints.webclient.KtorWebClient
import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.extensions.toAmount
import net.dankito.banking.extensions.toMoney
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import java.math.BigDecimal
open class fints4kBankingClient( open class fints4kBankingClient(
@ -133,7 +134,7 @@ open class fints4kBankingClient(
callback(BankingClientResponse(false, "Cannot find account for ${bankAccount.identifier}")) // TODO: translate callback(BankingClientResponse(false, "Cannot find account for ${bankAccount.identifier}")) // TODO: translate
} }
else { else {
val mappedData = BankTransferData(data.creditorName, data.creditorIban, data.creditorBic, data.amount.toIonspinBigDecimal(), data.usage, data.instantPayment) val mappedData = BankTransferData(data.creditorName, data.creditorIban, data.creditorBic, data.amount.toMoney(), data.usage, data.instantPayment)
client.doBankTransferAsync(mappedData, account) { response -> client.doBankTransferAsync(mappedData, account) { response ->
saveData() saveData()