Implemented mapping Holdings
This commit is contained in:
parent
c5b7967ce1
commit
5187e34797
|
@ -14,5 +14,8 @@ value class Amount(val amount: String = "0") {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(amount: Double) : this(amount.toString())
|
||||||
|
|
||||||
|
|
||||||
override fun toString() = amount
|
override fun toString() = amount
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package net.codinux.banking.client.model
|
||||||
import kotlinx.datetime.*
|
import kotlinx.datetime.*
|
||||||
import net.codinux.banking.client.model.config.JsonIgnore
|
import net.codinux.banking.client.model.config.JsonIgnore
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
|
|
||||||
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
|
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
|
||||||
@NoArgConstructor
|
@NoArgConstructor
|
||||||
|
@ -28,6 +29,7 @@ open class BankAccount(
|
||||||
|
|
||||||
open val bookedTransactions: MutableList<AccountTransaction> = mutableListOf(),
|
open val bookedTransactions: MutableList<AccountTransaction> = mutableListOf(),
|
||||||
open val prebookedTransactions: MutableList<PrebookedAccountTransaction> = mutableListOf(),
|
open val prebookedTransactions: MutableList<PrebookedAccountTransaction> = mutableListOf(),
|
||||||
|
open val holdings: List<Holding> = emptyList(),
|
||||||
|
|
||||||
var userSetDisplayName: String? = null,
|
var userSetDisplayName: String? = null,
|
||||||
var displayIndex: Int = 0,
|
var displayIndex: Int = 0,
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package net.codinux.banking.client.model.extensions
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
|
||||||
|
// TODO: really map to BigDecimal
|
||||||
|
fun Amount.toBigDecimal(): Double = this.amount.toDouble()
|
|
@ -7,6 +7,7 @@ import net.codinux.banking.client.model.Amount
|
||||||
import net.codinux.banking.client.model.BankAccount
|
import net.codinux.banking.client.model.BankAccount
|
||||||
import net.codinux.banking.client.model.PrebookedAccountTransaction
|
import net.codinux.banking.client.model.PrebookedAccountTransaction
|
||||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
import net.codinux.banking.client.model.securitiesaccount.Holding
|
||||||
|
|
||||||
@NoArgConstructor
|
@NoArgConstructor
|
||||||
open class GetTransactionsResponse(
|
open class GetTransactionsResponse(
|
||||||
|
@ -14,6 +15,7 @@ open class GetTransactionsResponse(
|
||||||
val balance: Amount? = null,
|
val balance: Amount? = null,
|
||||||
val bookedTransactions: List<AccountTransaction>,
|
val bookedTransactions: List<AccountTransaction>,
|
||||||
val prebookedTransactions: List<PrebookedAccountTransaction>,
|
val prebookedTransactions: List<PrebookedAccountTransaction>,
|
||||||
|
val holdings: List<Holding> = emptyList(),
|
||||||
val transactionsRetrievalTime: Instant,
|
val transactionsRetrievalTime: Instant,
|
||||||
val retrievedTransactionsFrom: LocalDate? = null,
|
val retrievedTransactionsFrom: LocalDate? = null,
|
||||||
val retrievedTransactionsTo: LocalDate? = null
|
val retrievedTransactionsTo: LocalDate? = null
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package net.codinux.banking.client.model.securitiesaccount
|
||||||
|
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@NoArgConstructor
|
||||||
|
open class Holding(
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
val isin: String? = null,
|
||||||
|
val wkn: String? = null,
|
||||||
|
|
||||||
|
val quantity: Int? = null,
|
||||||
|
val currency: String? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gesamter Kurswert aller Einheiten des Wertpapiers
|
||||||
|
*/
|
||||||
|
val totalBalance: Amount? = null,
|
||||||
|
/**
|
||||||
|
* Aktueller Kurswert einer einzelnen Einheit des Wertpapiers
|
||||||
|
*/
|
||||||
|
val marketValue: Amount? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Änderung in Prozent Aktueller Kurswert gegenüber Einstandspreis.
|
||||||
|
*/
|
||||||
|
val performancePercentage: Float? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gesamter Einstandspreis (Kaufpreis)
|
||||||
|
*/
|
||||||
|
val totalCostPrice: Amount? = null,
|
||||||
|
/**
|
||||||
|
* (Durchschnittlicher) Einstandspreis/-kurs einer Einheit des Wertpapiers
|
||||||
|
*/
|
||||||
|
val averageCostPrice: Amount? = null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeitpunkt zu dem der Kurswert bestimmt wurde
|
||||||
|
*/
|
||||||
|
val pricingTime: Instant? = null,
|
||||||
|
|
||||||
|
val buyingDate: LocalDate? = null,
|
||||||
|
) {
|
||||||
|
override fun toString() = "$name $totalBalance $currency"
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
package net.codinux.banking.client.fints4k
|
package net.codinux.banking.client.fints4k
|
||||||
|
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.datetime.Instant
|
||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import net.codinux.banking.client.model.*
|
import net.codinux.banking.client.model.*
|
||||||
import net.codinux.banking.client.model.AccountTransaction
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
import net.codinux.banking.client.model.Amount
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import net.codinux.banking.client.model.extensions.toBigDecimal
|
||||||
import net.codinux.banking.client.model.tan.*
|
import net.codinux.banking.client.model.tan.*
|
||||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||||
|
@ -24,6 +26,7 @@ import net.dankito.banking.client.model.response.ErrorCode
|
||||||
import net.codinux.banking.fints.mapper.FinTsModelMapper
|
import net.codinux.banking.fints.mapper.FinTsModelMapper
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.signatur.Sicherheitsfunktion
|
||||||
import net.codinux.banking.fints.model.*
|
import net.codinux.banking.fints.model.*
|
||||||
|
import net.codinux.banking.fints.transactions.swift.model.Holding
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMedium
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.MobilePhoneTanMedium
|
||||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
||||||
|
@ -111,7 +114,8 @@ open class FinTs4kMapper {
|
||||||
account.retrievedTransactionsFrom = finTsBankAccount.retrievedTransactionsFrom
|
account.retrievedTransactionsFrom = finTsBankAccount.retrievedTransactionsFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.success(GetTransactionsResponse(account, mapAmount(finTsBankAccount.balance), mapBookedTransactions(finTsBankAccount), emptyList(),
|
Response.success(GetTransactionsResponse(account, mapMoney(finTsBankAccount.balance), mapBookedTransactions(finTsBankAccount), emptyList(),
|
||||||
|
mapHoldings(finTsBankAccount.statementOfHoldings, finTsBankAccount.currency, finTsBankAccount.lastTransactionsRetrievalTime),
|
||||||
finTsBankAccount.lastTransactionsRetrievalTime ?: Clock.System.now(), param.retrieveTransactionsFrom, param.retrieveTransactionsTo))
|
finTsBankAccount.lastTransactionsRetrievalTime ?: Clock.System.now(), param.retrieveTransactionsFrom, param.retrieveTransactionsTo))
|
||||||
} else {
|
} else {
|
||||||
mapError(getAccountDataResponse)
|
mapError(getAccountDataResponse)
|
||||||
|
@ -156,10 +160,11 @@ open class FinTs4kMapper {
|
||||||
account.identifier, account.subAccountNumber, account.iban, account.productName, account.accountHolderName,
|
account.identifier, account.subAccountNumber, account.iban, account.productName, account.accountHolderName,
|
||||||
mapAccountType(account.type), account.currency, account.accountLimit,
|
mapAccountType(account.type), account.currency, account.accountLimit,
|
||||||
account.isAccountTypeSupportedByApplication, mapFeatures(account),
|
account.isAccountTypeSupportedByApplication, mapFeatures(account),
|
||||||
mapAmount(account.balance),
|
mapMoney(account.balance),
|
||||||
account.serverTransactionsRetentionDays,
|
account.serverTransactionsRetentionDays,
|
||||||
account.lastTransactionsRetrievalTime, account.retrievedTransactionsFrom,
|
account.lastTransactionsRetrievalTime, account.retrievedTransactionsFrom,
|
||||||
bookedTransactions = mapBookedTransactions(account).toMutableList()
|
bookedTransactions = mapBookedTransactions(account).toMutableList(),
|
||||||
|
holdings = mapHoldings(account.statementOfHoldings, account.currency, account.lastTransactionsRetrievalTime)
|
||||||
)
|
)
|
||||||
|
|
||||||
protected open fun mapAccountType(type: net.dankito.banking.client.model.BankAccountType): BankAccountType =
|
protected open fun mapAccountType(type: net.dankito.banking.client.model.BankAccountType): BankAccountType =
|
||||||
|
@ -185,12 +190,12 @@ open class FinTs4kMapper {
|
||||||
account.bookedTransactions.map { mapTransaction(it) }
|
account.bookedTransactions.map { mapTransaction(it) }
|
||||||
|
|
||||||
protected open fun mapTransaction(transaction: net.dankito.banking.client.model.AccountTransaction): AccountTransaction = AccountTransaction(
|
protected open fun mapTransaction(transaction: net.dankito.banking.client.model.AccountTransaction): AccountTransaction = AccountTransaction(
|
||||||
mapAmount(transaction.amount), transaction.amount.currency.code, transaction.reference,
|
mapMoney(transaction.amount), transaction.amount.currency.code, transaction.reference,
|
||||||
transaction.bookingDate, transaction.valueDate,
|
transaction.bookingDate, transaction.valueDate,
|
||||||
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
|
transaction.otherPartyName, transaction.otherPartyBankId, transaction.otherPartyAccountId,
|
||||||
|
|
||||||
transaction.postingText,
|
transaction.postingText,
|
||||||
mapNullableAmount(transaction.openingBalance), mapNullableAmount(transaction.closingBalance),
|
mapNullableMoney(transaction.openingBalance), mapNullableMoney(transaction.closingBalance),
|
||||||
|
|
||||||
transaction.statementNumber, transaction.sheetNumber,
|
transaction.statementNumber, transaction.sheetNumber,
|
||||||
|
|
||||||
|
@ -209,9 +214,74 @@ open class FinTs4kMapper {
|
||||||
transaction.isReversal
|
transaction.isReversal
|
||||||
)
|
)
|
||||||
|
|
||||||
protected open fun mapNullableAmount(amount: Money?) = amount?.let { mapAmount(it) }
|
protected open fun mapHoldings(statements: List<net.codinux.banking.fints.transactions.swift.model.StatementOfHoldings>, accountCurrency: String, lastAccountUpdateTime: Instant? = null) =
|
||||||
|
statements.flatMap { mapHoldings(it, accountCurrency, lastAccountUpdateTime) }
|
||||||
|
|
||||||
protected open fun mapAmount(amount: Money) = Amount.fromString(amount.amount.string.replace(',', '.'))
|
protected open fun mapHoldings(statement: net.codinux.banking.fints.transactions.swift.model.StatementOfHoldings, accountCurrency: String, lastAccountUpdateTime: Instant? = null): List<net.codinux.banking.client.model.securitiesaccount.Holding> {
|
||||||
|
|
||||||
|
val totalBalance = mapNullableAmount(statement.totalBalance)
|
||||||
|
val currency = statement.currency ?: accountCurrency
|
||||||
|
val statementDate: Instant? = /* statement.statementDate ?: statement.preparationDate ?: */ lastAccountUpdateTime // TODO
|
||||||
|
|
||||||
|
return statement.holdings.map { mapHolding(it, currency, statementDate, if (statement.holdings.size == 1) totalBalance else null) }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun mapHolding(holding: Holding, accountCurrency: String, statementDate: Instant?, totalBalance: Amount? = null) = net.codinux.banking.client.model.securitiesaccount.Holding(
|
||||||
|
holding.name, holding.isin, holding.wkn,
|
||||||
|
|
||||||
|
holding.quantity, holding.currency ?: accountCurrency,
|
||||||
|
|
||||||
|
getTotalBalance(holding), mapNullableAmount(holding.marketValue),
|
||||||
|
calculatePerformance(holding),
|
||||||
|
getTotalCostPrice(holding), mapNullableAmount(holding.averageCostPrice),
|
||||||
|
|
||||||
|
holding.pricingTime ?: statementDate, holding.buyingDate
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getTotalBalance(holding: Holding): Amount? {
|
||||||
|
return if (holding.totalBalance != null) {
|
||||||
|
mapNullableAmount(holding.totalBalance)
|
||||||
|
} else if (holding.quantity != null && holding.marketValue != null) {
|
||||||
|
Amount(holding.quantity!! * mapAmount(holding.marketValue!!).toBigDecimal())
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTotalCostPrice(holding: Holding): Amount? {
|
||||||
|
return if (holding.totalCostPrice != null) {
|
||||||
|
mapNullableAmount(holding.totalCostPrice)
|
||||||
|
} else if (holding.quantity != null && holding.averageCostPrice != null) {
|
||||||
|
Amount(holding.quantity!! * mapAmount(holding.averageCostPrice!!).toBigDecimal())
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculatePerformance(holding: Holding): Float? {
|
||||||
|
val totalBalance = getTotalBalance(holding)
|
||||||
|
val totalCostPrice = getTotalCostPrice(holding)
|
||||||
|
|
||||||
|
if (totalBalance != null && totalCostPrice != null) {
|
||||||
|
return ((totalBalance.toBigDecimal() - totalCostPrice.toBigDecimal()) / totalCostPrice.toBigDecimal() * 100).toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
val marketValue = mapNullableAmount(holding.marketValue)
|
||||||
|
val costPrice = mapNullableAmount(holding.averageCostPrice)
|
||||||
|
if (marketValue != null && costPrice != null) {
|
||||||
|
return ((marketValue.toBigDecimal() - costPrice.toBigDecimal()) / costPrice.toBigDecimal() * 100).toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun mapNullableMoney(amount: Money?) = amount?.let { mapMoney(it) }
|
||||||
|
|
||||||
|
protected open fun mapMoney(amount: Money) = Amount.fromString(amount.amount.string.replace(',', '.'))
|
||||||
|
|
||||||
|
protected open fun mapNullableAmount(amount: net.codinux.banking.fints.model.Amount?) = amount?.let { mapAmount(it) }
|
||||||
|
|
||||||
|
protected open fun mapAmount(amount: net.codinux.banking.fints.model.Amount) = Amount.fromString(amount.string.replace(',', '.'))
|
||||||
|
|
||||||
|
|
||||||
open fun mapTanChallenge(challenge: net.codinux.banking.fints.model.TanChallenge): TanChallenge {
|
open fun mapTanChallenge(challenge: net.codinux.banking.fints.model.TanChallenge): TanChallenge {
|
||||||
|
|
Loading…
Reference in New Issue