Compare commits

..

13 Commits

19 changed files with 267 additions and 96 deletions

View File

@ -41,7 +41,7 @@ interface BankingClient {
*
* Optionally specify which [accounts] should be updated. If not specified all accounts will be updated.
*/
suspend fun updateAccountTransactionsAsync(user: User, accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
suspend fun updateAccountTransactionsAsync(bank: BankAccess, accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
suspend fun transferMoneyAsync(bankCode: String, loginName: String, password: String, recipientName: String,

View File

@ -3,7 +3,7 @@ package net.codinux.banking.client
import net.codinux.banking.client.model.AccountCredentials
import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.BankAccount
import net.codinux.banking.client.model.User
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.options.GetAccountDataOptions
import net.codinux.banking.client.model.request.GetAccountDataRequest
import net.codinux.banking.client.model.request.TransferMoneyRequest
@ -16,23 +16,23 @@ abstract class BankingClientForUserBase(
protected val client: BankingClient
) : BankingClientForUser {
private lateinit var user: User
private lateinit var bank: BankAccess
override suspend fun getAccountDataAsync(options: GetAccountDataOptions) =
client.getAccountDataAsync(GetAccountDataRequest(credentials, options)).also {
it.data?.user?.let { retrievedUser ->
this.user = retrievedUser
it.data?.bank?.let { retrievedBank ->
this.bank = retrievedBank
}
}
override suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> =
client.updateAccountTransactionsAsync(user, accounts)
client.updateAccountTransactionsAsync(bank, accounts)
override suspend fun transferMoneyAsync(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String?) =
transferMoneyAsync(TransferMoneyRequest(null, recipientName, recipientAccountIdentifier, null, amount, paymentReference = paymentReference))
override suspend fun transferMoneyAsync(request: TransferMoneyRequest) =
client.transferMoneyAsync(TransferMoneyRequestForUser(user.bankCode, user.loginName, user.password!!, request))
client.transferMoneyAsync(TransferMoneyRequestForUser(bank.domesticBankCode, bank.loginName, bank.password!!, request))
}

View File

@ -3,7 +3,7 @@ package net.codinux.banking.client
import kotlinx.coroutines.runBlocking
import net.codinux.banking.client.model.Amount
import net.codinux.banking.client.model.BankAccount
import net.codinux.banking.client.model.User
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.options.GetAccountDataOptions
import net.codinux.banking.client.model.request.GetAccountDataRequest
import net.codinux.banking.client.model.request.TransferMoneyRequest
@ -19,8 +19,8 @@ fun BankingClient.getAccountData(request: GetAccountDataRequest) = runBlocking {
getAccountDataAsync(request)
}
fun BankingClient.updateAccountTransactions(user: User, accounts: List<BankAccount>? = null) = runBlocking {
updateAccountTransactionsAsync(user, accounts)
fun BankingClient.updateAccountTransactions(bank: BankAccess, accounts: List<BankAccount>? = null) = runBlocking {
updateAccountTransactionsAsync(bank, accounts)
}

View File

@ -2,28 +2,42 @@ package net.codinux.banking.client.model
import platform.Foundation.NSDecimalNumber
import net.codinux.banking.client.model.config.NoArgConstructor
import platform.Foundation.NSDecimalNumberHandler
import platform.Foundation.NSRoundingMode
@NoArgConstructor
actual class Amount actual constructor(amount: String) : NSDecimalNumber(string = amount) {
actual class Amount actual constructor(amount: String) {
actual companion object {
actual val Zero = Amount("0.00")
private val handler = NSDecimalNumberHandler.decimalNumberHandlerWithRoundingMode(roundingMode = NSRoundingMode.NSRoundBankers, scale = DecimalPrecision.toShort(), false, false, false, false)
}
internal val amount = NSDecimalNumber(string = amount)
actual operator fun plus(other: Amount): Amount =
Amount(this.decimalNumberByAdding(other).stringValue)
Amount(amount.decimalNumberByAdding(other.amount).stringValue)
actual operator fun minus(other: Amount): Amount =
Amount(this.decimalNumberBySubtracting(other).stringValue)
Amount(amount.decimalNumberBySubtracting(other.amount).stringValue)
actual operator fun times(other: Amount): Amount =
Amount(this.decimalNumberByMultiplyingBy(other).stringValue)
Amount(amount.decimalNumberByMultiplyingBy(other.amount).stringValue)
actual operator fun div(other: Amount): Amount =
Amount(this.decimalNumberByDividingBy(other).stringValue)
Amount(amount.decimalNumberByDividingBy(other.amount, handler).stringValue)
actual override fun toString(): String = this.stringValue
override fun equals(other: Any?): Boolean {
return other is Amount && this.amount == other.amount
}
override fun hashCode() =
amount.hashCode()
actual override fun toString(): String = amount.stringValue
}

View File

@ -124,7 +124,8 @@ open class AccountTransaction(
*/
val isReversal: Boolean = false,
var userSetDisplayName: String? = null,
var userSetReference: String? = null,
var userSetOtherPartyName: String? = null,
var category: String? = null,
var notes: String? = null,
) {

View File

@ -12,6 +12,19 @@ fun Amount.toFloat() =
fun Amount.toDouble() =
this.toString().toDouble()
val Amount.isNegative: Boolean
get() = this.toString().startsWith("-")
fun Collection<Amount>.sum(): Amount {
var sum: Amount = Amount.Zero
for (element in this) {
sum += element
}
return sum
}
@NoArgConstructor
expect class Amount(amount: String = "0") {

View File

@ -7,16 +7,20 @@ import net.codinux.banking.client.model.tan.TanMethod
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
@NoArgConstructor
open class User(
val bankCode: String,
open class BankAccess(
/**
* The country specific bank code, like in Germany the Bankleitzahl, in USA the Routing Number, in Great Britain
* the Sort Code, in India the FSC Code, ...
*/
val domesticBankCode: String,
var loginName: String,
/**
* User may decides to not save password .
* User may decides to not save password.
*/
var password: String?,
val bankName: String,
val bic: String,
val bic: String?, // not all banks (in the world) have a BIC
val customerName: String,
/**
@ -29,7 +33,7 @@ open class User(
* So in most cases the userId is identical with the customerId = loginName in our speech, but there are rare cases
* where the userId differs from customerId.
*/
val userId: String? = null,
var userId: String? = null,
open val accounts: List<BankAccount> = emptyList(),
@ -39,8 +43,8 @@ open class User(
* As [tanMethods] also contains selected TanMethod, we didn't want to duplicate this object. Use
* [selectedTanMethod] to get selected TanMethod or iterate over [tanMethods] and filter selected one by this id.
*/
val selectedTanMethodIdentifier: String? = null,
open val tanMethods: List<TanMethod> = listOf(),
var selectedTanMethodIdentifier: String? = null,
open val tanMethods: MutableList<out TanMethod> = mutableListOf(),
/**
* Identifier of selected TanMedium.
@ -48,11 +52,15 @@ open class User(
* As [tanMedia] also contains selected TanMedium, we didn't want to duplicate this object. Use [selectedTanMedium]
* to get selected TanMedium or iterate over [tanMedia] and filter selected one by this medium name.
*/
val selectedTanMediumIdentifier: String? = null,
open val tanMedia: List<TanMedium> = listOf(),
var selectedTanMediumIdentifier: String? = null,
open val tanMedia: MutableList<out TanMedium> = mutableListOf(),
var bankingGroup: BankingGroup? = null,
open var serverAddress: String? = null
open var serverAddress: String? = null,
/**
* The ISO code of the country where the bank resides and to know the system of [domesticBankCode].
*/
val countryCode: String = "de"
) {
var userSetDisplayName: String? = null

View File

@ -20,16 +20,15 @@ open class BankAccount(
val isAccountTypeSupportedByApplication: Boolean = false,
val features: Set<BankAccountFeatures> = emptySet(),
// var balance: BigDecimal = BigDecimal.ZERO,
var balance: Amount = Amount.Zero, // TODO: add a BigDecimal library
var balance: Amount = Amount.Zero,
val serverTransactionsRetentionDays: Int? = null,
open var lastAccountUpdateTime: Instant? = null,
var retrievedTransactionsFrom: LocalDate? = null,
open val bookedTransactions: MutableList<AccountTransaction> = mutableListOf(),
open val prebookedTransactions: MutableList<PrebookedAccountTransaction> = mutableListOf(),
open val holdings: List<Holding> = emptyList(),
open val bookedTransactions: MutableList<out AccountTransaction> = mutableListOf(),
open val prebookedTransactions: MutableList<out PrebookedAccountTransaction> = mutableListOf(),
open val holdings: MutableList<out Holding> = mutableListOf(),
var userSetDisplayName: String? = null,
var displayIndex: Int = 0,
@ -41,6 +40,18 @@ open class BankAccount(
open val displayName: String
get() = userSetDisplayName ?: productName ?: identifier
open fun addTransactions(transactions: List<out AccountTransaction>) {
(this.bookedTransactions as MutableList<AccountTransaction>).addAll(transactions)
}
open fun addPrebookedTransactions(transactions: List<out PrebookedAccountTransaction>) {
(this.prebookedTransactions as MutableList<PrebookedAccountTransaction>).addAll(transactions)
}
open fun addHoldings(holdings: List<out Holding>) {
(this.holdings as MutableList<Holding>).addAll(holdings)
}
@get:JsonIgnore
open val supportsTransactionRetrieval: Boolean
get() = supportsAnyFeature(BankAccountFeatures.RetrieveBalance)

View File

@ -1,20 +1,20 @@
package net.codinux.banking.client.model.response
import net.codinux.banking.client.model.AccountTransaction
import net.codinux.banking.client.model.User
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.config.JsonIgnore
import net.codinux.banking.client.model.config.NoArgConstructor
@Suppress("RUNTIME_ANNOTATION_NOT_SUPPORTED")
@NoArgConstructor
open class GetAccountDataResponse(
val user: User
val bank: BankAccess
) {
@get:JsonIgnore
val bookedTransactions: List<AccountTransaction>
get() = user.accounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate }
get() = bank.accounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate }
override fun toString() = user.toString()
override fun toString() = bank.toString()
}

View File

@ -7,43 +7,47 @@ import net.codinux.banking.client.model.config.NoArgConstructor
@NoArgConstructor
open class Holding(
val name: String,
open var name: String,
val isin: String? = null,
val wkn: String? = null,
open var isin: String? = null,
open var wkn: String? = null,
val quantity: Int? = null,
val currency: String? = null,
open var quantity: Int? = null,
open var currency: String? = null,
/**
* Gesamter Kurswert aller Einheiten des Wertpapiers
*/
val totalBalance: Amount? = null,
open var totalBalance: Amount? = null,
/**
* Aktueller Kurswert einer einzelnen Einheit des Wertpapiers
*/
val marketValue: Amount? = null,
open var marketValue: Amount? = null,
/**
* Änderung in Prozent Aktueller Kurswert gegenüber Einstandspreis.
*/
val performancePercentage: Float? = null,
open var performancePercentage: Float? = null,
/**
* Gesamter Einstandspreis (Kaufpreis)
*/
val totalCostPrice: Amount? = null,
open var totalCostPrice: Amount? = null,
/**
* (Durchschnittlicher) Einstandspreis/-kurs einer Einheit des Wertpapiers
*/
val averageCostPrice: Amount? = null,
open var averageCostPrice: Amount? = null,
/**
* Zeitpunkt zu dem der Kurswert bestimmt wurde
*/
val pricingTime: Instant? = null,
open var pricingTime: Instant? = null,
val buyingDate: LocalDate? = null,
open var buyingDate: LocalDate? = null,
var userSetDisplayName: String? = null,
) {
open val identifier: String by lazy { "${isin}_$wkn" }
override fun toString() = "$name $totalBalance $currency"
}

View File

@ -3,7 +3,7 @@ package net.codinux.banking.client.model.tan
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import net.codinux.banking.client.model.BankAccountViewInfo
import net.codinux.banking.client.model.User
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.BankViewInfo
import net.codinux.banking.client.model.config.JsonIgnore
import net.codinux.banking.client.model.config.NoArgConstructor
@ -24,9 +24,9 @@ open class TanChallenge(
open val selectedTanMethodId: String,
/**
* When adding an account, frontend has no UserAccount object in BankingClientCallback to know which TanMethods are
* available for User.
* available for user.
* Also on other calls to bank server, bank server may returned an updated list of available TanMethods, so that
* [User] may contains an outdated list of available TanMethods.
* [BankAccess] may contains an outdated list of available TanMethods.
*
* Therefore i added list with up to date TanMethods here to ensure EnterTanDialog can display user's up to date TanMethods.
*/
@ -43,7 +43,7 @@ open class TanChallenge(
open val tanImage: TanImage? = null,
open val flickerCode: FlickerCode? = null,
open val user: BankViewInfo,
open val bank: BankViewInfo,
open val account: BankAccountViewInfo? = null,
/**
* Datum und Uhrzeit, bis zu welchem Zeitpunkt eine TAN auf Basis der gesendeten Challenge gültig ist. Nach Ablauf der Gültigkeitsdauer wird die entsprechende TAN entwertet.

View File

@ -14,7 +14,9 @@ open class TanMedium(
/**
* Only set if [type] is [TanMediumType.MobilePhone].
*/
val mobilePhone: MobilePhoneTanMedium? = null
val mobilePhone: MobilePhoneTanMedium? = null,
var userSetDisplayName: String? = null
) {
/**
@ -52,12 +54,13 @@ open class TanMedium(
}
val displayName: String by lazy {
identifier + " " + when (status) {
TanMediumStatus.Used -> "Aktiv"
TanMediumStatus.Available -> "Verfügbar"
TanMediumStatus.ActiveFollowUpCard -> " Folgekarte, aktiv bei erster Nutzung"
TanMediumStatus.AvailableFollowUpCard -> " Folgekarte, die erst aktiviert werden muss"
}
userSetDisplayName
?: (identifier + " " + when (status) {
TanMediumStatus.Used -> "Aktiv"
TanMediumStatus.Available -> "Verfügbar"
TanMediumStatus.ActiveFollowUpCard -> " Folgekarte, aktiv bei erster Nutzung"
TanMediumStatus.AvailableFollowUpCard -> " Folgekarte, die erst aktiviert werden muss"
})
}
override fun toString() = "$mediumName $status"

View File

@ -10,7 +10,8 @@ open class TanMethod(
open val type: TanMethodType,
open val identifier: String,
open val maxTanInputLength: Int? = null,
open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric
open val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric,
open var userSetDisplayName: String? = null
) {
@get:JsonIgnore

View File

@ -3,7 +3,6 @@ package net.codinux.banking.client.model
import net.codinux.banking.client.model.config.NoArgConstructor
@JsModule("big.js")
@kotlin.js.JsNonModule
open external class Big(value: String) {
fun plus(other: Big): Big
fun minus(other: Big): Big
@ -14,38 +13,41 @@ open external class Big(value: String) {
@NoArgConstructor
actual class Amount actual constructor(amount: String): Big(amount) {
actual class Amount actual constructor(amount: String) {
actual companion object {
actual val Zero = Amount("0.00")
}
internal val amount = Big(amount)
actual operator fun plus(other: Amount): Amount {
return Amount(super.plus(other).toString())
return Amount(amount.plus(other.amount).toString())
}
actual operator fun minus(other: Amount): Amount {
return Amount(super.minus(other).toString())
return Amount(amount.minus(other.amount).toString())
}
actual operator fun times(other: Amount): Amount {
return Amount(super.times(other).toString())
return Amount(amount.times(other.amount).toString())
}
actual operator fun div(other: Amount): Amount {
return Amount(super.div(other).toString())
return Amount(amount.div(other.amount).toString())
}
override fun equals(other: Any?): Boolean {
return other is Amount && this.toString() == other.toString()
return other is Amount && amount.toString() == other.amount.toString()
}
override fun hashCode(): Int {
return super.hashCode()
}
actual override fun toString(): String = super.toString()
actual override fun toString(): String = amount.toString()
}

View File

@ -4,7 +4,7 @@ import net.codinux.banking.client.BankingClient
import net.codinux.banking.client.BankingClientCallback
import net.codinux.banking.client.model.BankAccount
import net.codinux.banking.client.model.BankAccountFeatures
import net.codinux.banking.client.model.User
import net.codinux.banking.client.model.BankAccess
import net.codinux.banking.client.model.options.GetAccountDataOptions
import net.codinux.banking.client.model.request.GetAccountDataRequest
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
@ -35,14 +35,14 @@ open class FinTs4kBankingClient(
return mapper.map(response, request.bankInfo)
}
override suspend fun updateAccountTransactionsAsync(user: User, accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> {
val accountsToRequest = (accounts ?: user.accounts).filter { it.supportsAnyFeature(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.RetrieveBalance) }
override suspend fun updateAccountTransactionsAsync(bank: BankAccess, accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> {
val accountsToRequest = (accounts ?: bank.accounts).filter { it.supportsAnyFeature(BankAccountFeatures.RetrieveBalance, BankAccountFeatures.RetrieveBalance) }
if (accountsToRequest.isNotEmpty()) {
var finTsModel: BankData? = null
val responses = accountsToRequest.map { account ->
val parameter = mapper.mapToUpdateAccountTransactionsParameter(user, account, finTsModel)
val parameter = mapper.mapToUpdateAccountTransactionsParameter(bank, account, finTsModel)
val response = client.getAccountDataAsync(parameter)

View File

@ -58,19 +58,19 @@ open class FinTs4kMapper {
bank.serverAddress, bank.bic, bank.name
)
open fun mapToUpdateAccountTransactionsParameter(user: User, account: BankAccount, finTsModel: BankData?): GetAccountDataParameter {
open fun mapToUpdateAccountTransactionsParameter(bank: BankAccess, account: BankAccount, finTsModel: BankData?): GetAccountDataParameter {
val defaults = GetAccountDataOptions()
val accountIdentifier = BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
val from = account.lastAccountUpdateTime?.toLocalDateTime(TimeZone.EuropeBerlin)?.date // TODO: in case lastTransactionsUpdateTime is not set, this would retrieve all transactions (and require a TAN im most cases)
val retrieveTransactions = if (from != null) RetrieveTransactions.AccordingToRetrieveFromAndTo else RetrieveTransactions.valueOf(defaults.retrieveTransactions.name)
// val preferredTanMethods = listOf(mapTanMethodType(user.selectedTanMethod.type)) // TODO: currently we aren't saving TanMethods in database, re-enable as soon as TanMethods get saved
// val preferredTanMethods = listOf(mapTanMethodType(bank.selectedTanMethod.type)) // TODO: currently we aren't saving TanMethods in database, re-enable as soon as TanMethods get saved
val preferredTanMethods = defaults.preferredTanMethods?.map { mapTanMethodType(it) }
return GetAccountDataParameter(user.bankCode, user.loginName, user.password!!, listOf(accountIdentifier), true,
return GetAccountDataParameter(bank.domesticBankCode, bank.loginName, bank.password!!, listOf(accountIdentifier), true,
retrieveTransactions, from,
preferredTanMethods = preferredTanMethods,
preferredTanMedium = user.selectedTanMediumIdentifier,
preferredTanMedium = bank.selectedTanMediumIdentifier,
finTsModel = finTsModel
)
}
@ -91,7 +91,7 @@ open class FinTs4kMapper {
open fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse, bank: BankInfo? = null): Response<GetAccountDataResponse> =
if (response.successful && response.customerAccount != null) {
Response.success(GetAccountDataResponse(mapUser(response.customerAccount!!, bank)))
Response.success(GetAccountDataResponse(mapBank(response.customerAccount!!, bank)))
} else {
mapError(response)
}
@ -99,12 +99,12 @@ open class FinTs4kMapper {
open fun map(responses: List<Triple<BankAccount, GetAccountDataParameter, net.dankito.banking.client.model.response.GetAccountDataResponse>>): Response<List<GetTransactionsResponse>> {
val type = if (responses.all { it.third.successful }) ResponseType.Success else ResponseType.Error
// TODO: update UserAccount and BankAccount objects according to retrieved data
// TODO: update BankAccess and BankAccount objects according to retrieved data
val mappedResponses = responses.map { (account, param, getAccountDataResponse) ->
val user = getAccountDataResponse.customerAccount
val finTsBankAccount = user?.accounts?.firstOrNull { it.identifier == account.identifier && it.subAccountNumber == account.subAccountNumber }
val bank = getAccountDataResponse.customerAccount
val finTsBankAccount = bank?.accounts?.firstOrNull { it.identifier == account.identifier && it.subAccountNumber == account.subAccountNumber }
if (getAccountDataResponse.successful && user != null && finTsBankAccount != null) {
if (getAccountDataResponse.successful && bank != null && finTsBankAccount != null) {
if (finTsBankAccount.lastAccountUpdateTime != null) {
account.lastAccountUpdateTime = finTsBankAccount.lastAccountUpdateTime
}
@ -127,7 +127,7 @@ open class FinTs4kMapper {
}
open fun mapToUserAccountViewInfo(bank: BankData): BankViewInfo = BankViewInfo(
open fun mapToBankViewInfo(bank: BankData): BankViewInfo = BankViewInfo(
bank.bankCode, bank.customerId, bank.bankName, getBankingGroup(bank.bankName, bank.bic)
)
@ -138,17 +138,18 @@ open class FinTs4kMapper {
)
protected open fun mapUser(user: net.dankito.banking.client.model.CustomerAccount, bank: BankInfo? = null) = User(
user.bankCode, user.loginName, user.password,
bank?.name ?: user.bankName, user.bic, user.customerName, user.userId,
user.accounts.map { mapAccount(it) }.sortedBy { it.type }
protected open fun mapBank(bank: net.dankito.banking.client.model.CustomerAccount, info: BankInfo? = null) = BankAccess(
bank.bankCode, bank.loginName, bank.password,
info?.name ?: bank.bankName, bank.bic, bank.customerName, bank.userId,
bank.accounts.map { mapAccount(it) }.sortedBy { it.type }
.onEachIndexed { index, bankAccount -> bankAccount.displayIndex = index },
user.selectedTanMethod?.securityFunction?.code, user.tanMethods.map { mapTanMethod(it) },
user.selectedTanMedium?.mediumName, user.tanMedia.map { mapTanMedium(it) },
bank.selectedTanMethod?.securityFunction?.code, bank.tanMethods.map { mapTanMethod(it) }.toMutableList(),
bank.selectedTanMedium?.mediumName, bank.tanMedia.map { mapTanMedium(it) }.toMutableList(),
bank?.bankingGroup ?: getBankingGroup(user.bankName, user.bic),
user.finTsServerAddress
info?.bankingGroup ?: getBankingGroup(bank.bankName, bank.bic),
bank.finTsServerAddress,
"de"
)
protected open fun getBankingGroup(bankName: String, bic: String): BankingGroup? =
@ -163,7 +164,7 @@ open class FinTs4kMapper {
account.serverTransactionsRetentionDays,
account.lastAccountUpdateTime, account.retrievedTransactionsFrom,
bookedTransactions = mapBookedTransactions(account).toMutableList(),
holdings = mapHoldings(account.statementOfHoldings, account.currency, account.lastAccountUpdateTime)
holdings = mapHoldings(account.statementOfHoldings, account.currency, account.lastAccountUpdateTime).toMutableList()
)
protected open fun mapAccountType(type: net.dankito.banking.client.model.BankAccountType): BankAccountType =
@ -297,13 +298,13 @@ open class FinTs4kMapper {
// TanMedium has not natural id in FinTS model so we have to create our own one
val selectedTanMediumName = challenge.bank.selectedTanMedium?.let { selected -> tanMedia.firstOrNull { it == selected } }?.identifier
val user = mapToUserAccountViewInfo(challenge.bank)
val bank = mapToBankViewInfo(challenge.bank)
val account = challenge.account?.let { mapToBankAccountViewInfo(it) }
val tanImage = if (challenge is ImageTanChallenge) mapTanImage(challenge.image) else null
val flickerCode = if (challenge is FlickerCodeTanChallenge) mapFlickerCode(challenge.flickerCode) else null
return object : TanChallenge(type, action, challenge.messageToShowToUser, selectedTanMethodId, tanMethods, selectedTanMediumName, tanMedia, tanImage, flickerCode, user, account, challenge.tanExpirationTime, challenge.challengeCreationTimestamp) {
return object : TanChallenge(type, action, challenge.messageToShowToUser, selectedTanMethodId, tanMethods, selectedTanMediumName, tanMedia, tanImage, flickerCode, bank, account, challenge.tanExpirationTime, challenge.challengeCreationTimestamp) {
override fun addTanExpiredCallback(callback: () -> Unit) {
challenge.addTanExpiredCallback(callback)
}

View File

@ -88,12 +88,12 @@ class ShowUsage {
private fun printReceivedData(response: Response<GetAccountDataResponse>) {
response.data?.let { data ->
val user = data.user
println("Kunde: ${user.customerName} ${user.accounts.size} Konten @ ${user.bic} ${user.bankName}")
val bank = data.bank
println("Kunde: ${bank.customerName} ${bank.accounts.size} Konten @ ${bank.bic} ${bank.bankName}")
println()
println("Konten:")
user.accounts.sortedBy { it.type }.forEach { account ->
bank.accounts.sortedBy { it.type }.forEach { account ->
println("${account.identifier} ${account.productName} ${account.balance} ${account.currency}")
}

View File

@ -0,0 +1,95 @@
Für den deutschen Begriff "Bankzugang" gibt es im Englischen verschiedene mögliche Übersetzungen, abhängig vom Kontext. Hier sind einige Alternativen:
1. **Bank access** Diese Übersetzung ist direkt und wird verwendet, wenn es um den allgemeinen Zugang zu einem Bankkonto oder einer Bank geht.
2. **Online banking access** Diese Option wird oft genutzt, wenn es sich spezifisch um den Zugang zu einem Online-Banking-Konto handelt.
3. **Bank account access** Dies wird verwendet, wenn man den Zugang zu einem spezifischen Bankkonto meint.
4. **Bank login** Eine übliche Formulierung für den Zugang zu einem Online-Banking-Portal durch Einloggen mit Anmeldedaten.
5. **Bank credentials** Dies bezieht sich auf die Anmeldeinformationen (Benutzername, Passwort, etc.) für den Zugang zu einem Bankkonto, speziell im digitalen Bereich.
6. **Access to banking services** Diese Variante beschreibt eher den allgemeinen Zugang zu Bankdienstleistungen, sowohl digital als auch in Filialen.
Die beste Wahl hängt vom genauen Kontext ab, z.B. ob es sich um physischen Zugang zu einer Bank, Online-Zugang oder Zugang zu bestimmten Bankdiensten handelt.
Hier sind einige Namensvorschläge für deine Klasse mit entsprechenden Übersetzungen ins Englische:
1. **BankVerbindung**
_BankConnection_
2. **BankZugangsdaten**
_BankAccessData_
3. **OnlineBankProfil**
_OnlineBankProfile_
4. **BankKontoDetails**
_BankAccountDetails_
5. **BankNutzerProfil**
_BankUserProfile_
6. **BankNutzerDaten**
_BankUserData_
7. **BankZugangsprofil**
_BankAccessProfile_
8. **BankKontoverbindung**
_BankAccountConnection_
9. **BankServiceInfo**
_BankServiceInfo_
10. **BankNutzerVerwaltung**
_BankUserManagement_
Diese Namen umfassen sowohl den Zugang zur Bank als auch Daten über die Bank und den Nutzer.
Hier sind einige Namensvorschläge für die Klasse auf Deutsch und Englisch:
1. **BankZugangsdaten**
- **Englisch**: BankAccessData
2. **OnlineBankingZugang**
- **Englisch**: OnlineBankingAccess
3. **BankVerbindung**
- **Englisch**: BankConnection
4. **BankZugang**
- **Englisch**: BankAccess
5. **NutzerBankDetails**
- **Englisch**: UserBankDetails
6. **BankingAuthentifizierung**
- **Englisch**: BankingAuthentication
7. **BankLoginDaten**
- **Englisch**: BankLoginData
8. **BankZugangsInfo**
- **Englisch**: BankAccessInfo
9. **KontoZugangsdaten**
- **Englisch**: AccountAccessData
10. **OnlineBankingVerbindung**
- **Englisch**: OnlineBankingConnection
Diese Namen sollen die wesentlichen Informationen der Klasse widerspiegeln und die verschiedenen Attribute gut abdecken.

View File

@ -0,0 +1,18 @@
In vielen Ländern gibt es eigene Systeme zur eindeutigen Identifizierung von Banken, die ähnlich wie die **Bankleitzahl** (BLZ) in Deutschland funktionieren. Hier sind einige Beispiele:
1. **USA** **Routing Number**: In den USA wird die **Routing Transit Number (RTN)** oder **ABA-Routing-Nummer** verwendet, um Banken zu identifizieren.
2. **Großbritannien** **Sort Code**: In Großbritannien wird der **Sort Code** verwendet, um eine Bank und ihre Filiale zu identifizieren.
3. **International** **SWIFT/BIC**: Das **SWIFT/BIC**-System (Society for Worldwide Interbank Financial Telecommunication / Bank Identifier Code) wird weltweit verwendet, um Banken international eindeutig zu identifizieren.
4. **Kanada** **Institution Number**: In Kanada gibt es eine **Institution Number** (in Kombination mit einer Transitnummer).
5. **Australien** **BSB Number**: In Australien ist es die **BSB Number** (Bank-State-Branch Number).
6. **Indien** **IFSC Code**: In Indien wird der **Indian Financial System Code (IFSC)** verwendet.
7. **Schweiz** **SIC Number**: In der Schweiz gibt es das **SIC (Swiss Interbank Clearing)**-System, um Banken zu identifizieren.
### Guter englischer Name für eine landesweite eindeutige Bankidentifikation
Ein passender englischer Begriff für einen solchen eindeutigen Identifikationswert wäre:
- **National Bank Identifier (NBI)**: Dies beschreibt eine eindeutige Kennung, die eine Bank auf nationaler Ebene identifiziert.
- **Domestic Bank Code (DBC)**: Dies weist ebenfalls auf die Verwendung im nationalen Kontext hin.
- **Bank Identifier Code (BIC)**: Obwohl dieser Begriff oft international verwendet wird, kann er auch auf eine landesweite eindeutige Identifikation hinweisen, wenn er im nationalen Kontext benutzt wird.
Ein allgemeiner Begriff wie **"National Bank Code"** wäre ebenfalls leicht verständlich und eindeutig in seiner Funktion.